Initial revision
diff --git a/ospf6d/ospf6_intra.c b/ospf6d/ospf6_intra.c
new file mode 100644
index 0000000..b9c9ebd
--- /dev/null
+++ b/ospf6d/ospf6_intra.c
@@ -0,0 +1,896 @@
+/*
+ * Copyright (C) 2002 Yasuhiro Ohara
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING.  If not, write to the 
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
+ * Boston, MA 02111-1307, USA.  
+ */
+
+#include "ospf6d.h"
+
+static int intra_index;
+#define IS_OSPF6_DUMP_INTRA (ospf6_dump_is_on (intra_index))
+
+#define ADD    0
+#define REMOVE 1
+
+static void
+ospf6_intra_route_calculate (int type, struct ospf6_lsa *lsa,
+                             struct ospf6_route_req *topo_entry)
+{
+  struct ospf6_intra_area_prefix_lsa *intra_prefix;
+  char *start, *end;
+  struct ospf6_prefix *ospf6_prefix;
+  struct ospf6_route_req request;
+  struct ospf6_area *area;
+
+  if (IS_OSPF6_DUMP_INTRA)
+    {
+      char buf[64];
+      struct prefix_ls *p_ls;
+      p_ls = (struct prefix_ls *) &topo_entry->route.prefix;
+      inet_ntop (AF_INET, &p_ls->adv_router, buf, sizeof (buf));
+      zlog_info ("INTRA: Calculate [%s] %s and %s",
+                 (type == ADD ? "add" : "remove"), lsa->str, buf);
+    }
+
+  intra_prefix = OSPF6_LSA_HEADER_END (lsa->header);
+
+  area = lsa->scope;
+  assert (area);
+
+  start = (char *) (intra_prefix + 1);
+  end = (char *) lsa->header + ntohs (lsa->header->length);
+  for (ospf6_prefix = (struct ospf6_prefix *) start;
+       (char *) ospf6_prefix < end;
+       ospf6_prefix = OSPF6_NEXT_PREFIX (ospf6_prefix))
+    {
+      memset (&request, 0, sizeof (request));
+
+      request.route.type = OSPF6_DEST_TYPE_NETWORK;
+      request.route.prefix.family = AF_INET6;
+      request.route.prefix.prefixlen = ospf6_prefix->prefix_length;
+      ospf6_prefix_in6_addr (ospf6_prefix, &request.route.prefix.u.prefix6);
+
+      request.path.type = OSPF6_PATH_TYPE_INTRA;
+      request.path.area_id = area->area_id;
+      request.path.origin.type = lsa->header->type;
+      request.path.origin.id = lsa->header->id;
+      request.path.origin.adv_router = lsa->header->adv_router;
+      request.path.cost = topo_entry->path.cost +
+                          ntohs (ospf6_prefix->prefix_metric);
+      request.path.capability[0] = topo_entry->path.capability[0];
+      request.path.capability[1] = topo_entry->path.capability[1];
+      request.path.capability[2] = topo_entry->path.capability[2];
+
+      memcpy (&request.nexthop.address, &topo_entry->nexthop.address,
+              sizeof (request.nexthop.address));
+      request.nexthop.ifindex = topo_entry->nexthop.ifindex;
+
+      if (type == ADD)
+        ospf6_route_add (&request, area->route_table);
+      else if (type == REMOVE)
+        ospf6_route_remove (&request, area->route_table);
+      else
+        assert (0);
+    }
+}
+
+int
+ospf6_intra_prefix_database_hook_remove (void *data)
+{
+  struct ospf6_lsa *lsa = data;
+  struct ospf6_area *area;
+  struct ospf6_intra_area_prefix_lsa *iap;
+  struct prefix_ls prefix_ls;
+  struct ospf6_route_req topo_entry;
+
+  if (lsa->header->type != htons (OSPF6_LSA_TYPE_INTRA_PREFIX))
+    return 0;
+
+  area = (struct ospf6_area *) lsa->scope;
+  assert (area);
+
+  if (IS_OSPF6_DUMP_INTRA)
+    zlog_info ("INTRA: area %s remove: %s", area->str, lsa->str);
+
+  iap = OSPF6_LSA_HEADER_END (lsa->header);
+  memset (&prefix_ls, 0, sizeof (prefix_ls));
+  prefix_ls.prefixlen = 64;
+  prefix_ls.adv_router.s_addr = iap->refer_advrtr;
+  prefix_ls.id.s_addr = iap->refer_lsid;
+
+  if (iap->refer_lstype == htons (OSPF6_LSA_TYPE_ROUTER) &&
+      iap->refer_lsid != htonl (0))
+    {
+      zlog_warn ("SPF: Malformed ID %lu of Router reference in %s",
+                 (u_long) ntohl (iap->refer_lsid), lsa->str);
+      prefix_ls.id.s_addr = htonl (0);
+    }
+
+  ospf6_route_lookup (&topo_entry, (struct prefix *) &prefix_ls,
+                      area->table_topology);
+
+  while (iap->refer_lstype == topo_entry.path.origin.type &&
+         iap->refer_lsid == topo_entry.path.origin.id &&
+         iap->refer_advrtr == topo_entry.path.origin.adv_router)
+    {
+      ospf6_intra_route_calculate (REMOVE, lsa, &topo_entry);
+      ospf6_route_next (&topo_entry);
+    }
+  return 0;
+}
+
+int
+ospf6_intra_prefix_database_hook_add (void *data)
+{
+  struct ospf6_lsa *lsa = data;
+  struct ospf6_area *area;
+  struct ospf6_intra_area_prefix_lsa *iap;
+  struct prefix_ls prefix_ls;
+  struct ospf6_route_req topo_entry;
+
+  if (lsa->header->type != htons (OSPF6_LSA_TYPE_INTRA_PREFIX))
+    return 0;
+
+  area = (struct ospf6_area *) lsa->scope;
+  assert (area);
+
+  if (IS_LSA_MAXAGE (lsa))
+    {
+      ospf6_intra_prefix_database_hook_remove (lsa);
+      return 0;
+    }
+
+  if (IS_OSPF6_DUMP_INTRA)
+    zlog_info ("INTRA: area %s add: %s", area->str, lsa->str);
+
+  iap = OSPF6_LSA_HEADER_END (lsa->header);
+
+  memset (&prefix_ls, 0, sizeof (struct prefix_ls));
+  prefix_ls.prefixlen = 64;
+  prefix_ls.adv_router.s_addr = iap->refer_advrtr;
+  prefix_ls.id.s_addr = iap->refer_lsid;
+
+  if (iap->refer_lstype == htons (OSPF6_LSA_TYPE_ROUTER) &&
+      iap->refer_lsid != htonl (0))
+    {
+      zlog_warn ("INTRA: Malformed ID %lu of Router reference in %s",
+                 (u_long) ntohl (iap->refer_lsid), lsa->str);
+      prefix_ls.id.s_addr = htonl (0);
+    }
+
+  ospf6_route_lookup (&topo_entry, (struct prefix *) &prefix_ls,
+                      area->table_topology);
+
+  while (iap->refer_lstype == topo_entry.path.origin.type &&
+         iap->refer_lsid == topo_entry.path.origin.id &&
+         iap->refer_advrtr == topo_entry.path.origin.adv_router)
+    {
+      ospf6_intra_route_calculate (ADD, lsa, &topo_entry);
+      ospf6_route_next (&topo_entry);
+    }
+  return 0;
+}
+
+void
+ospf6_intra_topology_add (void *data)
+{
+  struct ospf6_route_req *topo_entry = data;
+  struct ospf6_area *area;
+  struct ospf6_intra_area_prefix_lsa *iap;
+  struct ospf6_lsdb_node node;
+
+  area = ospf6_area_lookup (topo_entry->path.area_id, ospf6);
+  if (! area)
+    return;
+
+  if (topo_entry->route.type == OSPF6_DEST_TYPE_ROUTER &&
+      (CHECK_FLAG (topo_entry->path.router_bits, OSPF6_ROUTER_LSA_BIT_B) ||
+       CHECK_FLAG (topo_entry->path.router_bits, OSPF6_ROUTER_LSA_BIT_E)))
+    ospf6_route_add (topo_entry, ospf6->topology_table);
+
+  for (ospf6_lsdb_type (&node, htons (OSPF6_LSA_TYPE_INTRA_PREFIX),
+                        area->lsdb);
+       ! ospf6_lsdb_is_end (&node);
+       ospf6_lsdb_next (&node))
+    {
+      if (IS_LSA_MAXAGE (node.lsa))
+        continue;
+
+      iap = OSPF6_LSA_HEADER_END (node.lsa->header);
+
+      if (iap->refer_lstype == htons (OSPF6_LSA_TYPE_ROUTER) &&
+          iap->refer_lsid != htonl (0))
+        {
+          zlog_warn ("INTRA: Malformed ID %lu of Router reference in %s",
+                     (u_long) ntohl (iap->refer_lsid), node.lsa->str);
+        }
+
+      if (iap->refer_lstype != topo_entry->path.origin.type ||
+          iap->refer_lsid != topo_entry->path.origin.id ||
+          iap->refer_advrtr != topo_entry->path.origin.adv_router)
+        continue;
+
+      ospf6_intra_route_calculate (ADD, node.lsa, topo_entry);
+    }
+}
+
+void
+ospf6_intra_topology_remove (void *data)
+{
+  struct ospf6_route_req *topo_entry = data;
+  struct ospf6_area *area;
+  struct ospf6_intra_area_prefix_lsa *iap;
+  struct ospf6_lsdb_node node;
+
+  area = ospf6_area_lookup (topo_entry->path.area_id, ospf6);
+  if (! area)
+    return;
+
+  if (topo_entry->route.type == OSPF6_DEST_TYPE_ROUTER &&
+      (CHECK_FLAG (topo_entry->path.router_bits, OSPF6_ROUTER_LSA_BIT_B) ||
+       CHECK_FLAG (topo_entry->path.router_bits, OSPF6_ROUTER_LSA_BIT_E)))
+    ospf6_route_remove (topo_entry, ospf6->topology_table);
+
+  for (ospf6_lsdb_type (&node, htons (OSPF6_LSA_TYPE_INTRA_PREFIX),
+                        area->lsdb);
+       ! ospf6_lsdb_is_end (&node);
+       ospf6_lsdb_next (&node))
+    {
+      if (IS_LSA_MAXAGE (node.lsa))
+        continue;
+
+      iap = OSPF6_LSA_HEADER_END (node.lsa->header);
+
+      if (iap->refer_lstype == htons (OSPF6_LSA_TYPE_ROUTER) &&
+          iap->refer_lsid != htonl (0))
+        zlog_warn ("SPF: Malformed ID %lu of Router reference in %s",
+                   (u_long) ntohl (iap->refer_lsid), node.lsa->str);
+
+      if (iap->refer_lstype != topo_entry->path.origin.type ||
+          iap->refer_lsid != topo_entry->path.origin.id ||
+          iap->refer_advrtr != topo_entry->path.origin.adv_router)
+        continue;
+
+      ospf6_intra_route_calculate (REMOVE, node.lsa, topo_entry);
+    }
+}
+
+
+/*****************************************/
+/* RFC2740 3.4.3.7 Intra-Area-Prefix-LSA */
+/*****************************************/
+
+#define CONTINUE_IF_ADDRESS_LINKLOCAL(addr)\
+  if (IN6_IS_ADDR_LINKLOCAL (&(addr)->u.prefix6))\
+    {\
+      char buf[64];\
+      prefix2str (addr, buf, sizeof (buf));\
+      if (IS_OSPF6_DUMP_PREFIX)\
+        zlog_info ("  Filter out Linklocal: %s", buf);\
+      continue;\
+    }
+
+#define CONTINUE_IF_ADDRESS_UNSPECIFIED(addr)\
+  if (IN6_IS_ADDR_UNSPECIFIED (&(addr)->u.prefix6))\
+    {\
+      char buf[64];\
+      prefix2str (addr, buf, sizeof (buf));\
+      if (IS_OSPF6_DUMP_PREFIX)\
+        zlog_info ("  Filter out Unspecified: %s", buf);\
+      continue;\
+    }
+
+#define CONTINUE_IF_ADDRESS_LOOPBACK(addr)\
+  if (IN6_IS_ADDR_LOOPBACK (&(addr)->u.prefix6))\
+    {\
+      char buf[64];\
+      prefix2str (addr, buf, sizeof (buf));\
+      if (IS_OSPF6_DUMP_PREFIX)\
+        zlog_info ("  Filter out Loopback: %s", buf);\
+      continue;\
+    }
+
+#define CONTINUE_IF_ADDRESS_V4COMPAT(addr)\
+  if (IN6_IS_ADDR_V4COMPAT (&(addr)->u.prefix6))\
+    {\
+      char buf[64];\
+      prefix2str (addr, buf, sizeof (buf));\
+      if (IS_OSPF6_DUMP_PREFIX)\
+        zlog_info ("  Filter out V4Compat: %s", buf);\
+      continue;\
+    }
+
+#define CONTINUE_IF_ADDRESS_V4MAPPED(addr)\
+  if (IN6_IS_ADDR_V4MAPPED (&(addr)->u.prefix6))\
+    {\
+      char buf[64];\
+      prefix2str (addr, buf, sizeof (buf));\
+      if (IS_OSPF6_DUMP_PREFIX)\
+        zlog_info ("  Filter out V4Mapped: %s", buf);\
+      continue;\
+    }
+
+
+int
+ospf6_lsa_intra_prefix_show (struct vty *vty, struct ospf6_lsa *lsa)
+{
+  struct ospf6_intra_area_prefix_lsa *iap_lsa;
+  struct ospf6_prefix *prefix;
+  unsigned short prefixnum;
+  char buf[128], type[32], id[32], adv_router[32];
+  struct in6_addr in6;
+  char *start, *end, *current;
+
+  assert (lsa->header);
+  iap_lsa = (struct ospf6_intra_area_prefix_lsa *) (lsa->header + 1);
+
+  prefixnum = ntohs (iap_lsa->prefix_number);
+  ospf6_lsa_type_string (iap_lsa->refer_lstype, type, sizeof (type));
+  inet_ntop (AF_INET, &iap_lsa->refer_lsid, id, sizeof (id));
+  inet_ntop (AF_INET, &iap_lsa->refer_advrtr, adv_router,
+             sizeof (adv_router));
+
+  vty_out (vty, "     Number of Prefix: %d%s", prefixnum, VTY_NEWLINE);
+  vty_out (vty, "     Referenced LS Type: %s%s", type, VTY_NEWLINE);
+  vty_out (vty, "     Referenced LS ID: %s%s", id, VTY_NEWLINE);
+  vty_out (vty, "     Referenced Advertising Router: %s%s", adv_router,
+           VTY_NEWLINE);
+
+  start = (char *) lsa->header + sizeof (struct ospf6_lsa_header)
+          + sizeof (struct ospf6_intra_area_prefix_lsa);
+  end = (char *) lsa->header + ntohs (lsa->header->length);
+
+  for (current = start; current < end; current += OSPF6_PREFIX_SIZE (prefix))
+    {
+      prefix = (struct ospf6_prefix *) current;
+      if (current + OSPF6_PREFIX_SIZE (prefix) > end)
+        {
+          vty_out (vty, "    Trailing %d byte garbage ... Malformed%s",
+                   end - current, VTY_NEWLINE);
+          return -1;
+        }
+
+      ospf6_prefix_options_str (prefix->prefix_options, buf, sizeof (buf));
+      vty_out (vty, "     Prefix Options: %s%s", buf, VTY_NEWLINE);
+
+      ospf6_prefix_in6_addr (prefix, &in6);
+      inet_ntop (AF_INET6, &in6, buf, sizeof (buf));
+      vty_out (vty, "     Prefix: %s/%d%s",
+               buf, prefix->prefix_length, VTY_NEWLINE);
+    }
+
+  return 0;
+}
+
+void
+ospf6_lsa_intra_prefix_update_transit (char *ifname)
+{
+  char buffer [MAXLSASIZE];
+  u_int16_t size;
+  struct ospf6_lsa *old;
+  struct interface *ifp;
+  struct ospf6_interface *o6i;
+  struct ospf6_neighbor *o6n;
+
+  struct ospf6_intra_area_prefix_lsa *iap;
+  struct ospf6_lsdb_node n;
+  listnode node;
+  char *start, *end, *current;
+  struct ospf6_prefix *prefix, *dup, *src, *dst;
+  struct ospf6_link_lsa *link;
+  char buf[128];
+  int count, prefix_num;
+
+  list adv_list;
+
+  ifp = if_lookup_by_name (ifname);
+  if (! ifp)
+    {
+      zlog_warn ("Update Intra-Prefix (Transit): No such Interface: %s",
+                  ifname);
+      return;
+    }
+
+  o6i = (struct ospf6_interface *) ifp->info;
+  if (! o6i || ! o6i->area)
+    {
+      zlog_warn ("Update Intra-Prefix (Transit): Interface not enabled: %s",
+                  ifname);
+      return;
+    }
+
+  /* find previous LSA */
+  old = ospf6_lsdb_lookup (htons (OSPF6_LSA_TYPE_INTRA_PREFIX),
+                           htonl (o6i->if_id), ospf6->router_id,
+                           o6i->area);
+
+  /* Don't originate Network-LSA if not DR */
+  if (o6i->state != IFS_DR)
+    {
+      if (old)
+        {
+          if (IS_OSPF6_DUMP_PREFIX)
+            zlog_info ("Update Intra-Prefix (Transit): %s not DR",
+                       o6i->interface->name);
+          ospf6_lsa_premature_aging (old);
+        }
+      return;
+    }
+
+  /* If none of neighbor is adjacent to us */
+  count = 0;
+  o6i->foreach_nei (o6i, &count, NBS_FULL, ospf6_count_state);
+  if (count == 0)
+    {
+      if (IS_OSPF6_DUMP_PREFIX)
+        zlog_info ("Update Intra-Prefix (Transit): %s is Stub",
+                   o6i->interface->name);
+      if (old)
+        ospf6_lsa_premature_aging (old);
+      return;
+    }
+
+  if (IS_OSPF6_DUMP_PREFIX)
+    zlog_info ("Update Intra-Prefix (Transit): Interface %s",
+               o6i->interface->name);
+
+  adv_list = list_new ();
+
+  /* foreach Link-LSA associated with this Link */
+  for (ospf6_lsdb_type (&n, htons (OSPF6_LSA_TYPE_LINK), o6i->lsdb);
+       ! ospf6_lsdb_is_end (&n); ospf6_lsdb_next (&n))
+    {
+      if (IS_LSA_MAXAGE (n.lsa))
+        continue;
+
+      if (IS_OSPF6_DUMP_PREFIX)
+        zlog_info ("Update Intra-Prefix (Transit): Checking %s",
+                    n.lsa->str);
+
+      /* Check status of the advertising router */
+      if (n.lsa->header->adv_router != o6i->area->ospf6->router_id)
+        {
+          o6n = ospf6_neighbor_lookup (n.lsa->header->adv_router, o6i);
+          if (! o6n)
+            {
+              if (IS_OSPF6_DUMP_PREFIX)
+                zlog_info ("Update Intra-Prefix (Transit): neighbor not found");
+              continue;
+            }
+
+          if (o6n->state != NBS_FULL)
+            {
+              if (IS_OSPF6_DUMP_PREFIX)
+                zlog_info ("Update Intra-Prefix (Transit): %s not FULL",
+                           o6n->str);
+              continue;
+            }
+        }
+
+      /* For each Prefix in this Link-LSA */
+      link = (struct ospf6_link_lsa *) (n.lsa->header + 1);
+      prefix_num = ntohl (link->llsa_prefix_num);
+
+      if (IS_OSPF6_DUMP_PREFIX)
+        zlog_info ("  Prefix #%d", prefix_num);
+
+      start = (char *) (link + 1);
+      end = (char *) (n.lsa->header) + ntohs (n.lsa->header->length);
+      prefix = (struct ospf6_prefix *) start;
+      for (current = start; current < end;
+           current += OSPF6_PREFIX_SIZE (prefix))
+        {
+          prefix = (struct ospf6_prefix *) current;
+          ospf6_prefix_string (prefix, buf, sizeof (buf));
+
+          /* Check duplicate prefix */
+          dup = ospf6_prefix_lookup (adv_list, prefix);
+          if (dup)
+            {
+              if (IS_OSPF6_DUMP_PREFIX)
+                zlog_info ("  Duplicate %s", buf);
+              dup->prefix_options |= prefix->prefix_options;
+              continue;
+            }
+
+          if (prefix_num <= 0)
+            {
+              zlog_warn ("  Wong prefix number ...");
+              break;
+            }
+
+          if (IS_OSPF6_DUMP_PREFIX)
+            zlog_info ("  Prefix %s", buf);
+
+          /* copy prefix to advertise list */
+          ospf6_prefix_add (adv_list, prefix);
+
+          prefix_num --;
+        }
+    }
+
+  /* if no prefix to advertise, return */
+  if (listcount (adv_list) == 0)
+    {
+      if (IS_OSPF6_DUMP_PREFIX)
+        zlog_info ("  No Prefix to advertise");
+      if (old)
+        ospf6_lsa_premature_aging (old);
+      return;
+    }
+
+  /* prepare buffer */
+  memset (buffer, 0, sizeof (buffer));
+  size = sizeof (struct ospf6_intra_area_prefix_lsa);
+  iap = (struct ospf6_intra_area_prefix_lsa *) buffer;
+
+  /* Set Referenced LSA field */
+  iap->refer_lstype = htons (OSPF6_LSA_TYPE_NETWORK);
+  iap->refer_lsid = htonl (o6i->if_id);
+  iap->refer_advrtr = o6i->area->ospf6->router_id;
+
+  dst = (struct ospf6_prefix *) (iap + 1);
+  for (node = listhead (adv_list); node; nextnode (node))
+    {
+      src = (struct ospf6_prefix *) getdata (node);
+
+      memcpy (dst, src, OSPF6_PREFIX_SIZE (src));
+
+      size += OSPF6_PREFIX_SIZE (dst);
+      dst = OSPF6_NEXT_PREFIX (dst);
+    }
+  iap->prefix_number = htons (listcount (adv_list));
+
+  while ((node = listhead (adv_list)) != NULL)
+    {
+      prefix = getdata (node);
+      ospf6_prefix_delete (prefix);
+      listnode_delete (adv_list, prefix);
+    }
+  list_delete (adv_list);
+
+  ospf6_lsa_originate (htons (OSPF6_LSA_TYPE_INTRA_PREFIX),
+                       htonl (o6i->if_id), ospf6->router_id,
+                       buffer, size, o6i->area);
+}
+
+void
+ospf6_lsa_intra_prefix_update_stub (u_int32_t area_id)
+{
+  char buffer [MAXLSASIZE];
+  u_int16_t size;
+  struct ospf6_lsa *old;
+  struct ospf6_area *o6a;
+  int count;
+
+  struct ospf6_intra_area_prefix_lsa *iap;
+  listnode i,j;
+  struct ospf6_interface *o6i = NULL;
+  struct ospf6_prefix *prefix, *dst, *src;
+  struct connected *c;
+  char buf[128];
+
+  list adv_list;
+  listnode node;
+  char prefix_buf[sizeof (struct ospf6_prefix) + sizeof (struct in6_addr)];
+
+  o6a = ospf6_area_lookup (area_id, ospf6);
+  if (! o6a)
+    {
+      char tmp[16];
+      inet_ntop (AF_INET, &area_id, tmp, sizeof (tmp));
+      zlog_warn ("Update Intra-Prefix (Stub): No such area: %s", tmp);
+      return;
+    }
+  else if (IS_OSPF6_DUMP_PREFIX)
+    {
+      zlog_info ("Update Intra-Prefix (Stub): area: %s", o6a->str);
+    }
+
+  /* find previous LSA */
+  old = ospf6_lsdb_lookup (htons (OSPF6_LSA_TYPE_INTRA_PREFIX),
+                           htonl (0), ospf6->router_id,
+                           o6a); /* xxx, ls-id */
+
+  adv_list = list_new ();
+
+  /* Examin for each interface */
+  for (i = listhead (o6a->if_list); i; nextnode (i))
+    {
+      o6i = (struct ospf6_interface *) getdata (i);
+
+      if (o6i->state == IFS_DOWN)
+        {
+          if (IS_OSPF6_DUMP_PREFIX)
+            zlog_info ("    Interface %s: down", o6i->interface->name);
+          continue;
+        }
+
+      count = 0;
+      o6i->foreach_nei (o6i, &count, NBS_FULL, ospf6_count_state);
+      if (o6i->state != IFS_LOOPBACK && o6i->state != IFS_PTOP &&
+          count != 0)
+        {
+          /* This interface's prefix will be included in DR's */
+          if (IS_OSPF6_DUMP_PREFIX)
+            zlog_info ("    Interface %s: not stub", o6i->interface->name);
+          continue;
+        }
+
+      if (IS_OSPF6_DUMP_PREFIX)
+        zlog_info ("    Interface %s:", o6i->interface->name);
+
+      /* copy foreach address prefix */
+      for (j = listhead (o6i->interface->connected); j; nextnode (j))
+        {
+          c = (struct connected *) getdata (j);
+
+          /* filter prefix not IPv6 */
+          if (c->address->family != AF_INET6)
+            continue;
+
+          /* for log */
+          prefix2str (c->address, buf, sizeof (buf));
+
+          CONTINUE_IF_ADDRESS_LINKLOCAL (c->address);
+          CONTINUE_IF_ADDRESS_UNSPECIFIED (c->address);
+          CONTINUE_IF_ADDRESS_LOOPBACK (c->address);
+          CONTINUE_IF_ADDRESS_V4COMPAT (c->address);
+          CONTINUE_IF_ADDRESS_V4MAPPED (c->address);
+
+          /* filter prefix specified by configuration */
+          if (o6i->plist_name)
+            {
+              struct prefix_list *plist;
+              enum prefix_list_type result = PREFIX_PERMIT;
+
+              plist = prefix_list_lookup (AFI_IP6, o6i->plist_name);
+              if (plist)
+                result = prefix_list_apply (plist, c->address);
+              else
+                zlog_warn ("Update Intra-Prefix (Stub): "
+                           "Prefix list \"%s\" not found",
+                           o6i->plist_name);
+
+              if (result == PREFIX_DENY)
+                {
+                  if (IS_OSPF6_DUMP_PREFIX)
+                    zlog_info ("    %s: Filtered by %s",
+                               buf, o6i->plist_name);
+                  continue;
+                }
+            }
+
+          /* initialize buffer for ospf6 prefix */
+          memset (prefix_buf, 0, sizeof (prefix_buf));
+          prefix = (struct ospf6_prefix *) prefix_buf;
+
+          /* set ospf6 prefix according to its state */
+          /* xxx, virtual links */
+          if (! CHECK_FLAG (o6i->flag, OSPF6_INTERFACE_FLAG_FORCE_PREFIX) &&
+              (o6i->state == IFS_LOOPBACK || o6i->state == IFS_PTOP
+              /* xxx, PoinToMultiPoint I/F type */ ))
+            {
+              prefix->prefix_length = 128;
+              prefix->prefix_options = OSPF6_PREFIX_OPTION_LA;
+              prefix->prefix_metric = htons (0);
+              memcpy (prefix + 1, &c->address->u.prefix6,
+                      OSPF6_PREFIX_SPACE (prefix->prefix_length));
+            }
+          else
+            {
+              struct prefix_ipv6 prefix_ipv6;
+              /* apply mask */
+              prefix_copy ((struct prefix *) &prefix_ipv6, c->address);
+              apply_mask_ipv6 (&prefix_ipv6);
+
+              prefix->prefix_length = prefix_ipv6.prefixlen;
+              prefix->prefix_options = 0;  /* xxx, no options yet */
+              prefix->prefix_metric = htons (o6i->cost);
+              memcpy (prefix + 1, &prefix_ipv6.prefix,
+                      OSPF6_PREFIX_SPACE (prefix->prefix_length));
+            }
+
+          ospf6_prefix_string (prefix, buf, sizeof (buf));
+          if (IS_OSPF6_DUMP_PREFIX)
+            zlog_info ("    Advertise %s", buf);
+
+          /* check in the prefix to advertising prefix list */
+          ospf6_prefix_add (adv_list, prefix);
+        }
+    }
+
+  /* If no prefix to advertise */
+  if (listcount (adv_list) == 0)
+    {
+      if (IS_OSPF6_DUMP_PREFIX)
+        zlog_info ("    No prefix to advertise");
+      if (old)
+        ospf6_lsa_premature_aging (old);
+      return;
+    }
+
+  /* prepare buffer */
+  memset (buffer, 0, sizeof (buffer));
+  size = sizeof (struct ospf6_intra_area_prefix_lsa);
+  iap = (struct ospf6_intra_area_prefix_lsa *) buffer;
+
+  /* Set Referenced LSA field */
+  iap->refer_lstype = htons (OSPF6_LSA_TYPE_ROUTER);
+  iap->refer_lsid = htonl (0);
+  iap->refer_advrtr = o6a->ospf6->router_id;
+
+  dst = (struct ospf6_prefix *) (iap + 1);
+  for (node = listhead (adv_list); node; nextnode (node))
+    {
+      src = (struct ospf6_prefix *) getdata (node);
+
+      memcpy (dst, src, OSPF6_PREFIX_SIZE (src));
+
+      size += OSPF6_PREFIX_SIZE (dst);
+      dst = OSPF6_NEXT_PREFIX (dst);
+    }
+  iap->prefix_number = htons (listcount (adv_list));
+
+  while ((node = listhead (adv_list)) != NULL)
+    {
+      prefix = getdata (node);
+      ospf6_prefix_delete (prefix);
+      listnode_delete (adv_list, prefix);
+    }
+  list_delete (adv_list);
+
+  ospf6_lsa_originate (htons (OSPF6_LSA_TYPE_INTRA_PREFIX),
+                       htonl (0) /* xxx */, ospf6->router_id,
+                       buffer, size, o6a);
+}
+
+int
+ospf6_lsa_intra_prefix_hook_interface (void *interface)
+{
+  struct ospf6_interface *o6i = interface;
+  if (o6i->area)
+    {
+      ospf6_lsa_intra_prefix_update_transit (o6i->interface->name);
+      ospf6_lsa_intra_prefix_update_stub (o6i->area->area_id);
+    }
+  return 0;
+}
+
+int
+ospf6_lsa_intra_prefix_hook_neighbor (void *neighbor)
+{
+  struct ospf6_neighbor *o6n = neighbor;
+  if (o6n->ospf6_interface->area)
+    {
+      ospf6_lsa_intra_prefix_update_transit (o6n->ospf6_interface->interface->name);
+      ospf6_lsa_intra_prefix_update_stub (o6n->ospf6_interface->area->area_id);
+    }
+  return 0;
+}
+
+int
+ospf6_intra_prefix_link_database_hook (void *new)
+{
+  struct ospf6_lsa *lsa = new;
+  struct ospf6_interface *o6i;
+
+  if (lsa->header->type != htons (OSPF6_LSA_TYPE_LINK))
+    return 0;
+
+  o6i = lsa->scope;
+  if (o6i->state != IFS_DR)
+    return 0;
+
+  ospf6_lsa_intra_prefix_update_transit (o6i->interface->name);
+  ospf6_lsa_intra_prefix_update_stub (o6i->area->area_id);
+  return 0;
+}
+
+int
+ospf6_lsa_intra_prefix_refresh (void *old)
+{
+  struct ospf6_lsa *lsa = old;
+  struct ospf6_interface *o6i;
+  struct ospf6_area *o6a;
+  u_int32_t id;
+
+  id = ntohl (lsa->header->id);
+  if (id)
+    {
+      o6i = ospf6_interface_lookup_by_index (id);
+      if (o6i)
+        ospf6_lsa_intra_prefix_update_transit (o6i->interface->name);
+      else
+        ospf6_lsa_premature_aging (lsa);
+    }
+  else
+    {
+      o6a = lsa->scope;
+      ospf6_lsa_intra_prefix_update_stub (o6a->area_id);
+    }
+
+  return 0;
+}
+
+void
+ospf6_intra_prefix_register ()
+{
+  struct ospf6_lsa_slot slot, *sp;
+  struct ospf6_hook hook;
+
+  memset (&slot, 0, sizeof (struct ospf6_lsa_slot));
+  slot.type              = htons (OSPF6_LSA_TYPE_INTRA_PREFIX);
+  slot.name              = "Intra-Prefix";
+  slot.func_show         = ospf6_lsa_intra_prefix_show;
+  slot.func_refresh      = ospf6_lsa_intra_prefix_refresh;
+  ospf6_lsa_slot_register (&slot);
+
+  memset (&hook, 0, sizeof (hook));
+  hook.name = "OriginateIntraPrefix";
+  hook.hook_add = ospf6_lsa_intra_prefix_hook_interface;
+  hook.hook_change = ospf6_lsa_intra_prefix_hook_interface;
+  hook.hook_remove = NULL; /* XXX */
+  ospf6_hook_register (&hook, &interface_hook);
+
+  memset (&hook, 0, sizeof (hook));
+  hook.name = "OriginateIntraPrefix";
+  hook.hook_add = ospf6_lsa_intra_prefix_hook_neighbor;
+  hook.hook_change = ospf6_lsa_intra_prefix_hook_neighbor;
+  hook.hook_remove = ospf6_lsa_intra_prefix_hook_neighbor;
+  ospf6_hook_register (&hook, &neighbor_hook);
+
+  sp = ospf6_lsa_slot_get (htons (OSPF6_LSA_TYPE_INTRA_PREFIX));
+  hook.name = "CalculateIntraPrefix";
+  hook.hook_add = ospf6_intra_prefix_database_hook_add;
+  hook.hook_change = ospf6_intra_prefix_database_hook_add;
+  hook.hook_remove = ospf6_intra_prefix_database_hook_remove;
+  ospf6_hook_register (&hook, &sp->database_hook);
+}
+
+void
+ospf6_intra_database_hook_intra_prefix (struct ospf6_lsa *old,
+                                        struct ospf6_lsa *new)
+{
+  if (old)
+    ospf6_intra_prefix_database_hook_remove (old);
+  if (new && ! IS_LSA_MAXAGE (new))
+    ospf6_intra_prefix_database_hook_add (new);
+}
+
+void
+ospf6_intra_database_hook_link (struct ospf6_lsa *old,
+                                struct ospf6_lsa *new)
+{
+  ospf6_intra_prefix_link_database_hook (new);
+  ospf6_spf_database_hook (old, new);
+}
+
+void
+ospf6_intra_init ()
+{
+  ospf6_lsdb_hook[OSPF6_LSA_TYPE_INTRA_PREFIX & OSPF6_LSTYPE_CODE_MASK].hook =
+    ospf6_intra_database_hook_intra_prefix;
+  ospf6_lsdb_hook[OSPF6_LSA_TYPE_LINK & OSPF6_LSTYPE_CODE_MASK].hook = 
+    ospf6_intra_database_hook_link;
+
+  intra_index = ospf6_dump_install ("intra-area", "Intra-area calculation\n");
+  ospf6_intra_prefix_register ();
+}
+
+