Initial revision
diff --git a/ospf6d/ospf6_route.c b/ospf6d/ospf6_route.c
new file mode 100644
index 0000000..c35efa6
--- /dev/null
+++ b/ospf6d/ospf6_route.c
@@ -0,0 +1,1130 @@
+/*
+ * Copyright (C) 1999 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"
+
+char *
+dtype_name[OSPF6_DEST_TYPE_MAX] =
+{
+  "Unknown", "Router", "Network", "Discard"
+};
+#define DTYPE_NAME(x) \
+  (0 < (x) && (x) < sizeof (dtype_name) ? \
+   dtype_name[(x)] : dtype_name[0])
+
+char *
+dtype_abname[OSPF6_DEST_TYPE_MAX] =
+{
+  "?", "R", "N", "D"
+};
+#define DTYPE_ABNAME(x) \
+  (0 < (x) && (x) < sizeof (dtype_abname) ? \
+   dtype_abname[(x)] : dtype_abname[0])
+
+char *
+ptype_name[OSPF6_PATH_TYPE_MAX] =
+{
+  "Unknown", "Intra", "Inter", "External-1", "External-2",
+  "System", "Kernel", "Connect", "Static", "RIP", "RIPng",
+  "OSPF", "OSPF6", "BGP"
+};
+#define PTYPE_NAME(x) \
+  (0 < (x) && (x) < sizeof (ptype_name) ? \
+   ptype_name[(x)] : ptype_name[0])
+
+char *
+ptype_abname[OSPF6_PATH_TYPE_MAX] =
+{
+  "??", "Ia", "Ie", "E1", "E2",
+  "-X", "-K", "-C", "-S", "-R", "-R",
+  "-O", "-O", "-B"
+};
+#define PTYPE_ABNAME(x) \
+  (0 < (x) && (x) < sizeof (ptype_abname) ? \
+   ptype_abname[(x)] : ptype_abname[0])
+
+
+
+int
+ospf6_path_cmp (void *arg1, void *arg2)
+{
+  struct ospf6_path_node *pn1 = arg1;
+  struct ospf6_path_node *pn2 = arg2;
+  struct ospf6_path *p1 = &pn1->path;
+  struct ospf6_path *p2 = &pn2->path;
+
+  if (p1->type < p2->type)
+    return -1;
+  else if (p1->type > p2->type)
+    return 1;
+
+  if (p1->type == OSPF6_PATH_TYPE_EXTERNAL2)
+    {
+      if (p1->cost_e2 < p2->cost_e2)
+        return -1;
+      else if (p1->cost_e2 > p2->cost_e2)
+        return 1;
+    }
+
+  if (p1->cost < p2->cost)
+    return -1;
+  else if (p1->cost > p2->cost)
+    return 1;
+
+  /* if from the same source, recognize as identical
+     (and treat this as update) */
+  if (! memcmp (&p1->origin, &p2->origin, sizeof (struct ls_origin)) &&
+      p1->area_id == p2->area_id)
+    return 0;
+
+  /* else, always prefer left */
+  return -1;
+}
+
+int
+ospf6_nexthop_cmp (void *arg1, void *arg2)
+{
+  int i, ret = 0;
+  struct ospf6_nexthop_node *nn1 = arg1;
+  struct ospf6_nexthop_node *nn2 = arg2;
+  struct ospf6_nexthop *n1 = &nn1->nexthop;
+  struct ospf6_nexthop *n2 = &nn2->nexthop;
+
+  if (memcmp (n1, n2, sizeof (struct ospf6_nexthop)) == 0)
+    return 0;
+
+  for (i = 0; i < sizeof (struct in6_addr); i++)
+    {
+      if (nn1->nexthop.address.s6_addr[i] != nn2->nexthop.address.s6_addr[i])
+        {
+          ret = nn1->nexthop.address.s6_addr[i] -
+                nn2->nexthop.address.s6_addr[i];
+          break;
+        }
+    }
+
+  if (ret == 0)
+    ret = -1;
+
+  return ret;
+}
+
+static void
+ospf6_route_request (struct ospf6_route_req *request,
+                     struct ospf6_route_node   *rn,
+                     struct ospf6_path_node    *pn,
+                     struct ospf6_nexthop_node *nn)
+{
+  assert (request);
+  assert (rn && pn && nn);
+
+  request->route_node = rn->route_node;
+
+  linklist_head (rn->path_list, &request->path_lnode);
+  while (request->path_lnode.data != pn)
+    {
+      //assert (! linklist_end (&request->path_lnode));
+      if (linklist_end (&request->path_lnode))
+        {
+          struct linklist_node node;
+
+          zlog_info ("rn: %p, pn: %p", rn, pn);
+          zlog_info ("origin: %hx %x %x bits: %x opt: %x%x%x popt: %x area: %x type: %d cost  %d %d %d",
+          pn->path.origin.type, pn->path.origin.id, pn->path.origin.adv_router, (int)pn->path.router_bits, (int)pn->path.capability[0],
+          (int)pn->path.capability[1], (int)pn->path.capability[2],
+          (int)pn->path.prefix_options, pn->path.area_id,
+          pn->path.type, pn->path.metric_type, pn->path.cost, pn->path.cost_e2);
+
+          for (linklist_head (rn->path_list, &node); ! linklist_end (&node);
+               linklist_next (&node))
+            {
+              struct ospf6_path_node *pn2 = node.data;
+
+              zlog_info (" %p: path data with pn(%p): %s", pn2, pn,
+                         (memcmp (&pn->path, &pn2->path,
+                                  sizeof (struct ospf6_path)) ?
+                          "different" : "same"));
+
+          zlog_info ("  origin: %hx %x %x bits: %x opt: %x%x%x popt: %x area: %x type: %d cost  %d %d %d",
+          pn2->path.origin.type, pn2->path.origin.id, pn2->path.origin.adv_router, (int)pn2->path.router_bits, (int)pn2->path.capability[0],
+          (int)pn2->path.capability[1], (int)pn2->path.capability[2],
+          (int)pn2->path.prefix_options, pn2->path.area_id,
+          pn2->path.type, pn2->path.metric_type, pn2->path.cost, pn2->path.cost_e2);
+
+              if (! memcmp (&pn->path, &pn2->path, sizeof (struct ospf6_path)))
+                {
+                  pn = pn2;
+                  request->nexthop_lnode.data = pn2;
+                }
+            }
+          break;
+        }
+      linklist_next (&request->path_lnode);
+    }
+  assert (request->path_lnode.data == pn);
+
+  linklist_head (pn->nexthop_list, &request->nexthop_lnode);
+  while (request->nexthop_lnode.data != nn)
+    {
+      assert (! linklist_end (&request->nexthop_lnode));
+      linklist_next (&request->nexthop_lnode);
+    }
+  assert (request->nexthop_lnode.data == nn);
+
+  request->table = rn->table;
+  request->count = rn->count;
+  request->route_id = rn->route_id;
+  memcpy (&request->route,   &rn->route,   sizeof (struct ospf6_route));
+  memcpy (&request->path,    &pn->path,    sizeof (struct ospf6_path));
+  memcpy (&request->nexthop, &nn->nexthop, sizeof (struct ospf6_nexthop));
+}
+
+int
+ospf6_route_count (struct ospf6_route_req *request)
+{
+  return request->count;
+}
+
+int
+ospf6_route_lookup (struct ospf6_route_req *request,
+                    struct prefix *prefix,
+                    struct ospf6_route_table *table)
+{
+  struct route_node *node;
+  struct ospf6_route_node   *rn = NULL;
+  struct ospf6_path_node    *pn = NULL;
+  struct ospf6_nexthop_node *nn = NULL;
+  struct linklist_node lnode;
+
+  if (request)
+    memset ((void *) request, 0, sizeof (struct ospf6_route_req));
+
+  node = route_node_lookup (table->table, prefix);
+  if (! node)
+    return 0;
+
+  rn = (struct ospf6_route_node *) node->info;
+  if (! rn)
+    return 0;
+
+  if (request)
+    {
+      linklist_head (rn->path_list, &lnode);
+      pn = lnode.data;
+      linklist_head (pn->nexthop_list, &lnode);
+      nn = lnode.data;
+
+      ospf6_route_request (request, rn, pn, nn);
+    }
+
+  return 1;
+}
+
+void
+ospf6_route_head (struct ospf6_route_req *request,
+                  struct ospf6_route_table *table)
+{
+  struct route_node *node;
+  struct ospf6_route_node   *rn = NULL;
+  struct ospf6_path_node    *pn = NULL;
+  struct ospf6_nexthop_node *nn = NULL;
+  struct linklist_node lnode;
+
+  if (request)
+    memset (request, 0, sizeof (struct ospf6_route_req));
+
+  node = route_top (table->table);
+  if (! node)
+    return;
+
+  while (node && node->info == NULL)
+    node = route_next (node);
+  if (! node)
+    return;
+
+  rn = (struct ospf6_route_node *) node->info;
+  linklist_head (rn->path_list, &lnode);
+  pn = lnode.data;
+  linklist_head (pn->nexthop_list, &lnode);
+  nn = lnode.data;
+
+  ospf6_route_request (request, rn, pn, nn);
+}
+
+int
+ospf6_route_end (struct ospf6_route_req *request)
+{
+  if (request->route_node == NULL &&
+      linklist_end (&request->path_lnode) &&
+      linklist_end (&request->nexthop_lnode) &&
+      request->nexthop.ifindex == 0 &&
+      IN6_IS_ADDR_UNSPECIFIED (&request->nexthop.address))
+    return 1;
+  return 0;
+}
+
+void
+ospf6_route_next (struct ospf6_route_req *request)
+{
+  struct ospf6_route_node   *route_node = NULL;
+  struct ospf6_path_node    *path_node = NULL;
+  struct ospf6_nexthop_node *nexthop_node = NULL;
+
+  linklist_next (&request->nexthop_lnode);
+  if (linklist_end (&request->nexthop_lnode))
+    {
+      linklist_next (&request->path_lnode);
+      if (linklist_end (&request->path_lnode))
+        {
+          request->route_node = route_next (request->route_node);
+          while (request->route_node && request->route_node->info == NULL)
+            request->route_node = route_next (request->route_node);
+          if (request->route_node)
+            {
+              route_node = request->route_node->info;
+              if (route_node)
+                linklist_head (route_node->path_list, &request->path_lnode);
+            }
+        }
+
+      path_node = request->path_lnode.data;
+      if (path_node)
+        linklist_head (path_node->nexthop_list, &request->nexthop_lnode);
+    }
+
+  nexthop_node = request->nexthop_lnode.data;
+
+  if (nexthop_node == NULL)
+    {
+      assert (path_node == NULL);
+      assert (route_node == NULL);
+
+      memset (&request->route,   0, sizeof (struct ospf6_route));
+      memset (&request->path,    0, sizeof (struct ospf6_path));
+      memset (&request->nexthop, 0, sizeof (struct ospf6_nexthop));
+    }
+  else
+    {
+      path_node = request->path_lnode.data;
+      route_node = request->route_node->info;
+
+      assert (path_node != NULL);
+      assert (route_node != NULL);
+
+      memcpy (&request->route,   &route_node->route,
+              sizeof (struct ospf6_route));
+      memcpy (&request->path,    &path_node->path,
+              sizeof (struct ospf6_path));
+      memcpy (&request->nexthop, &nexthop_node->nexthop,
+              sizeof (struct ospf6_nexthop));
+    }
+}
+
+#define ADD    0
+#define CHANGE 1
+#define REMOVE 2
+
+void
+ospf6_route_hook_call (int type,
+                       struct ospf6_route_req *request,
+                       struct ospf6_route_table *table)
+{
+  struct linklist_node node;
+  void (*func) (struct ospf6_route_req *);
+
+  for (linklist_head (table->hook_list[type], &node);
+       ! linklist_end (&node);
+       linklist_next (&node))
+    {
+      func = node.data;
+      (*func) (request);
+    }
+}
+
+void
+ospf6_route_hook_register (void (*add)    (struct ospf6_route_req *),
+                           void (*change) (struct ospf6_route_req *),
+                           void (*remove) (struct ospf6_route_req *),
+                           struct ospf6_route_table *table)
+{
+  linklist_add (add,    table->hook_list[ADD]);
+  linklist_add (change, table->hook_list[CHANGE]);
+  linklist_add (remove, table->hook_list[REMOVE]);
+}
+
+void
+ospf6_route_hook_unregister (void (*add)    (struct ospf6_route_req *),
+                             void (*change) (struct ospf6_route_req *),
+                             void (*remove) (struct ospf6_route_req *),
+                             struct ospf6_route_table *table)
+{
+  linklist_remove (add,    table->hook_list[ADD]);
+  linklist_remove (change, table->hook_list[CHANGE]);
+  linklist_remove (remove, table->hook_list[REMOVE]);
+}
+
+
+int
+prefix_ls2str (struct prefix *p, char *str, int size)
+{
+  char id[BUFSIZ], adv_router[BUFSIZ];
+  struct prefix_ls *pl = (struct prefix_ls *) p;
+
+  inet_ntop (AF_INET, &pl->id, id, BUFSIZ);
+  inet_ntop (AF_INET, &pl->adv_router, adv_router, BUFSIZ);
+  snprintf (str, size, "%s-%s", adv_router, id);
+  return 0;
+}
+
+void
+ospf6_route_log_request (char *what, char *where,
+                         struct ospf6_route_req *request)
+{
+  char prefix[64];
+  char area_id[16];
+  char type[16], id[16], adv[16];
+  char address[64], ifname[IFNAMSIZ];
+
+  if (request->route.prefix.family != AF_INET &&
+      request->route.prefix.family != AF_INET6)
+    prefix_ls2str (&request->route.prefix, prefix, sizeof (prefix));
+  else
+    prefix2str (&request->route.prefix, prefix, sizeof (prefix));
+
+  inet_ntop (AF_INET, &request->path.area_id, area_id, sizeof (area_id));
+
+  ospf6_lsa_type_string (request->path.origin.type, type, sizeof (type));
+  inet_ntop (AF_INET, &request->path.origin.id, id, sizeof (id));
+  inet_ntop (AF_INET, &request->path.origin.adv_router, adv, sizeof (adv));
+
+  inet_ntop (AF_INET6, &request->nexthop.address, address, sizeof (address));
+
+  zlog_info ("ROUTE: %s %s %s %s %s",
+             what, DTYPE_ABNAME (request->route.type), prefix,
+             ((strcmp ("Add", what) == 0) ? "to" : "from"), where);
+  zlog_info ("ROUTE:     Area: %s type: %s cost: %lu (E2: %lu)",
+             area_id, PTYPE_NAME (request->path.type),
+             (u_long) request->path.cost, (u_long) request->path.cost_e2);
+  zlog_info ("ROUTE:     Origin: Type: %s", type);
+  zlog_info ("ROUTE:     Origin: Id: %s Adv: %s", id, adv);
+  zlog_info ("ROUTE:     Nexthop: %s", address);
+  zlog_info ("ROUTE:     Nexthop: Ifindex: %u (%s)",
+             request->nexthop.ifindex,
+             if_indextoname (request->nexthop.ifindex, ifname));
+}
+
+struct ospf6_path_node *
+ospf6_route_find_path_node (struct ospf6_route_req *request,
+                            struct ospf6_route_node *rn)
+{
+  struct linklist_node node;
+
+  for (linklist_head (rn->path_list, &node); ! linklist_end (&node);
+       linklist_next (&node))
+    {
+      struct ospf6_path_node *path_node = node.data;
+
+      if (path_node->path.area_id == request->path.area_id &&
+          path_node->path.origin.type == request->path.origin.type &&
+          path_node->path.origin.id == request->path.origin.id &&
+          path_node->path.origin.adv_router == request->path.origin.adv_router)
+        return path_node;
+    }
+
+#if 0
+  zlog_info ("req path : area: %#x origin: type: %d, id: %d, adv_router: %#x",
+             request->path.area_id, request->path.origin.type,
+             request->path.origin.id, request->path.origin.adv_router);
+  for (linklist_head (rn->path_list, &node); ! linklist_end (&node);
+       linklist_next (&node))
+    {
+      struct ospf6_path_node *path_node = node.data;
+      zlog_info ("  path : area: %#x origin: type: %d, id: %d, adv_router: %#x",
+                 path_node->path.area_id, path_node->path.origin.type,
+                 path_node->path.origin.id, path_node->path.origin.adv_router);
+    }
+#endif
+
+  return NULL;
+}
+
+struct ospf6_nexthop_node *
+ospf6_route_find_nexthop_node (struct ospf6_route_req *request,
+                               struct ospf6_path_node *pn)
+{
+  struct linklist_node node;
+  for (linklist_head (pn->nexthop_list, &node); ! linklist_end (&node);
+       linklist_next (&node))
+    {
+      struct ospf6_nexthop_node *nexthop_node = node.data;
+
+      if (! memcmp (&nexthop_node->nexthop, &request->nexthop,
+          sizeof (struct ospf6_nexthop)))
+        return nexthop_node;
+    }
+  return NULL;
+}
+
+void
+ospf6_route_add (struct ospf6_route_req *request,
+                 struct ospf6_route_table *table)
+{
+  struct ospf6_route_node   *rn;
+  struct ospf6_path_node    *pn;
+  struct ospf6_nexthop_node *nn;
+  struct route_node *route_node;
+
+  struct ospf6_route_req route;
+
+  int route_change   = 0;
+  int path_change    = 0;
+  int nexthop_change = 0;
+
+  /* find the requested route */
+  route_node = route_node_get (table->table, &request->route.prefix);
+  rn = (struct ospf6_route_node *) route_node->info;
+
+  if (rn)
+    {
+      if (memcmp (&rn->route, &request->route, sizeof (struct ospf6_route)))
+        {
+          memcpy (&rn->route, &request->route, sizeof (struct ospf6_route));
+          route_change++;
+        }
+    }
+  else
+    {
+      rn = XCALLOC (MTYPE_OSPF6_ROUTE, sizeof (struct ospf6_route_node));
+      rn->table = table;
+      rn->route_node = route_node;
+      rn->route_id = table->route_id++;
+      rn->path_list = linklist_create ();
+      rn->path_list->cmp = ospf6_path_cmp;
+      memcpy (&rn->route, &request->route, sizeof (struct ospf6_route));
+      route_node->info = rn;
+    }
+
+  /* find the same path */
+  pn = ospf6_route_find_path_node (request, rn);
+
+  if (pn)
+    {
+      if (memcmp (&pn->path, &request->path, sizeof (struct ospf6_path)))
+        {
+          memcpy (&pn->path, &request->path, sizeof (struct ospf6_path));
+          path_change++;
+        }
+    }
+  else
+    {
+      pn = XCALLOC (MTYPE_OSPF6_ROUTE, sizeof (struct ospf6_path_node));
+      pn->route_node = rn;
+      pn->nexthop_list = linklist_create ();
+      pn->nexthop_list->cmp = ospf6_nexthop_cmp;
+      memcpy (&pn->path, &request->path, sizeof (struct ospf6_path));
+      linklist_add (pn, rn->path_list);
+    }
+
+  /* find the same nexthop */
+  nn = ospf6_route_find_nexthop_node (request, pn);
+
+  if (nn)
+    {
+      if (memcmp (&nn->nexthop, &request->nexthop,
+                  sizeof (struct ospf6_nexthop)))
+        {
+          memcpy (&nn->nexthop, &request->nexthop,
+                  sizeof (struct ospf6_nexthop));
+          nexthop_change++;
+          gettimeofday (&nn->installed, (struct timezone *) NULL);
+        }
+    }
+  else
+    {
+      nn = XCALLOC (MTYPE_OSPF6_ROUTE, sizeof (struct ospf6_nexthop_node));
+      nn->path_node = pn;
+      memcpy (&nn->nexthop, &request->nexthop, sizeof (struct ospf6_nexthop));
+      linklist_add (nn, pn->nexthop_list);
+      rn->count++;
+      gettimeofday (&nn->installed, (struct timezone *) NULL);
+    }
+
+  SET_FLAG (nn->flag, OSPF6_ROUTE_FLAG_ADD);
+  if (route_change)
+    SET_FLAG (nn->flag, OSPF6_ROUTE_FLAG_ROUTE_CHANGE);
+  if (path_change)
+    SET_FLAG (nn->flag, OSPF6_ROUTE_FLAG_PATH_CHANGE);
+  if (nexthop_change)
+    SET_FLAG (nn->flag, OSPF6_ROUTE_FLAG_CHANGE);
+
+  if (table->freeze)
+    return;
+
+  if (IS_OSPF6_DUMP_ROUTE)
+    {
+      ospf6_route_log_request ("Add", table->name, request);
+
+      if (CHECK_FLAG (nn->flag, OSPF6_ROUTE_FLAG_ROUTE_CHANGE))
+        zlog_info ("ROUTE:   route attribute change");
+      if (CHECK_FLAG (nn->flag, OSPF6_ROUTE_FLAG_PATH_CHANGE))
+        zlog_info ("ROUTE:   path attribute change");
+      if (CHECK_FLAG (nn->flag, OSPF6_ROUTE_FLAG_CHANGE))
+        zlog_info ("ROUTE:   nexthop attribute change");
+    }
+
+  if (CHECK_FLAG (nn->flag, OSPF6_ROUTE_FLAG_ROUTE_CHANGE) ||
+      CHECK_FLAG (nn->flag, OSPF6_ROUTE_FLAG_PATH_CHANGE))
+    SET_FLAG (nn->flag, OSPF6_ROUTE_FLAG_CHANGE);
+
+  /* Call hooks */
+  ospf6_route_request (&route, rn, pn, nn);
+  if (CHECK_FLAG (nn->flag, OSPF6_ROUTE_FLAG_ADD))
+    ospf6_route_hook_call (ADD, &route, table);
+  else if (CHECK_FLAG (nn->flag, OSPF6_ROUTE_FLAG_CHANGE))
+    ospf6_route_hook_call (CHANGE, &route, table);
+
+  if (table->hook_add &&
+      CHECK_FLAG (nn->flag, OSPF6_ROUTE_FLAG_ADD))
+    (*table->hook_add) (&route);
+  else if (table->hook_change &&
+           CHECK_FLAG (nn->flag, OSPF6_ROUTE_FLAG_CHANGE))
+    (*table->hook_change) (&route);
+
+  /* clear flag */
+  nn->flag = 0;
+}
+
+void
+ospf6_route_remove (struct ospf6_route_req *request,
+                    struct ospf6_route_table *table)
+{
+  struct ospf6_route_node   *rn;
+  struct ospf6_path_node    *pn;
+  struct ospf6_nexthop_node *nn;
+  struct route_node *route_node;
+  struct ospf6_route_req route;
+
+  /* find the requested route */
+  route_node = route_node_get (table->table, &request->route.prefix);
+  rn = (struct ospf6_route_node *) route_node->info;
+
+  if (! rn)
+    {
+      if (IS_OSPF6_DUMP_ROUTE)
+        {
+          ospf6_route_log_request ("Remove", table->name, request);
+          zlog_info ("ROUTE:   Can't remove: No such route");
+        }
+      return;
+    }
+
+  pn = ospf6_route_find_path_node (request, rn);
+  if (! pn)
+    {
+      if (IS_OSPF6_DUMP_ROUTE)
+        {
+          ospf6_route_log_request ("Remove", table->name, request);
+          zlog_info ("ROUTE:   Can't remove: No such path");
+        }
+      return;
+    }
+
+  if (pn->path.area_id != request->path.area_id ||
+      pn->path.origin.type != request->path.origin.type ||
+      pn->path.origin.id != request->path.origin.id ||
+      pn->path.origin.adv_router != request->path.origin.adv_router)
+    {
+      if (IS_OSPF6_DUMP_ROUTE)
+        {
+          ospf6_route_log_request ("Remove", table->name, request);
+          zlog_info ("ROUTE:   Can't remove: Path differ");
+          {
+            char *s, *e, *c;
+            char line[512], *p;
+
+            p = line;
+            s = (char *) &pn->path;
+            e = s + sizeof (struct ospf6_path);
+            for (c = s; c < e; c++)
+              {
+                if ((c - s) % 4 == 0)
+                  snprintf (p++, line + sizeof (line) - p, " ");
+                snprintf (p, line + sizeof (line) - p, "%02x", *c);
+                p += 2;
+              }
+            zlog_info ("ROUTE:     path: %s", line);
+
+            p = line;
+            s = (char *) &request->path;
+            e = s + sizeof (struct ospf6_path);
+            for (c = s; c < e; c++)
+              {
+                if ((c - s) % 4 == 0)
+                  snprintf (p++, line + sizeof (line) - p, " ");
+                snprintf (p, line + sizeof (line) - p, "%02x", *c);
+                p += 2;
+              }
+            zlog_info ("ROUTE:     req : %s", line);
+
+          }
+        }
+      return;
+    }
+
+  nn = ospf6_route_find_nexthop_node (request, pn);
+  if (! nn)
+    {
+      if (IS_OSPF6_DUMP_ROUTE)
+        {
+          ospf6_route_log_request ("Remove", table->name, request);
+          zlog_info ("ROUTE:   Can't remove: No such nexthop");
+        }
+      return;
+    }
+
+  if (memcmp (&nn->nexthop, &request->nexthop, sizeof (struct ospf6_nexthop)))
+    {
+      if (IS_OSPF6_DUMP_ROUTE)
+        {
+          ospf6_route_log_request ("Remove", table->name, request);
+          zlog_info ("ROUTE:   Can't remove: Nexthop differ");
+        }
+      return;
+    }
+
+  SET_FLAG (nn->flag, OSPF6_ROUTE_FLAG_REMOVE);
+
+  if (table->freeze)
+    return;
+
+  if (IS_OSPF6_DUMP_ROUTE)
+    ospf6_route_log_request ("Remove", table->name, request);
+
+  ospf6_route_request (&route, rn, pn, nn);
+  ospf6_route_hook_call (REMOVE, &route, table);
+  if (table->hook_remove)
+    (*table->hook_remove) (&route);
+
+  /* clear flag */
+  nn->flag = 0;
+
+  /* remove nexthop */
+  linklist_remove (nn, pn->nexthop_list);
+  rn->count--;
+  XFREE (MTYPE_OSPF6_ROUTE, nn);
+
+  /* remove path if there's no nexthop for the path */
+  if (pn->nexthop_list->count != 0)
+    return;
+  linklist_remove (pn, rn->path_list);
+  linklist_delete (pn->nexthop_list);
+  XFREE (MTYPE_OSPF6_ROUTE, pn);
+
+  /* remove route if there's no path for the route */
+  if (rn->path_list->count != 0)
+    return;
+  route_node->info = NULL;
+  linklist_delete (rn->path_list);
+  XFREE (MTYPE_OSPF6_ROUTE, rn);
+}
+
+void
+ospf6_route_remove_all (struct ospf6_route_table *table)
+{
+  struct ospf6_route_req request;
+
+  for (ospf6_route_head (&request, table); ! ospf6_route_end (&request);
+       ospf6_route_next (&request))
+    ospf6_route_remove (&request, table);
+}
+
+
+struct ospf6_route_table *
+ospf6_route_table_create (char *name)
+{
+  int i;
+  struct ospf6_route_table *new;
+
+  new = XCALLOC (MTYPE_OSPF6_ROUTE, sizeof (struct ospf6_route_table));
+  snprintf (new->name, sizeof (new->name), "%s", name);
+
+  new->table = route_table_init ();
+  for (i = 0; i < 3; i++)
+    new->hook_list[i] = linklist_create ();
+
+  return new;
+}
+
+void
+ospf6_route_table_delete (struct ospf6_route_table *table)
+{
+  int i;
+
+  ospf6_route_remove_all (table);
+  route_table_finish (table->table);
+  for (i = 0; i < 3; i++)
+    linklist_delete (table->hook_list[i]);
+  XFREE (MTYPE_OSPF6_ROUTE, table);
+}
+
+void
+ospf6_route_table_freeze (struct ospf6_route_table *route_table)
+{
+  if (IS_OSPF6_DUMP_ROUTE)
+    zlog_info ("ROUTE: Table freeze: %s", route_table->name);
+  assert (route_table->freeze == 0);
+  route_table->freeze = 1;
+}
+
+void
+ospf6_route_table_thaw (struct ospf6_route_table *route_table)
+{
+  struct route_node *node;
+  struct linklist_node pnode;
+  struct linklist_node nnode;
+
+  struct ospf6_route_node   *rn;
+  struct ospf6_path_node    *pn;
+  struct ospf6_nexthop_node *nn;
+
+  struct ospf6_route_req request;
+
+  if (IS_OSPF6_DUMP_ROUTE)
+    zlog_info ("ROUTE: Table thaw: %s", route_table->name);
+
+  assert (route_table->freeze == 1);
+  route_table->freeze = 0;
+
+  for (node = route_top (route_table->table); node;
+       node = route_next (node))
+    {
+      rn = node->info;
+      if (! rn)
+        continue;
+
+      for (linklist_head (rn->path_list, &pnode);
+           ! linklist_end (&pnode);
+           linklist_next (&pnode))
+        {
+          pn = pnode.data;
+
+          for (linklist_head (pn->nexthop_list, &nnode);
+               ! linklist_end (&nnode);
+               linklist_next (&nnode))
+            {
+              nn = nnode.data;
+
+              /* if the add and remove flag set without change flag,
+                 do nothing with this route */
+              if (! CHECK_FLAG (nn->flag, OSPF6_ROUTE_FLAG_CHANGE) &&
+                  CHECK_FLAG (nn->flag, OSPF6_ROUTE_FLAG_ADD) &&
+                  CHECK_FLAG (nn->flag, OSPF6_ROUTE_FLAG_REMOVE))
+                {
+                  nn->flag = 0;
+                  continue;
+                }
+
+              memset (&request, 0, sizeof (request));
+              memcpy (&request.route, &rn->route, sizeof (rn->route));
+              memcpy (&request.path, &pn->path, sizeof (pn->path));
+              memcpy (&request.nexthop, &nn->nexthop, sizeof (nn->nexthop));
+
+              if (CHECK_FLAG (nn->flag, OSPF6_ROUTE_FLAG_ADD) ||
+                  CHECK_FLAG (nn->flag, OSPF6_ROUTE_FLAG_CHANGE))
+                ospf6_route_add (&request, route_table);
+              else if (CHECK_FLAG (nn->flag, OSPF6_ROUTE_FLAG_REMOVE))
+                ospf6_route_remove (&request, route_table);
+            }
+        }
+    }
+}
+
+
+/* VTY commands */
+
+void
+ospf6_route_show (struct vty *vty, struct ospf6_route_node *rn)
+{
+  struct linklist_node pnode;
+  struct linklist_node nnode;
+  struct ospf6_path_node    *pn;
+  struct ospf6_nexthop_node *nn;
+
+  struct timeval now, res;
+  char duration[16];
+
+  u_int pc = 0;
+  u_int nc = 0;
+#define HEAD (pc == 0 && nc == 0)
+
+  char prefix[64], nexthop[64], ifname[IFNAMSIZ];
+
+  gettimeofday (&now, (struct timezone *) NULL);
+
+  /* destination */
+  if (rn->route.prefix.family == AF_INET ||
+      rn->route.prefix.family == AF_INET6)
+    prefix2str (&rn->route.prefix, prefix, sizeof (prefix));
+  else
+    prefix_ls2str (&rn->route.prefix, prefix, sizeof (prefix));
+
+  for (linklist_head (rn->path_list, &pnode); ! linklist_end (&pnode);
+       linklist_next (&pnode))
+    {
+      pn = pnode.data;
+
+      for (linklist_head (pn->nexthop_list, &nnode); ! linklist_end (&nnode);
+           linklist_next (&nnode))
+        {
+          nn = nnode.data;
+
+          inet_ntop (AF_INET6, &nn->nexthop.address, nexthop,
+                     sizeof (nexthop));
+          if (! if_indextoname (nn->nexthop.ifindex, ifname))
+            snprintf (ifname, sizeof (ifname), "%d", nn->nexthop.ifindex);
+
+          ospf6_timeval_sub (&now, &nn->installed, &res);
+          ospf6_timeval_string_summary (&res, duration, sizeof (duration));
+
+          vty_out (vty, "%c%1s %2s %-30s %-25s %6s %s%s",
+                   (HEAD ? '*' : ' '),
+                   DTYPE_ABNAME (rn->route.type),
+                   PTYPE_ABNAME (pn->path.type),
+                   prefix, nexthop, ifname, duration, VTY_NEWLINE);
+
+          nc++;
+        }
+      pc++;
+    }
+}
+
+void
+ospf6_route_show_detail (struct vty *vty, struct ospf6_route_node *rn)
+{
+  struct linklist_node pnode;
+  struct linklist_node nnode;
+  struct ospf6_path_node    *pn;
+  struct ospf6_nexthop_node *nn;
+
+  u_int pc = 0;
+  u_int nc = 0;
+
+  char prefix[64], nexthop[64], ifname[IFNAMSIZ];
+  char area_id[16], type[16], id[16], adv[16];
+  char capa[64];
+
+  /* destination */
+  if (rn->route.prefix.family == AF_INET ||
+      rn->route.prefix.family == AF_INET6)
+    prefix2str (&rn->route.prefix, prefix, sizeof (prefix));
+  else
+    prefix_ls2str (&rn->route.prefix, prefix, sizeof (prefix));
+
+  vty_out (vty, "%s%s%s", VTY_NEWLINE, prefix, VTY_NEWLINE);
+  vty_out (vty, "    Destination Type: %s%s",
+           DTYPE_NAME (rn->route.type), VTY_NEWLINE);
+
+  for (linklist_head (rn->path_list, &pnode); ! linklist_end (&pnode);
+       linklist_next (&pnode))
+    {
+      pn = pnode.data;
+
+      inet_ntop (AF_INET, &pn->path.area_id, area_id, sizeof (area_id));
+      ospf6_lsa_type_string (pn->path.origin.type, type, sizeof (type));
+      inet_ntop (AF_INET, &pn->path.origin.id, id, sizeof (id));
+      inet_ntop (AF_INET, &pn->path.origin.adv_router, adv, sizeof (adv));
+      ospf6_options_string (pn->path.capability, capa, sizeof (capa));
+
+      vty_out (vty, "  Path:%s", VTY_NEWLINE);
+      vty_out (vty, "    Associated Area: %s%s", area_id, VTY_NEWLINE);
+      vty_out (vty, "    LS Origin: %s ID: %s Adv: %s%s",
+               type, id, adv, VTY_NEWLINE);
+      vty_out (vty, "    Path Type: %s%s",
+               PTYPE_NAME (pn->path.type), VTY_NEWLINE);
+      vty_out (vty, "    Metric Type: %d%s",
+               pn->path.metric_type, VTY_NEWLINE);
+      vty_out (vty, "    Cost: Type-1: %lu Type-2: %lu%s",
+               (u_long) pn->path.cost, (u_long) pn->path.cost_e2,
+               VTY_NEWLINE);
+      vty_out (vty, "    Router Bits: %s|%s|%s|%s%s",
+               (CHECK_FLAG (pn->path.router_bits, OSPF6_ROUTER_LSA_BIT_W) ?
+                "W" : "-"),
+               (CHECK_FLAG (pn->path.router_bits, OSPF6_ROUTER_LSA_BIT_V) ?
+                "V" : "-"),
+               (CHECK_FLAG (pn->path.router_bits, OSPF6_ROUTER_LSA_BIT_E) ?
+                "E" : "-"),
+               (CHECK_FLAG (pn->path.router_bits, OSPF6_ROUTER_LSA_BIT_B) ?
+                "B" : "-"), VTY_NEWLINE);
+      vty_out (vty, "    Optional Capabilities: %s%s", capa, VTY_NEWLINE);
+      vty_out (vty, "    Prefix Options: %s%s", "xxx", VTY_NEWLINE);
+      vty_out (vty, "    Next Hops:%s", VTY_NEWLINE);
+
+      for (linklist_head (pn->nexthop_list, &nnode); ! linklist_end (&nnode);
+           linklist_next (&nnode))
+        {
+          nn = nnode.data;
+
+          inet_ntop (AF_INET6, &nn->nexthop.address, nexthop,
+                     sizeof (nexthop));
+          if (! if_indextoname (nn->nexthop.ifindex, ifname))
+            snprintf (ifname, sizeof (ifname), "%d", nn->nexthop.ifindex);
+
+          vty_out (vty, "       %c%s%%%s%s",
+                   (HEAD ? '*' : ' '), nexthop, ifname, VTY_NEWLINE);
+
+          nc++;
+        }
+      pc++;
+    }
+  vty_out (vty, "%s", VTY_NEWLINE);
+}
+
+int
+ospf6_route_table_show (struct vty *vty, int argc, char **argv,
+                        struct ospf6_route_table *table)
+{
+  int i, ret;
+  unsigned long ret_ul;
+  char *endptr;
+  struct prefix prefix;
+  int detail = 0;
+  int arg_ipv6  = 0;
+  int arg_ipv4  = 0;
+  int arg_digit = 0;
+  struct prefix_ipv6 *p6 = (struct prefix_ipv6 *) &prefix;
+  struct prefix_ls   *pl = (struct prefix_ls *) &prefix;
+  struct route_node *node;
+
+  u_int route_count = 0;
+  u_int path_count = 0;
+  u_int route_redundant = 0;
+
+  memset (&prefix, 0, sizeof (struct prefix));
+
+  for (i = 0; i < argc; i++)
+    {
+      if (! strcmp (argv[i], "detail"))
+        {
+          detail++;
+          break;
+        }
+
+      if (! arg_ipv6 && ! arg_ipv4 && ! arg_digit)
+        {
+
+          if ((ret = inet_pton (AF_INET6, argv[i], &p6->prefix)) == 1)
+            {
+              p6->family = AF_INET6;
+              p6->prefixlen = 128;
+              arg_ipv6++;
+              continue;
+            }
+          else if ((ret = inet_pton (AF_INET, argv[i], &pl->adv_router)) == 1)
+            {
+              pl->family = AF_UNSPEC;
+              pl->prefixlen = 64; /* xxx */
+              arg_ipv4++;
+              continue;
+            }
+          else
+            {
+              ret_ul = strtoul (argv[i], &endptr, 10);
+              if (*endptr == '\0')
+                {
+                  pl->adv_router.s_addr = htonl (ret_ul);
+                  pl->family = AF_UNSPEC;
+                  pl->prefixlen = 64; /* xxx */
+                  arg_digit++;
+                  continue;
+                }
+              else
+                {
+                  vty_out (vty, "Malformed argument: %s%s",
+                           argv[i], VTY_NEWLINE);
+                  return CMD_SUCCESS;
+                }
+            }
+        }
+
+      if (arg_ipv4 || arg_digit)
+        {
+          if ((ret = inet_pton (AF_INET, argv[i], &pl->id)) == 1)
+            {
+              arg_ipv4++;
+            }
+          else
+            {
+              ret_ul = strtoul (argv[i], &endptr, 10);
+              if (*endptr == '\0')
+                {
+                  pl->id.s_addr = htonl (ret_ul);
+                  arg_digit++;
+                }
+              else
+                {
+                  vty_out (vty, "Malformed argument: %s%s",
+                           argv[i], VTY_NEWLINE);
+                  return CMD_SUCCESS;
+                }
+            }
+        }
+    }
+
+  if (arg_ipv4 || arg_ipv6 || arg_digit)
+    {
+      node = route_node_match (table->table, &prefix);
+      if (node && node->info)
+        ospf6_route_show_detail (vty, node->info);
+      return CMD_SUCCESS;
+    }
+
+  if (! detail)
+    {
+      vty_out (vty, "%s%c%1s %2s %-30s %-25s %6s%s", VTY_NEWLINE,
+               ' ', " ", " ", "Destination", "Gateway", "I/F", VTY_NEWLINE);
+      vty_out (vty, "---------------------------%s", VTY_NEWLINE);
+    }
+
+  for (node = route_top (table->table); node; node = route_next (node))
+    {
+      struct ospf6_route_node *route = node->info;
+
+      if (! route)
+        continue;
+
+      if (detail)
+        ospf6_route_show_detail (vty, route);
+      else
+        ospf6_route_show (vty, route);
+
+      route_count++;
+      path_count += route->path_list->count;
+      if (route->path_list->count > 1)
+        route_redundant++;
+    }
+
+  vty_out (vty, "===========%s", VTY_NEWLINE);
+  vty_out (vty, "Route: %d Path: %d Redundant: %d%s",
+           route_count, path_count, route_redundant, VTY_NEWLINE);
+
+  return CMD_SUCCESS;
+}
+