Initial revision
diff --git a/ospf6d/ospf6_abr.c b/ospf6d/ospf6_abr.c
new file mode 100644
index 0000000..864e0c2
--- /dev/null
+++ b/ospf6d/ospf6_abr.c
@@ -0,0 +1,655 @@
+/*
+ * Copyright (C) 2001 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"
+
+#include "ospf6_dump.h"
+#include "ospf6_abr.h"
+
+static int abr_index;
+#define IS_OSPF6_DUMP_ABR (ospf6_dump_is_on (abr_index))
+
+#define ADD    0
+#define CHANGE 1
+#define REMOVE 2
+
+/* Inter-Area-Prefix-LSA Calculation */
+
+static struct ospf6_route_req *
+ospf6_abr_entry_lookup (struct ospf6_route_req *abr_entry,
+                        u_int32_t router_id, struct ospf6_area *area)
+{
+  struct prefix_ls abr_id;
+  char router_string[32];
+
+  inet_ntop (AF_INET, &router_id, router_string, sizeof (router_string));
+
+  //zlog_info ("ABR:   Finding router %s in area %s", router_string, area->str);
+
+  memset (&abr_id, 0, sizeof (abr_id));
+  abr_id.family = AF_UNSPEC;
+  abr_id.prefixlen = 64; /* xxx */
+  abr_id.id.s_addr = htonl (0);
+  abr_id.adv_router.s_addr = router_id;
+
+  ospf6_route_lookup (abr_entry, (struct prefix *) &abr_id,
+                      area->table_topology);
+
+  if (ospf6_route_end (abr_entry))
+    {
+      if (IS_OSPF6_DUMP_ABR)
+        zlog_info ("ABR:   Router %s not found in area %s",
+                   router_string, area->str);
+      return NULL;
+    }
+
+  if (abr_entry->path.area_id != area->area_id)
+    {
+      if (IS_OSPF6_DUMP_ABR)
+        zlog_info ("ABR: ABR area id mismatch");
+      return NULL;
+    }
+
+  if (! CHECK_FLAG (abr_entry->path.router_bits, OSPF6_ROUTER_LSA_BIT_B))
+    {
+      if (IS_OSPF6_DUMP_ABR)
+        zlog_info ("ABR: ABR entry's B bit off");
+      return NULL;
+    }
+
+  return abr_entry;
+}
+
+static int
+ospf6_abr_prefix_lsa_to_route (struct ospf6_lsa *lsa,
+                               struct ospf6_route_req *request)
+{
+  struct ospf6_inter_area_prefix_lsa *iep;
+  struct ospf6_route_req abr_entry;
+
+  if (lsa->header->type != htons (OSPF6_LSA_TYPE_INTER_PREFIX))
+    {
+      if (IS_OSPF6_DUMP_ABR)
+        zlog_info ("ABR: LSA type mismatch");
+      return -1;
+    }
+
+  if (IS_LSA_MAXAGE (lsa))
+    {
+      if (IS_OSPF6_DUMP_ABR)
+        zlog_info ("ABR: LSA MaxAge");
+      return -1;
+    }
+
+  if (! ospf6_abr_entry_lookup (&abr_entry, lsa->header->adv_router,
+                                (struct ospf6_area *) lsa->scope))
+    {
+      if (IS_OSPF6_DUMP_ABR)
+        zlog_info ("ABR: ABR check failed");
+      return -1;
+    }
+
+  iep = OSPF6_LSA_HEADER_END (lsa->header);
+
+  memset (request, 0, sizeof (struct ospf6_route_req));
+  request->route.type = OSPF6_DEST_TYPE_NETWORK;
+  request->route.prefix.family = AF_INET6;
+  request->route.prefix.prefixlen = iep->prefix.prefix_length;
+  ospf6_prefix_in6_addr (&iep->prefix, &request->route.prefix.u.prefix6);
+
+  request->path.cost = abr_entry.path.cost +
+                      (ntohl (iep->metric) & ntohl (0x000fffff));
+  request->path.type = OSPF6_PATH_TYPE_INTER;
+  request->path.origin.type = lsa->header->type;
+  request->path.origin.id = lsa->header->id;
+  request->path.origin.adv_router = lsa->header->adv_router;
+  memcpy (&request->nexthop.address, &abr_entry.nexthop.address,
+          sizeof (request->nexthop.address));
+  request->nexthop.ifindex = abr_entry.nexthop.ifindex;
+
+  return 0;
+}
+
+void
+ospf6_abr_prefix_lsa_add (struct ospf6_lsa *lsa)
+{
+  struct ospf6_route_req request;
+  int ret;
+
+  if (IS_OSPF6_DUMP_ABR)
+    zlog_info ("ABR: Calculate %s", lsa->str);
+
+  ret = ospf6_abr_prefix_lsa_to_route (lsa, &request);
+  if (ret < 0)
+    return;
+
+  if (IS_OSPF6_DUMP_ABR)
+    zlog_info ("ABR: Inter Area Route add for %s", lsa->str);
+
+  ospf6_route_add (&request, ospf6->route_table);
+}
+
+void
+ospf6_abr_prefix_lsa_remove (struct ospf6_lsa *lsa)
+{
+  struct ospf6_inter_area_prefix_lsa *iep;
+  struct prefix_ipv6 prefix6;
+  struct ospf6_route_req request;
+
+  iep = OSPF6_LSA_HEADER_END (lsa->header);
+
+  prefix6.family = AF_INET6;
+  prefix6.prefixlen = iep->prefix.prefix_length;
+  ospf6_prefix_in6_addr (&iep->prefix, &prefix6.prefix);
+
+  if (IS_OSPF6_DUMP_ABR)
+    zlog_info ("ABR: Inter Area Route remove for %s", lsa->str);
+
+  for (ospf6_route_lookup (&request, (struct prefix *) &prefix6,
+                           ospf6->route_table);
+       ! ospf6_route_end (&request);
+       ospf6_route_next (&request))
+   {
+     if (memcmp (&prefix6, &request.route.prefix, sizeof (prefix6)))
+       break;
+     if (request.path.origin.type != htons (OSPF6_LSA_TYPE_INTER_PREFIX) ||
+         request.path.origin.adv_router != lsa->header->adv_router ||
+         request.path.origin.id != lsa->header->id)
+       continue;
+
+     ospf6_route_remove (&request, ospf6->route_table);
+   }
+}
+
+static int
+ospf6_abr_router_lsa_to_route (struct ospf6_lsa *lsa,
+                               struct ospf6_route_req *request)
+{
+  struct ospf6_inter_area_router_lsa *ier;
+  struct ospf6_route_req abr_entry;
+
+  if (lsa->header->type != htons (OSPF6_LSA_TYPE_INTER_ROUTER))
+    {
+      if (IS_OSPF6_DUMP_ABR)
+        zlog_info ("ABR: LSA type mismatch");
+      return -1;
+    }
+
+  if (IS_LSA_MAXAGE (lsa))
+    {
+      if (IS_OSPF6_DUMP_ABR)
+        zlog_info ("ABR: LSA MaxAge");
+      return -1;
+    }
+
+  if (! ospf6_abr_entry_lookup (&abr_entry, lsa->header->adv_router,
+                                (struct ospf6_area *) lsa->scope))
+    {
+      if (IS_OSPF6_DUMP_ABR)
+        zlog_info ("ABR: Advertising router check failed");
+      return -1;
+    }
+
+  ier = OSPF6_LSA_HEADER_END (lsa->header);
+
+  memset (request, 0, sizeof (struct ospf6_route_req));
+  request->route.type = OSPF6_DEST_TYPE_ROUTER;
+  request->route.prefix.family = AF_UNSPEC;
+  request->route.prefix.prefixlen = 64; /* XXX */
+  ((struct prefix_ls *) &request->route.prefix)->adv_router.s_addr
+    = ier->router_id;
+
+  request->path.cost = abr_entry.path.cost +
+                      (ntohl (ier->metric & htonl (0x000fffff)));
+  request->path.type = OSPF6_PATH_TYPE_INTER;
+  request->path.origin.type = lsa->header->type;
+  request->path.origin.id = lsa->header->id;
+  request->path.origin.adv_router = lsa->header->adv_router;
+  SET_FLAG (request->path.router_bits, OSPF6_ROUTER_LSA_BIT_E);
+  request->path.capability[0] = ier->options[0];
+  request->path.capability[1] = ier->options[1];
+  request->path.capability[2] = ier->options[2];
+
+  memcpy (&request->nexthop.address, &abr_entry.nexthop.address,
+          sizeof (request->nexthop.address));
+  request->nexthop.ifindex = abr_entry.nexthop.ifindex;
+
+  return 0;
+}
+
+void
+ospf6_abr_router_lsa_add (struct ospf6_lsa *lsa)
+{
+  struct ospf6_route_req request;
+  int ret;
+
+  if (IS_OSPF6_DUMP_ABR)
+    zlog_info ("ABR: Calculate %s", lsa->str);
+
+  ret = ospf6_abr_router_lsa_to_route (lsa, &request);
+  if (ret < 0)
+    return;
+
+  if (IS_OSPF6_DUMP_ABR)
+    zlog_info ("ABR: Inter Area Router add for %s", lsa->str);
+
+  ospf6_route_add (&request, ospf6->topology_table);
+}
+
+void
+ospf6_abr_router_lsa_remove (struct ospf6_lsa *lsa)
+{
+  struct ospf6_inter_area_router_lsa *ier;
+  struct prefix_ls prefix_ls;
+  struct ospf6_route_req request;
+
+  ier = OSPF6_LSA_HEADER_END (lsa->header);
+
+  memset (&prefix_ls, 0, sizeof (prefix_ls));
+  prefix_ls.family = AF_INET6;
+  prefix_ls.prefixlen = 64; /* XXX */
+  prefix_ls.adv_router.s_addr = ier->router_id;
+
+  if (IS_OSPF6_DUMP_ABR)
+    zlog_info ("ABR: Inter Area Route remove for %s", lsa->str);
+
+  for (ospf6_route_lookup (&request, (struct prefix *) &prefix_ls,
+                           ospf6->route_table);
+       ! ospf6_route_end (&request);
+       ospf6_route_next (&request))
+   {
+     if (memcmp (&prefix_ls, &request.route.prefix, sizeof (prefix_ls)))
+       break;
+     if (request.path.origin.type != htons (OSPF6_LSA_TYPE_INTER_ROUTER) ||
+         request.path.origin.adv_router != lsa->header->adv_router ||
+         request.path.origin.id != lsa->header->id)
+       continue;
+
+     ospf6_route_remove (&request, ospf6->route_table);
+   }
+}
+
+
+void
+ospf6_abr_abr_entry_add (struct ospf6_route_req *abr_entry)
+{
+  struct ospf6_lsdb_node node;
+  struct prefix_ls *abr_id;
+  struct ospf6_route_req request;
+  struct ospf6_area *area;
+
+  if (IS_OSPF6_DUMP_ABR)
+    zlog_info ("ABR: New Area Border Router found");
+
+  area = ospf6_area_lookup (abr_entry->path.area_id, ospf6);
+  if (! area)
+    {
+      if (IS_OSPF6_DUMP_ABR)
+        zlog_info ("ABR: Can't find associated area");
+      return;
+    }
+
+  abr_id = (struct prefix_ls *) &abr_entry->route.prefix;
+  if (! ospf6_abr_entry_lookup (&request, abr_id->adv_router.s_addr, area))
+    {
+      if (IS_OSPF6_DUMP_ABR)
+        zlog_info ("ABR: back check failed");
+      return;
+    }
+
+  /* for each inter-prefix LSA this ABR originated */
+  for (ospf6_lsdb_type_router (&node, htons (OSPF6_LSA_TYPE_INTER_PREFIX),
+                               abr_id->adv_router.s_addr, area->lsdb);
+       ! ospf6_lsdb_is_end (&node);
+       ospf6_lsdb_next (&node))
+    ospf6_abr_prefix_lsa_add (node.lsa);
+
+  /* for each inter-router LSA this ABR originated */
+  for (ospf6_lsdb_type_router (&node, htons (OSPF6_LSA_TYPE_INTER_ROUTER),
+                               abr_id->adv_router.s_addr, area->lsdb);
+       ! ospf6_lsdb_is_end (&node);
+       ospf6_lsdb_next (&node))
+    ospf6_abr_router_lsa_add (node.lsa);
+}
+
+void
+ospf6_abr_abr_entry_remove (struct ospf6_route_req *abr_entry)
+{
+  struct ospf6_lsdb_node node;
+  struct prefix_ls *abr_id;
+  struct ospf6_area *area;
+
+  if (IS_OSPF6_DUMP_ABR)
+    zlog_info ("ABR: Area Border Router removed");
+
+  abr_id = (struct prefix_ls *) &abr_entry->route.prefix;
+
+  area = ospf6_area_lookup (abr_entry->path.area_id, ospf6);
+  if (! area)
+    {
+      if (IS_OSPF6_DUMP_ABR)
+        zlog_info ("ABR: Can't find associated area");
+      return;
+    }
+
+  /* for each inter-prefix LSA this ABR originated */
+  for (ospf6_lsdb_type_router (&node, htons (OSPF6_LSA_TYPE_INTER_PREFIX),
+                               abr_id->adv_router.s_addr, area->lsdb);
+       ! ospf6_lsdb_is_end (&node);
+       ospf6_lsdb_next (&node))
+    ospf6_abr_prefix_lsa_remove (node.lsa);
+
+  /* for each inter-router LSA this ABR originated */
+  for (ospf6_lsdb_type_router (&node, htons (OSPF6_LSA_TYPE_INTER_ROUTER),
+                               abr_id->adv_router.s_addr, area->lsdb);
+       ! ospf6_lsdb_is_end (&node);
+       ospf6_lsdb_next (&node))
+    ospf6_abr_router_lsa_remove (node.lsa);
+}
+
+/* Inter-Area-Prefix-LSA Origination */
+
+static void
+ospf6_abr_prefix_lsa_update_add (struct ospf6_route_req *request,
+                                 struct ospf6_area *area)
+{
+  char buffer [MAXLSASIZE];
+  u_int16_t size;
+  struct ospf6_inter_area_prefix_lsa *iep;
+  char *p;
+
+  if (IS_OSPF6_DUMP_ABR)
+    zlog_info ("Update Inter-Prefix for %s: ID: %lu",
+               area->str, (u_long) ntohl (request->route_id));
+
+  /* prepare buffer */
+  memset (buffer, 0, sizeof (buffer));
+  size = sizeof (struct ospf6_inter_area_prefix_lsa);
+  iep = (struct ospf6_inter_area_prefix_lsa *) buffer;
+  p = (char *) (iep + 1);
+
+  /* prefixlen */
+  iep->prefix.prefix_length = request->route.prefix.prefixlen;
+
+  /* PrefixOptions */
+  iep->prefix.prefix_options = request->path.prefix_options;
+
+  /* set Prefix */
+  memcpy (p, &request->route.prefix.u.prefix6,
+          OSPF6_PREFIX_SPACE (request->route.prefix.prefixlen));
+  ospf6_prefix_apply_mask (&iep->prefix);
+  size += OSPF6_PREFIX_SPACE (request->route.prefix.prefixlen);
+
+  ospf6_lsa_originate (htons (OSPF6_LSA_TYPE_INTER_PREFIX),
+                       htonl (request->route_id), ospf6->router_id,
+                       (char *) iep, size, area);
+}
+
+static void
+ospf6_abr_prefix_lsa_update_remove (struct ospf6_route_req *request,
+                                    struct ospf6_area *area)
+{
+  struct ospf6_lsa *lsa;
+  lsa = ospf6_lsdb_lookup_lsdb (htons (OSPF6_LSA_TYPE_INTER_PREFIX),
+                                htonl (request->route_id),
+                                ospf6->router_id, area->lsdb);
+  if (lsa)
+    ospf6_lsa_premature_aging (lsa);
+}
+
+static void
+ospf6_abr_prefix_lsa_update (int type, struct ospf6_route_req *request)
+{
+  struct ospf6_route_req route, target;
+  listnode node;
+  struct ospf6_area *area;
+  struct ospf6_interface *o6i;
+
+  if (request->route.type != OSPF6_DEST_TYPE_NETWORK)
+    return;
+
+  /* assert this is best path; if not, return */
+  ospf6_route_lookup (&route, &request->route.prefix, request->table);
+  if (memcmp (&route.path, &request->path, sizeof (route.path)))
+    return;
+
+  if (target.path.cost >= LS_INFINITY ||
+      target.path.cost_e2 >= LS_INFINITY)
+    {
+      if (IS_OSPF6_DUMP_ABR)
+        zlog_info ("ABR: Exceeds LS Infinity, ignore");
+      return;
+    }
+
+  ospf6_route_lookup (&target, &request->route.prefix, request->table);
+  if (type == REMOVE)
+    {
+      ospf6_route_next (&route);
+      if (! memcmp (&route.route, &request->route, sizeof (route.route)))
+        {
+          type = ADD;
+          ospf6_route_next (&target);
+        }
+    }
+
+  for (node = listhead (ospf6->area_list); node; nextnode (node))
+    {
+      area = getdata (node);
+
+      if (target.path.area_id == area->area_id)
+        continue;
+
+      o6i = ospf6_interface_lookup_by_index (target.nexthop.ifindex);
+      if (o6i && o6i->area && o6i->area->area_id == area->area_id)
+        {
+          zlog_info ("ABR: Logical equivalent of split horizon, skip for %s",
+                     area->str);
+          continue;
+        }
+
+      if (area->area_id == ntohs (0) && /* Backbone */
+          target.path.type != OSPF6_PATH_TYPE_INTRA)
+        continue;
+
+      /* XXX, stub area check */
+
+      /* XXX, aggregate */
+        /* if either the area of the route or the area trying to
+           advertise is backbone, do not aggregate */
+
+      if (type == ADD)
+        ospf6_abr_prefix_lsa_update_add (&target, area);
+      else
+        ospf6_abr_prefix_lsa_update_remove (&target, area);
+    }
+}
+
+void
+ospf6_abr_route_add (struct ospf6_route_req *request)
+{
+  ospf6_abr_prefix_lsa_update (ADD, request);
+}
+
+void
+ospf6_abr_route_remove (struct ospf6_route_req *request)
+{
+  ospf6_abr_prefix_lsa_update (REMOVE, request);
+}
+
+int
+ospf6_abr_prefix_lsa_refresh (void *data)
+{
+  struct ospf6_lsa *lsa = data;
+  struct ospf6_inter_area_prefix_lsa *ier;
+  struct prefix_ipv6 prefix6;
+  struct ospf6_route_req route;
+
+  ier = OSPF6_LSA_HEADER_END (lsa->header);
+  memset (&prefix6, 0, sizeof (prefix6));
+  prefix6.family = AF_INET6;
+  prefix6.prefixlen = ier->prefix.prefix_length;
+  ospf6_prefix_in6_addr (&ier->prefix, &prefix6.prefix);
+
+  ospf6_route_lookup (&route, (struct prefix *) &prefix6,
+                      ospf6->route_table);
+  assert (! ospf6_route_end (&route));
+
+  ospf6_abr_prefix_lsa_update (ADD, &route);
+  return 0;
+}
+
+int
+ospf6_abr_prefix_lsa_show (struct vty *vty, struct ospf6_lsa *lsa)
+{
+  struct ospf6_inter_area_prefix_lsa *ier;
+  char prefix[128];
+
+  assert (lsa->header);
+  ier = OSPF6_LSA_HEADER_END (lsa->header);
+
+  ospf6_prefix_string (&ier->prefix, prefix, sizeof (prefix));
+
+  vty_out (vty, "     Metric: %d%s",
+           ntohl (ier->metric & htonl (0x000fffff)), VTY_NEWLINE);
+  vty_out (vty, "     Prefix: %s%s", prefix, VTY_NEWLINE);
+
+  return 0;
+}
+
+int
+ospf6_abr_prefix_lsa_hook_add (void *data)
+{
+  struct ospf6_lsa *lsa = data;
+  ospf6_abr_prefix_lsa_add (lsa);
+  return 0;
+}
+
+int
+ospf6_abr_prefix_lsa_hook_remove (void *data)
+{
+  struct ospf6_lsa *lsa = data;
+  ospf6_abr_prefix_lsa_remove (lsa);
+  return 0;
+}
+
+void
+ospf6_abr_database_hook_inter_prefix (struct ospf6_lsa *old,
+                                      struct ospf6_lsa *new)
+{
+  if (old)
+    ospf6_abr_prefix_lsa_hook_remove (old);
+  if (new && ! IS_LSA_MAXAGE (new))
+    ospf6_abr_prefix_lsa_hook_add (new);
+}
+
+void
+ospf6_abr_register_inter_prefix ()
+{
+  struct ospf6_lsa_slot slot;
+
+  memset (&slot, 0, sizeof (slot));
+  slot.type         = htons (OSPF6_LSA_TYPE_INTER_PREFIX);
+  slot.name         = "Inter-Prefix";
+  slot.func_show    = ospf6_abr_prefix_lsa_show;
+  slot.func_refresh = ospf6_abr_prefix_lsa_refresh;
+  ospf6_lsa_slot_register (&slot);
+
+  ospf6_lsdb_hook[OSPF6_LSA_TYPE_INTER_PREFIX & OSPF6_LSTYPE_CODE_MASK].hook = 
+    ospf6_abr_database_hook_inter_prefix;
+}
+
+int
+ospf6_abr_router_lsa_hook_add (void *data)
+{
+  struct ospf6_lsa *lsa = data;
+  ospf6_abr_router_lsa_add (lsa);
+  return 0;
+}
+
+int
+ospf6_abr_router_lsa_hook_remove (void *data)
+{
+  struct ospf6_lsa *lsa = data;
+  ospf6_abr_router_lsa_remove (lsa);
+  return 0;
+}
+
+int
+ospf6_abr_router_lsa_show (struct vty *vty, struct ospf6_lsa *lsa)
+{
+  return 0;
+}
+
+int
+ospf6_abr_router_lsa_refresh (void *data)
+{
+  return 0;
+}
+
+void
+ospf6_abr_database_hook_inter_router (struct ospf6_lsa *old,
+                                      struct ospf6_lsa *new)
+{
+  if (old)
+    ospf6_abr_router_lsa_hook_remove (old);
+  if (new && ! IS_LSA_MAXAGE (new))
+    ospf6_abr_router_lsa_hook_add (new);
+}
+
+void
+ospf6_abr_register_inter_router ()
+{
+  struct ospf6_lsa_slot slot;
+
+  memset (&slot, 0, sizeof (slot));
+  slot.type         = htons (OSPF6_LSA_TYPE_INTER_ROUTER);
+  slot.name         = "Inter-Router";
+  slot.func_show    = ospf6_abr_router_lsa_show;
+  slot.func_refresh = ospf6_abr_router_lsa_refresh;
+  ospf6_lsa_slot_register (&slot);
+
+  ospf6_lsdb_hook[OSPF6_LSA_TYPE_INTER_ROUTER & OSPF6_LSTYPE_CODE_MASK].hook = 
+    ospf6_abr_database_hook_inter_router;
+}
+
+void
+ospf6_abr_inter_route_calculation (struct ospf6_area *area)
+{
+  struct ospf6_lsdb_node node;
+
+  /* for each inter-prefix LSA */
+  for (ospf6_lsdb_type (&node, htons (OSPF6_LSA_TYPE_INTER_PREFIX),
+                        area->lsdb);
+       ! ospf6_lsdb_is_end (&node);
+       ospf6_lsdb_next (&node))
+    ospf6_abr_prefix_lsa_add (node.lsa);
+}
+
+void
+ospf6_abr_init ()
+{
+  abr_index = ospf6_dump_install ("abr", "Area Border Router Function\n");
+
+  ospf6_abr_register_inter_prefix ();
+  ospf6_abr_register_inter_router ();
+}
+
+