Initial revision
diff --git a/ospf6d/ospf6_lsa.c b/ospf6d/ospf6_lsa.c
new file mode 100644
index 0000000..b14979f
--- /dev/null
+++ b/ospf6d/ospf6_lsa.c
@@ -0,0 +1,1926 @@
+/*
+ * LSA function
+ * 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 <zebra.h>
+
+/* Include other stuffs */
+#include "version.h"
+#include "log.h"
+#include "getopt.h"
+#include "linklist.h"
+#include "thread.h"
+#include "command.h"
+#include "memory.h"
+#include "sockunion.h"
+#include "if.h"
+#include "prefix.h"
+#include "stream.h"
+#include "thread.h"
+#include "filter.h"
+#include "zclient.h"
+#include "table.h"
+#include "plist.h"
+
+#include "ospf6_proto.h"
+#include "ospf6_prefix.h"
+#include "ospf6_lsa.h"
+#include "ospf6_lsdb.h"
+#include "ospf6_message.h"
+#include "ospf6_dump.h"
+
+#include "ospf6_top.h"
+#include "ospf6_area.h"
+#include "ospf6_interface.h"
+#include "ospf6_neighbor.h"
+#include "ospf6_ism.h"
+#include "ospf6_nsm.h"
+#include "ospf6_dbex.h"
+
+#define HEADER_DEPENDENCY
+#include "ospf6d.h"
+#undef HEADER_DEPENDENCY
+
+/* test LSAs identity */
+static int
+ospf6_lsa_issame (struct ospf6_lsa_header__ *lsh1,
+                  struct ospf6_lsa_header__ *lsh2)
+{
+  assert (lsh1 && lsh2);
+
+  if (lsh1->adv_router != lsh2->adv_router)
+    return 0;
+
+  if (lsh1->id != lsh2->id)
+    return 0;
+
+  if (lsh1->type != lsh2->type)
+    return 0;
+
+  return 1;
+}
+
+/* RFC2328: Section 13.2 */
+int
+ospf6_lsa_differ (struct ospf6_lsa *lsa1,
+                  struct ospf6_lsa *lsa2)
+{
+  int diff, cmplen;
+
+  if (! ospf6_lsa_issame (lsa1->header, lsa2->header))
+    return 1;
+
+  /* check Options field */
+  /* xxx */
+
+  ospf6_lsa_age_current (lsa1);
+  ospf6_lsa_age_current (lsa2);
+  if (ntohs (lsa1->header->age) == MAXAGE &&
+      ntohs (lsa2->header->age) != MAXAGE)
+    return 1;
+  if (ntohs (lsa1->header->age) != MAXAGE &&
+      ntohs (lsa2->header->age) == MAXAGE)
+    return 1;
+
+  /* compare body */
+  if (ntohs (lsa1->header->length) != ntohs (lsa2->header->length))
+    return 1;
+
+  cmplen = ntohs (lsa1->header->length) - sizeof (struct ospf6_lsa_header);
+  diff = memcmp (lsa1->header + 1, lsa2->header + 1, cmplen);
+
+  return diff;
+}
+
+int
+ospf6_lsa_match (u_int16_t type, u_int32_t id, u_int32_t adv_router,
+                 struct ospf6_lsa_header *lsh)
+{
+  if (lsh->advrtr != adv_router)
+    return 0;
+
+  if (lsh->ls_id != id)
+    return 0;
+
+  if (lsh->type != type)
+    return 0;
+
+  return 1;
+}
+
+/* ospf6 age functions */
+/* calculate birth and set expire timer */
+static void
+ospf6_lsa_age_set (struct ospf6_lsa *lsa)
+{
+  struct timeval now;
+
+  assert (lsa && lsa->header);
+
+  if (gettimeofday (&now, (struct timezone *)NULL) < 0)
+    zlog_warn ("LSA: gettimeofday failed, may fail LSA AGEs: %s",
+               strerror (errno));
+
+  lsa->birth.tv_sec = now.tv_sec - ntohs (lsa->header->age);
+  lsa->birth.tv_usec = now.tv_usec;
+  if (ntohs (lsa->header->age) != MAXAGE)
+    lsa->expire = thread_add_timer (master, ospf6_lsa_expire, lsa,
+                                    lsa->birth.tv_sec + MAXAGE - now.tv_sec);
+  else
+    lsa->expire = NULL;
+  return;
+}
+
+/* this function calculates current age from its birth,
+   then update age field of LSA header. return value is current age */
+u_int16_t
+ospf6_lsa_age_current (struct ospf6_lsa *lsa)
+{
+  struct timeval now;
+  u_int32_t ulage;
+  u_int16_t age;
+
+  assert (lsa);
+  assert (lsa->header);
+
+  /* current time */
+  if (gettimeofday (&now, (struct timezone *)NULL) < 0)
+    zlog_warn ("LSA: gettimeofday failed, may fail ages: %s",
+               strerror (errno));
+
+  /* calculate age */
+  ulage = now.tv_sec - lsa->birth.tv_sec;
+
+  /* if over MAXAGE, set to it */
+  if (ulage > MAXAGE)
+    age = MAXAGE;
+  else
+    age = ulage;
+
+  lsa->header->age = htons (age);
+  return age;
+}
+
+/* update age field of LSA header with adding InfTransDelay */
+void
+ospf6_lsa_age_update_to_send (struct ospf6_lsa *lsa, u_int32_t transdelay)
+{
+  unsigned short age;
+
+  age = ospf6_lsa_age_current (lsa) + transdelay;
+  if (age > MAXAGE)
+    age = MAXAGE;
+  lsa->header->age = htons (age);
+  return;
+}
+
+void
+ospf6_lsa_premature_aging (struct ospf6_lsa *lsa)
+{
+  /* log */
+  if (IS_OSPF6_DUMP_LSA)
+    zlog_info ("LSA: Premature aging: %s", lsa->str);
+
+  if (lsa->expire)
+    thread_cancel (lsa->expire);
+  lsa->expire = (struct thread *) NULL;
+  if (lsa->refresh)
+    thread_cancel (lsa->refresh);
+  lsa->refresh = (struct thread *) NULL;
+
+  memset (&lsa->birth, 0, sizeof (struct timeval));
+  thread_execute (master, ospf6_lsa_expire, lsa, 0);
+}
+
+/* check which is more recent. if a is more recent, return -1;
+   if the same, return 0; otherwise(b is more recent), return 1 */
+int
+ospf6_lsa_check_recent (struct ospf6_lsa *a, struct ospf6_lsa *b)
+{
+  signed long seqnuma, seqnumb;
+  u_int16_t cksuma, cksumb;
+  u_int16_t agea, ageb;
+
+  assert (a && a->header);
+  assert (b && b->header);
+  assert (ospf6_lsa_issame (a->header, b->header));
+
+  seqnuma = ((signed long) ntohl (a->header->seqnum))
+             - (signed long) INITIAL_SEQUENCE_NUMBER;
+  seqnumb = ((signed long) ntohl (b->header->seqnum))
+             - (signed long) INITIAL_SEQUENCE_NUMBER;
+
+  /* compare by sequence number */
+    /* xxx, care about LS sequence number wrapping */
+  recent_reason = "seqnum";
+  if (seqnuma > seqnumb)
+    return -1;
+  else if (seqnuma < seqnumb)
+    return 1;
+
+  /* Checksum */
+  cksuma = ntohs (a->header->checksum);
+  cksumb = ntohs (b->header->checksum);
+  if (cksuma > cksumb)
+    return -1;
+  if (cksuma < cksumb)
+    return 0;
+
+  /* Age check */
+  agea = ospf6_lsa_age_current (a);
+  ageb = ospf6_lsa_age_current (b);
+
+    /* MaxAge check */
+  recent_reason = "max age";
+  if (agea == OSPF6_LSA_MAXAGE && ageb != OSPF6_LSA_MAXAGE)
+    return -1;
+  else if (agea != OSPF6_LSA_MAXAGE && ageb == OSPF6_LSA_MAXAGE)
+    return 1;
+
+  recent_reason = "age differ";
+  if (agea > ageb && agea - ageb >= OSPF6_LSA_MAXAGEDIFF)
+    return 1;
+  else if (agea < ageb && ageb - agea >= OSPF6_LSA_MAXAGEDIFF)
+    return -1;
+
+  /* neither recent */
+  recent_reason = "the same instance";
+  return 0;
+}
+
+int
+ospf6_lsa_lsd_num (struct ospf6_lsa_header *lsa_header)
+{
+  int ldnum = 0;
+  u_int16_t len;
+
+  len = ntohs (lsa_header->length);
+  len -= sizeof (struct ospf6_lsa_header);
+  if (lsa_header->type == htons (OSPF6_LSA_TYPE_ROUTER))
+    {
+      len -= sizeof (struct ospf6_router_lsa);
+      ldnum = len / sizeof (struct ospf6_router_lsd);
+    }
+  else /* (lsa_header->type == htons (OSPF6_LSA_TYPE_NETWORK)) */
+    {
+      len -= sizeof (struct ospf6_network_lsa);
+      ldnum = len / sizeof (u_int32_t);
+    }
+
+  return ldnum;
+}
+
+void *
+ospf6_lsa_lsd_get (int index, struct ospf6_lsa_header *lsa_header)
+{
+  void *p;
+  struct ospf6_router_lsa *router_lsa;
+  struct ospf6_router_lsd *router_lsd;
+  struct ospf6_network_lsa *network_lsa;
+  struct ospf6_network_lsd *network_lsd;
+
+  if (lsa_header->type == htons (OSPF6_LSA_TYPE_ROUTER))
+    {
+      router_lsa = (struct ospf6_router_lsa *) (lsa_header + 1);
+      router_lsd = (struct ospf6_router_lsd *) (router_lsa + 1);
+      router_lsd += index;
+      p = (void *) router_lsd;
+    }
+  else if (lsa_header->type == htons (OSPF6_LSA_TYPE_NETWORK))
+    {
+      network_lsa = (struct ospf6_network_lsa *) (lsa_header + 1);
+      network_lsd = (struct ospf6_network_lsd *) (network_lsa + 1);
+      network_lsd += index;
+      p = (void *) network_lsd;
+    }
+  else
+    {
+      p = (void *) NULL;
+    }
+
+  return p;
+}
+
+/* network_lsd <-> router_lsd */
+static int
+ospf6_lsa_lsd_network_reference_match (struct ospf6_network_lsd *network_lsd1,
+                                       struct ospf6_lsa_header *lsa_header1,
+                                       struct ospf6_router_lsd *router_lsd2,
+                                       struct ospf6_lsa_header *lsa_header2)
+{
+  if (network_lsd1->adv_router != lsa_header2->advrtr)
+    return 0;
+  if (router_lsd2->type != OSPF6_ROUTER_LSD_TYPE_TRANSIT_NETWORK)
+    return 0;
+  if (router_lsd2->neighbor_router_id != lsa_header1->advrtr)
+    return 0;
+  if (router_lsd2->neighbor_interface_id != lsa_header1->ls_id)
+    return 0;
+  return 1;
+}
+
+/* router_lsd <-> router_lsd */
+static int
+ospf6_lsa_lsd_router_reference_match (struct ospf6_router_lsd *router_lsd1,
+                                      struct ospf6_lsa_header *lsa_header1,
+                                      struct ospf6_router_lsd *router_lsd2,
+                                      struct ospf6_lsa_header *lsa_header2)
+{
+  if (router_lsd1->type != OSPF6_ROUTER_LSD_TYPE_POINTTOPOINT)
+    return 0;
+  if (router_lsd2->type != OSPF6_ROUTER_LSD_TYPE_POINTTOPOINT)
+    return 0;
+  if (router_lsd1->neighbor_router_id != lsa_header2->advrtr)
+    return 0;
+  if (router_lsd2->neighbor_router_id != lsa_header1->advrtr)
+    return 0;
+  if (router_lsd1->neighbor_interface_id != router_lsd2->interface_id)
+    return 0;
+  if (router_lsd2->neighbor_interface_id != router_lsd1->interface_id)
+    return 0;
+  return 1;
+}
+
+int
+ospf6_lsa_lsd_is_refer_ok (int index1, struct ospf6_lsa_header *lsa_header1,
+                           int index2, struct ospf6_lsa_header *lsa_header2)
+{
+  struct ospf6_router_lsd *r1, *r2;
+  struct ospf6_network_lsd *n;
+
+  r1 = (struct ospf6_router_lsd *) NULL;
+  r2 = (struct ospf6_router_lsd *) NULL;
+  n = (struct ospf6_network_lsd *) NULL;
+  if (lsa_header1->type == htons (OSPF6_LSA_TYPE_ROUTER))
+    r1 = (struct ospf6_router_lsd *) ospf6_lsa_lsd_get (index1, lsa_header1);
+  else
+    n = (struct ospf6_network_lsd *) ospf6_lsa_lsd_get (index1, lsa_header1);
+
+  if (lsa_header2->type == htons (OSPF6_LSA_TYPE_ROUTER))
+    r2 = (struct ospf6_router_lsd *) ospf6_lsa_lsd_get (index2, lsa_header2);
+  else
+    n = (struct ospf6_network_lsd *) ospf6_lsa_lsd_get (index2, lsa_header2);
+
+  if (r1 && r2)
+    return ospf6_lsa_lsd_router_reference_match (r1, lsa_header1,
+                                                 r2, lsa_header2);
+  else if (r1 && n)
+    return ospf6_lsa_lsd_network_reference_match (n, lsa_header2,
+                                                  r1, lsa_header1);
+  else if (n && r2)
+    return ospf6_lsa_lsd_network_reference_match (n, lsa_header1,
+                                                 r2, lsa_header2);
+  return 0;
+}
+
+void
+ospf6_lsa_show (struct vty *vty, struct ospf6_lsa *lsa)
+{
+  char adv_router[64], id[64], type[32];
+
+  assert (lsa);
+  assert (lsa->header);
+
+  ospf6_lsa_type_string (lsa->header->type, type, sizeof (type));
+  inet_ntop (AF_INET, &lsa->header->id, id, sizeof (id));
+  inet_ntop (AF_INET, &lsa->header->adv_router,
+             adv_router, sizeof (adv_router));
+
+  vty_out (vty, "%s", VTY_NEWLINE);
+  vty_out (vty, "Age: %4hu Type: %s%s", ospf6_lsa_age_current (lsa),
+           type, VTY_NEWLINE);
+  vty_out (vty, "Link State ID: %s%s", id, VTY_NEWLINE);
+  vty_out (vty, "Advertising Router: %s%s", adv_router, VTY_NEWLINE);
+  vty_out (vty, "LS Sequence Number: %#lx%s", (u_long)ntohl (lsa->header->seqnum),
+           VTY_NEWLINE);
+  vty_out (vty, "CheckSum: %#hx Length: %hu%s", ntohs (lsa->header->checksum),
+           ntohs (lsa->header->length), VTY_NEWLINE);
+
+  {
+    struct ospf6_lsa_slot *slot;
+    slot = ospf6_lsa_slot_get (lsa->header->type);
+    if (slot)
+      {
+        (*slot->func_show) (vty, lsa);
+        vty_out (vty, "%s", VTY_NEWLINE);
+        return;
+      }
+  }
+
+  vty_out (vty, "%sUnknown LSA type ...%s", VTY_NEWLINE, VTY_NEWLINE);
+}
+
+void
+ospf6_lsa_show_summary_header (struct vty *vty)
+{
+  vty_out (vty, "%-12s %-15s %-15s %4s %8s %4s %4s %-8s%s",
+           "Type", "LSId", "AdvRouter", "Age", "SeqNum",
+           "Cksm", "Len", "Duration", VTY_NEWLINE);
+}
+
+void
+ospf6_lsa_show_summary (struct vty *vty, struct ospf6_lsa *lsa)
+{
+  char adv_router[16], id[16], type[16];
+  struct timeval now, res;
+  char duration[16];
+
+  assert (lsa);
+  assert (lsa->header);
+
+  memset (type, 0, sizeof (type));
+  ospf6_lsa_type_string (lsa->header->type, type, 13);
+  inet_ntop (AF_INET, &lsa->header->id, id, sizeof (id));
+  inet_ntop (AF_INET, &lsa->header->adv_router, adv_router,
+             sizeof (adv_router));
+
+  gettimeofday (&now, NULL);
+  ospf6_timeval_sub (&now, &lsa->installed, &res);
+  ospf6_timeval_string_summary (&res, duration, sizeof (duration));
+
+  vty_out (vty, "%-12s %-15s %-15s %4hu %8lx %04hx %4hu %8s%s",
+           type, id, adv_router, ospf6_lsa_age_current (lsa),
+           (u_long) ntohl (lsa->header->seqnum),
+           ntohs (lsa->header->checksum), ntohs (lsa->header->length),
+           duration, VTY_NEWLINE);
+}
+
+void
+ospf6_lsa_show_dump (struct vty *vty, struct ospf6_lsa *lsa)
+{
+  u_char *start, *end, *current;
+  char byte[4];
+
+  start = (char *) lsa->header;
+  end = (char *) lsa->header + ntohs (lsa->header->length);
+
+  vty_out (vty, "%s", VTY_NEWLINE);
+  vty_out (vty, "%s:%s", lsa->str, VTY_NEWLINE);
+
+  for (current = start; current < end; current ++)
+    {
+      if ((current - start) % 16 == 0)
+        vty_out (vty, "%s        ", VTY_NEWLINE);
+      else if ((current - start) % 4 == 0)
+        vty_out (vty, " ");
+
+      snprintf (byte, sizeof (byte), "%02x", *current);
+      vty_out (vty, "%s", byte);
+    }
+
+  vty_out (vty, "%s%s", VTY_NEWLINE, VTY_NEWLINE);
+}
+
+/* OSPFv3 LSA creation/deletion function */
+
+/* calculate LS sequence number for my new LSA.
+   return value is network byte order */
+static signed long
+ospf6_lsa_seqnum_new (u_int16_t type, u_int32_t id, u_int32_t adv_router,
+                      void *scope)
+{
+  struct ospf6_lsa *lsa;
+  signed long seqnum;
+
+  /* get current database copy */
+  lsa = ospf6_lsdb_lookup (type, id, adv_router, scope);
+
+  /* if current database copy not found, return InitialSequenceNumber */
+  if (!lsa)
+    seqnum = INITIAL_SEQUENCE_NUMBER;
+  else
+    seqnum = (signed long) ntohl (lsa->header->seqnum) + 1;
+
+  return (htonl (seqnum));
+}
+
+#if 0
+static void
+ospf6_lsa_header_set (u_int16_t type, u_int32_t ls_id, u_int32_t advrtr,
+                      struct ospf6_lsa_header *lsa_header, int bodysize)
+{
+  /* fill LSA header */
+  lsa_header->age = 0;
+  lsa_header->type = type;
+  lsa_header->ls_id = ls_id;
+  lsa_header->advrtr = advrtr;
+  lsa_header->seqnum =
+    ospf6_lsa_seqnum_new (lsa_header->type, lsa_header->ls_id,
+                          lsa_header->advrtr);
+  lsa_header->length = htons (sizeof (struct ospf6_lsa_header) + bodysize);
+
+  /* LSA checksum */
+  ospf6_lsa_checksum (lsa_header);
+}
+#endif /*0*/
+
+struct ospf6_lsa *
+ospf6_lsa_create (struct ospf6_lsa_header *source)
+{
+  struct ospf6_lsa *lsa = NULL;
+  struct ospf6_lsa_header *lsa_header = NULL;
+  u_int16_t lsa_size = 0;
+  char buf_router[16], buf_id[16], typebuf[32];
+
+  /* whole length of this LSA */
+  lsa_size = ntohs (source->length);
+
+  /* allocate memory for this LSA */
+  lsa_header = (struct ospf6_lsa_header *)
+    XMALLOC (MTYPE_OSPF6_LSA, lsa_size);
+  if (! lsa_header)
+    {
+      zlog_err ("Can't allocate memory for LSA Header");
+      return (struct ospf6_lsa *) NULL;
+    }
+  memset (lsa_header, 0, lsa_size);
+
+  /* copy LSA from source */
+  memcpy (lsa_header, source, lsa_size);
+
+  /* LSA information structure */
+  /* allocate memory */
+  lsa = (struct ospf6_lsa *)
+          XMALLOC (MTYPE_OSPF6_LSA, sizeof (struct ospf6_lsa));
+  memset (lsa, 0, sizeof (struct ospf6_lsa));
+
+  lsa->lsa_hdr = (struct ospf6_lsa_hdr *) lsa_header;
+  lsa->header = (struct ospf6_lsa_header__ *) lsa_header;
+
+  lsa->summary = 0; /* this is not LSA summary */
+
+  /* dump string */
+  inet_ntop (AF_INET, &lsa->header->id, buf_id, sizeof (buf_id));
+  inet_ntop (AF_INET, &lsa->header->adv_router, buf_router,
+             sizeof (buf_router));
+  snprintf (lsa->str, sizeof (lsa->str), "[%s ID=%s Adv=%s]",
+            ospf6_lsa_type_string (lsa_header->type, typebuf,
+                                   sizeof (typebuf)),
+            buf_id, buf_router);
+
+  /* calculate birth, expire and refresh of this lsa */
+  ospf6_lsa_age_set (lsa);
+
+#ifdef DEBUG
+  if (IS_OSPF6_DUMP_LSA)
+    zlog_info ("Create: %s (%p/%p)", lsa->str, lsa, lsa->header);
+#endif /*DEBUG*/
+
+  return lsa;
+}
+
+struct ospf6_lsa *
+ospf6_lsa_summary_create (struct ospf6_lsa_header__ *source)
+{
+  struct ospf6_lsa *lsa = NULL;
+  struct ospf6_lsa_header *lsa_header = NULL;
+  u_int16_t lsa_size = 0;
+  char buf_router[16], buf_id[16], typebuf[16];
+
+  /* LSA summary contains LSA Header only */
+  lsa_size = sizeof (struct ospf6_lsa_header);
+
+  /* allocate memory for this LSA */
+  lsa_header = (struct ospf6_lsa_header *)
+    XMALLOC (MTYPE_OSPF6_LSA_SUMMARY, lsa_size);
+  memset (lsa_header, 0, lsa_size);
+
+  /* copy LSA from source */
+  memcpy (lsa_header, source, lsa_size);
+
+  /* LSA information structure */
+  /* allocate memory */
+  lsa = (struct ospf6_lsa *)
+          XMALLOC (MTYPE_OSPF6_LSA_SUMMARY, sizeof (struct ospf6_lsa));
+  memset (lsa, 0, sizeof (struct ospf6_lsa));
+
+  lsa->lsa_hdr = (struct ospf6_lsa_hdr *) lsa_header;
+  lsa->header = (struct ospf6_lsa_header__ *) lsa_header;
+  lsa->summary = 1; /* this is LSA summary */
+
+  /* dump string */
+  inet_ntop (AF_INET, &lsa->header->id, buf_id, sizeof (buf_id));
+  inet_ntop (AF_INET, &lsa->header->adv_router, buf_router,
+             sizeof (buf_router));
+  snprintf (lsa->str, sizeof (lsa->str), "[%s Summary ID=%s Adv=%s]",
+            ospf6_lsa_type_string (lsa->header->type, typebuf,
+                                   sizeof (typebuf)),
+            buf_id, buf_router);
+
+  /* calculate birth, expire and refresh of this lsa */
+  ospf6_lsa_age_set (lsa);
+
+#ifdef DEBUG
+  if (IS_OSPF6_DUMP_LSA)
+    zlog_info ("Create: %s (%p/%p)", lsa->str, lsa, lsa->header);
+#endif /*DEBUG*/
+
+  return lsa;
+}
+
+void
+ospf6_lsa_delete (struct ospf6_lsa *lsa)
+{
+  /* just to make sure */
+  if (lsa->lock != 0)
+    {
+      zlog_err ("Can't delete %s: lock: %ld", lsa->str, lsa->lock);
+      return;
+    }
+
+  /* cancel threads */
+  if (lsa->expire)
+    thread_cancel (lsa->expire);
+  lsa->expire = (struct thread *) NULL;
+  if (lsa->refresh)
+    thread_cancel (lsa->refresh);
+  lsa->refresh = (struct thread *) NULL;
+
+#ifdef DEBUG
+  if (IS_OSPF6_DUMP_LSA)
+      zlog_info ("Delete %s (%p/%p)", lsa->str, lsa, lsa->header);
+#endif /*DEBUG*/
+
+  /* do free */
+  if (lsa->summary)
+    XFREE (MTYPE_OSPF6_LSA_SUMMARY, lsa->header);
+  else
+    XFREE (MTYPE_OSPF6_LSA, lsa->header);
+  lsa->header = NULL;
+
+  if (lsa->summary)
+    XFREE (MTYPE_OSPF6_LSA_SUMMARY, lsa);
+  else
+    XFREE (MTYPE_OSPF6_LSA, lsa);
+}
+
+/* increment reference counter of  struct ospf6_lsa */
+void
+ospf6_lsa_lock (struct ospf6_lsa *lsa)
+{
+  lsa->lock++;
+  return;
+}
+
+/* decrement reference counter of  struct ospf6_lsa */
+void
+ospf6_lsa_unlock (struct ospf6_lsa *lsa)
+{
+  /* decrement reference counter */
+  if (lsa->lock > 0)
+    lsa->lock--;
+  else
+    zlog_warn ("Can't unlock %s: already no lock", lsa->str);
+
+  if (lsa->lock == 0)
+    ospf6_lsa_delete (lsa);
+}
+
+void
+ospf6_lsa_originate (u_int16_t type, u_int32_t id, u_int32_t adv_router,
+                     char *data, int data_len, void *scope)
+{
+  char buffer[MAXLSASIZE];
+  struct ospf6_lsa_header *lsa_header;
+  struct ospf6_lsa *lsa;
+  struct ospf6_lsa *old;
+
+  assert (data_len <= sizeof (buffer) - sizeof (struct ospf6_lsa_header));
+
+  lsa_header = (struct ospf6_lsa_header *) buffer;
+
+  /* Copy LSA Body */
+  memcpy (buffer + sizeof (struct ospf6_lsa_header), data, data_len);
+
+  /* Fill LSA Header */
+  lsa_header->age = 0;
+  lsa_header->type = type;
+  lsa_header->ls_id = id;
+  lsa_header->advrtr = adv_router;
+  lsa_header->seqnum =
+    ospf6_lsa_seqnum_new (lsa_header->type, lsa_header->ls_id,
+                          lsa_header->advrtr, scope);
+  lsa_header->length = htons (sizeof (struct ospf6_lsa_header) + data_len);
+
+  /* LSA checksum */
+  ospf6_lsa_checksum (lsa_header);
+
+  /* create LSA */
+  lsa = ospf6_lsa_create ((struct ospf6_lsa_header *) buffer);
+  lsa->scope = scope;
+
+  /* find previous LSA */
+  old = ospf6_lsdb_lookup (lsa->header->type, lsa->header->id,
+                           lsa->header->adv_router, lsa->scope);
+  if (old)
+    {
+      /* Check if this is neither different instance nor refresh, return */
+      if (! CHECK_FLAG (old->flag, OSPF6_LSA_FLAG_REFRESH) &&
+          ! ospf6_lsa_differ (lsa, old))
+        {
+          if (IS_OSPF6_DUMP_LSA)
+            zlog_info ("LSA: Suppress updating %s", lsa->str);
+          ospf6_lsa_delete (lsa);
+          return;
+        }
+    }
+
+  lsa->refresh = thread_add_timer (master, ospf6_lsa_refresh, lsa,
+                                   OSPF6_LS_REFRESH_TIME);
+  gettimeofday (&lsa->originated, NULL);
+
+  //if (IS_OSPF6_DUMP_LSA)
+    zlog_info ("LSA: originate %s seq: %#x age: %hu %ld.%06ld",
+               lsa->str, ntohl (lsa->header->seqnum),
+               ospf6_lsa_age_current (lsa),
+               lsa->originated.tv_sec, lsa->originated.tv_usec);
+
+  ospf6_dbex_remove_from_all_retrans_list (lsa);
+  ospf6_dbex_flood (lsa, NULL);
+  ospf6_lsdb_install (lsa);
+}
+
+
+/* ospf6_lsa expired */
+int
+ospf6_lsa_expire (struct thread *thread)
+{
+  struct ospf6_lsa *lsa;
+  struct ospf6_lsdb *lsdb = NULL;
+  void (*hook) (struct ospf6_lsa *, struct ospf6_lsa *);
+
+  lsa = (struct ospf6_lsa *) THREAD_ARG (thread);
+  assert (lsa && lsa->lsa_hdr);
+
+  /* assertion */
+  assert (IS_LSA_MAXAGE (lsa));
+  assert (!lsa->refresh);
+
+  lsa->expire = (struct thread *) NULL;
+
+  /* log */
+  if (IS_OSPF6_DUMP_LSA)
+    zlog_info ("LSA: Expire: %s", lsa->str);
+
+  if (!lsa->summary)
+    {
+      /* reflood lsa */
+      ospf6_dbex_flood (lsa, NULL);
+
+      /* get scoped lsdb, call remove hook */
+      if (OSPF6_LSA_IS_SCOPE_LINKLOCAL (ntohs (lsa->header->type)))
+        lsdb = ((struct ospf6_interface *) lsa->scope)->lsdb;
+      else if (OSPF6_LSA_IS_SCOPE_AREA (ntohs (lsa->header->type)))
+        lsdb = ((struct ospf6_area *) lsa->scope)->lsdb;
+      else if (OSPF6_LSA_IS_SCOPE_AS (ntohs (lsa->header->type)))
+        lsdb = ((struct ospf6 *) lsa->scope)->lsdb;
+      else
+        assert (0);
+
+      /* call LSDB hook to re-process LSA */
+      hook = ospf6_lsdb_hook[ntohs (lsa->header->type) &
+                             OSPF6_LSTYPE_CODE_MASK].hook;
+      if (hook)
+        (*hook) (NULL, lsa);
+
+      /* do not free LSA, and do nothing about lslists.
+         wait event (ospf6_lsdb_check_maxage) */
+    }
+
+  return 0;
+}
+
+int
+ospf6_lsa_refresh (struct thread *thread)
+{
+  struct ospf6_lsa *lsa;
+  struct ospf6_lsa_slot *slot;
+
+  assert (thread);
+  lsa = (struct ospf6_lsa *) THREAD_ARG  (thread);
+  assert (lsa && lsa->lsa_hdr);
+
+  /* this will be used later as flag to decide really originate */
+  lsa->refresh = (struct thread *) NULL;
+  SET_FLAG (lsa->flag, OSPF6_LSA_FLAG_REFRESH);
+
+  /* log */
+  if (IS_OSPF6_DUMP_LSA)
+    zlog_info ("LSA Refresh: %s", lsa->str);
+
+  slot = ospf6_lsa_slot_get (lsa->header->type);
+  if (slot)
+    {
+      zlog_info ("LSA Refresh: %s", slot->name);
+      (*slot->func_refresh) (lsa);
+      return 0;
+    }
+
+  zlog_warn ("Can't Refresh LSA: Unknown type: %#x",
+             ntohs (lsa->header->type));
+  return 1;
+}
+
+
+
+/* enhanced Fletcher checksum algorithm, RFC1008 7.2 */
+#define MODX                4102
+#define LSA_CHECKSUM_OFFSET   15
+
+unsigned short
+ospf6_lsa_checksum (struct ospf6_lsa_header *lsa_header)
+{
+  u_char *sp, *ep, *p, *q;
+  int c0 = 0, c1 = 0;
+  int x, y;
+  u_int16_t length;
+
+  lsa_header->checksum = 0;
+  length = ntohs (lsa_header->length) - 2;
+  sp = (char *) &lsa_header->type;
+
+  for (ep = sp + length; sp < ep; sp = q)
+    {
+      q = sp + MODX;
+      if (q > ep)
+        q = ep;
+      for (p = sp; p < q; p++)
+        {
+          c0 += *p;
+          c1 += c0;
+        }
+      c0 %= 255;
+      c1 %= 255;
+    }
+
+  /* r = (c1 << 8) + c0; */
+  x = ((length - LSA_CHECKSUM_OFFSET) * c0 - c1) % 255;
+  if (x <= 0)
+    x += 255;
+  y = 510 - c0 - x;
+  if (y > 255)
+    y -= 255;
+
+  lsa_header->checksum = htons ((x << 8) + y);
+
+  return (lsa_header->checksum);
+}
+
+int
+ospf6_lsa_is_known_type (struct ospf6_lsa_header *lsa_header)
+{
+  struct ospf6_lsa_slot *slot;
+
+  slot = ospf6_lsa_slot_get (lsa_header->type);
+  if (slot)
+    return 1;
+  return 0;
+}
+
+struct ospf6_lsa_slot *slot_head = NULL;
+
+struct ospf6_lsa_slot *
+ospf6_lsa_slot_get (u_int16_t type)
+{
+  struct ospf6_lsa_slot *slot;
+
+  for (slot = slot_head; slot; slot = slot->next)
+    {
+      if (slot->type == type)
+        return slot;
+    }
+
+  return NULL;
+}
+
+int
+ospf6_lsa_slot_register (struct ospf6_lsa_slot *src)
+{
+  struct ospf6_lsa_slot *new, *slot;
+
+  slot = ospf6_lsa_slot_get (src->type);
+  if (slot)
+    {
+      if (IS_OSPF6_DUMP_LSA)
+        zlog_info ("LSA: Slot register: already exists: %#x %s",
+                   slot->type, slot->name);
+      return -1;
+    }
+
+  new = (struct ospf6_lsa_slot *)
+    XMALLOC (MTYPE_OSPF6_LSA, sizeof (struct ospf6_lsa_slot));
+  if (! new)
+    {
+      zlog_err ("Can't allocate memory for LSA slot: %s", strerror (errno));
+      return -1;
+    }
+  memset (new, 0, sizeof (struct ospf6_lsa_slot));
+  memcpy (new, src, sizeof (struct ospf6_lsa_slot));
+
+  if (IS_OSPF6_DUMP_LSA)
+    zlog_info ("LSA: Slot register: %#x %s", slot->type, slot->name);
+
+  if (slot_head == NULL)
+    {
+      new->prev = NULL;
+      new->next = NULL;
+      slot_head = new;
+      return 0;
+    }
+
+  slot = slot_head;
+  while (slot->next)
+    slot = slot->next;
+
+  slot->next = new;
+  new->prev = slot;
+
+  return 0;
+}
+
+int
+ospf6_lsa_slot_unregister (u_int16_t type)
+{
+  struct ospf6_lsa_slot *slot;
+
+  slot = ospf6_lsa_slot_get (type);
+  if (slot == NULL)
+    {
+      if (IS_OSPF6_DUMP_LSA)
+        zlog_info ("Registering LSA slot: no such slot: %#x", type);
+      return -1;
+    }
+
+  if (IS_OSPF6_DUMP_LSA)
+    zlog_info ("Unregistering LSA Slot: %#x %s", slot->type, slot->name);
+
+  if (slot->prev)
+    slot->prev->next = slot->next;
+  if (slot->next)
+    slot->next->prev = slot->prev;
+
+  if (slot_head == slot)
+    slot_head = slot->next;
+
+  XFREE (MTYPE_OSPF6_LSA, slot);
+  return 0;
+}
+
+char *
+ospf6_lsa_type_string (u_int16_t type, char *buf, int bufsize)
+{
+  struct ospf6_lsa_slot *slot;
+
+  slot = ospf6_lsa_slot_get (type);
+  if (slot)
+    snprintf (buf, bufsize, "%s", slot->name);
+  else
+    snprintf (buf, bufsize, "Type=0x%04x", ntohs (type));
+
+  return buf;
+}
+
+
+/*******************/
+/* LSA Origination */
+/*******************/
+
+#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_LSA)\
+        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_LSA)\
+        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_LSA)\
+        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_LSA)\
+        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_LSA)\
+        zlog_info ("  Filter out V4Mapped: %s", buf);\
+      continue;\
+    }
+
+/******************************/
+/* RFC2740 3.4.3.1 Router-LSA */
+/******************************/
+
+char *
+ospf6_lsa_router_bits_string (u_char router_bits, char *buf, int size)
+{
+  char w, v, e, b;
+
+  w = (router_bits & OSPF6_ROUTER_LSA_BIT_W ? 'W' : '-');
+  v = (router_bits & OSPF6_ROUTER_LSA_BIT_V ? 'V' : '-');
+  e = (router_bits & OSPF6_ROUTER_LSA_BIT_E ? 'E' : '-');
+  b = (router_bits & OSPF6_ROUTER_LSA_BIT_B ? 'B' : '-');
+  snprintf (buf, size, "----%c%c%c%c", w, v, e, b);
+  return buf;
+}
+
+int
+ospf6_lsa_router_show (struct vty *vty, struct ospf6_lsa *lsa)
+{
+  char *start, *end, *current;
+  char buf[32], name[32], bits[32], options[32];
+  struct ospf6_router_lsa *router_lsa;
+  struct ospf6_router_lsd *lsdesc;
+
+  assert (lsa->header);
+
+  router_lsa = (struct ospf6_router_lsa *)
+    ((char *) lsa->header + sizeof (struct ospf6_lsa_header));
+
+  ospf6_lsa_router_bits_string (router_lsa->bits, bits, sizeof (bits));
+  ospf6_options_string (router_lsa->options, options, sizeof (options));
+  vty_out (vty, "    Bits: %s Options: %s%s", bits, options, VTY_NEWLINE);
+
+  start = (char *) router_lsa + sizeof (struct ospf6_router_lsa);
+  end = (char *) lsa->header + ntohs (lsa->header->length);
+  for (current = start; current + sizeof (struct ospf6_router_lsd) <= end;
+       current += sizeof (struct ospf6_router_lsd))
+    {
+      lsdesc = (struct ospf6_router_lsd *) current;
+
+      if (lsdesc->type == OSPF6_ROUTER_LSD_TYPE_POINTTOPOINT)
+        snprintf (name, sizeof (name), "Point-To-Point");
+      else if (lsdesc->type == OSPF6_ROUTER_LSD_TYPE_TRANSIT_NETWORK)
+        snprintf (name, sizeof (name), "Transit-Network");
+      else if (lsdesc->type == OSPF6_ROUTER_LSD_TYPE_STUB_NETWORK)
+        snprintf (name, sizeof (name), "Stub-Network");
+      else if (lsdesc->type == OSPF6_ROUTER_LSD_TYPE_VIRTUAL_LINK)
+        snprintf (name, sizeof (name), "Virtual-Link");
+      else
+        snprintf (name, sizeof (name), "Unknown (%#x)", lsdesc->type);
+
+      vty_out (vty, "    Type: %s Metric: %d%s",
+               name, ntohs (lsdesc->metric), VTY_NEWLINE);
+      vty_out (vty, "    Interface ID: %s%s",
+               inet_ntop (AF_INET, &lsdesc->interface_id,
+                          buf, sizeof (buf)), VTY_NEWLINE);
+      vty_out (vty, "    Neighbor Interface ID: %s%s",
+               inet_ntop (AF_INET, &lsdesc->neighbor_interface_id,
+                          buf, sizeof (buf)), VTY_NEWLINE);
+      vty_out (vty, "    Neighbor Router ID: %s%s",
+               inet_ntop (AF_INET, &lsdesc->neighbor_router_id,
+                          buf, sizeof (buf)), VTY_NEWLINE);
+    }
+  return 0;
+}
+
+u_long
+ospf6_lsa_has_elasped (u_int16_t type, u_int32_t id,
+                       u_int32_t adv_router, void *scope)
+{
+  struct ospf6_lsa *old;
+  struct timeval now;
+
+  if (adv_router != ospf6->router_id)
+    zlog_info ("LSA: Router-ID changed ?");
+
+  old = ospf6_lsdb_lookup (type, id, adv_router, scope);
+  if (! old)
+    return OSPF6_LSA_MAXAGE;
+
+  gettimeofday (&now, NULL);
+  return ((u_long) SEC_TVDIFF (&now, &old->originated));
+}
+
+int
+ospf6_lsa_originate_router (struct thread *thread)
+{
+  char buffer [MAXLSASIZE];
+  u_int16_t size;
+  struct ospf6_area *o6a;
+  int count;
+  u_int32_t area_id;
+
+  struct ospf6_router_lsa *router_lsa;
+  struct ospf6_router_lsd *router_lsd;
+  listnode i;
+  struct ospf6_interface *o6i;
+  struct ospf6_neighbor *o6n = NULL;
+
+  area_id = (u_int32_t) THREAD_ARG (thread);
+
+  o6a = ospf6_area_lookup (area_id, ospf6);
+  if (! o6a)
+    {
+      inet_ntop (AF_INET, &area_id, buffer, sizeof (buffer));
+      if (IS_OSPF6_DUMP_LSA)
+        zlog_info ("LSA: Update Router-LSA: No such area: %s", buffer);
+      return 0;
+    }
+
+  /* clear thread */
+  o6a->thread_router_lsa = NULL;
+
+  if (IS_OSPF6_DUMP_LSA)
+    zlog_info ("LSA: originate Router-LSA for Area %s", o6a->str);
+
+  size = sizeof (struct ospf6_router_lsa);
+  memset (buffer, 0, sizeof (buffer));
+  router_lsa = (struct ospf6_router_lsa *) buffer;
+
+  OSPF6_OPT_CLEAR_ALL (router_lsa->options);
+  OSPF6_OPT_SET (router_lsa->options, OSPF6_OPT_V6);
+  OSPF6_OPT_SET (router_lsa->options, OSPF6_OPT_E);
+  OSPF6_OPT_CLEAR (router_lsa->options, OSPF6_OPT_MC);
+  OSPF6_OPT_CLEAR (router_lsa->options, OSPF6_OPT_N);
+  OSPF6_OPT_SET (router_lsa->options, OSPF6_OPT_R);
+  OSPF6_OPT_CLEAR (router_lsa->options, OSPF6_OPT_DC);
+
+  OSPF6_ROUTER_LSA_CLEAR_ALL_BITS (router_lsa);
+  OSPF6_ROUTER_LSA_CLEAR (router_lsa, OSPF6_ROUTER_LSA_BIT_B);
+
+  if (ospf6_is_asbr (o6a->ospf6))
+    OSPF6_ROUTER_LSA_SET (router_lsa, OSPF6_ROUTER_LSA_BIT_E);
+  else
+    OSPF6_ROUTER_LSA_CLEAR (router_lsa, OSPF6_ROUTER_LSA_BIT_E);
+
+  OSPF6_ROUTER_LSA_CLEAR (router_lsa, OSPF6_ROUTER_LSA_BIT_V);
+  OSPF6_ROUTER_LSA_CLEAR (router_lsa, OSPF6_ROUTER_LSA_BIT_W);
+
+  /* describe links for each interfaces */
+  router_lsd = (struct ospf6_router_lsd *) (router_lsa + 1);
+  for (i = listhead (o6a->if_list); i; nextnode (i))
+    {
+      o6i = (struct ospf6_interface *) getdata (i);
+      assert (o6i);
+
+      /* Interfaces in state Down or Loopback are not described */
+      if (o6i->state == IFS_DOWN || o6i->state == IFS_LOOPBACK)
+        continue;
+
+      /* Nor are interfaces without any full adjacencies described */
+      count = 0;
+      o6i->foreach_nei (o6i, &count, NBS_FULL, ospf6_count_state);
+      if (count == 0)
+        continue;
+
+      /* Point-to-Point interfaces */
+      if (if_is_pointopoint (o6i->interface))
+        {
+          if (listcount (o6i->neighbor_list) == 0)
+            continue;
+
+          if (listcount (o6i->neighbor_list) != 1)
+            zlog_warn ("LSA: Multiple neighbors on PoinToPoint: %s",
+                       o6i->interface->name);
+
+          o6n = (struct ospf6_neighbor *)
+                   getdata (listhead (o6i->neighbor_list));
+          assert (o6n);
+
+          router_lsd->type = OSPF6_ROUTER_LSD_TYPE_POINTTOPOINT;
+          router_lsd->metric = htons (o6i->cost);
+          router_lsd->interface_id = htonl (o6i->if_id);
+          router_lsd->neighbor_interface_id = htonl (o6n->ifid);
+          router_lsd->neighbor_router_id = o6n->router_id;
+
+          size += sizeof (struct ospf6_router_lsd);
+          router_lsd ++;
+
+          continue;
+        }
+
+      /* Broadcast and NBMA interfaces */
+      if (if_is_broadcast (o6i->interface))
+        {
+          /* If this router is not DR,
+             and If this router not fully adjacent with DR,
+             this interface is not transit yet: ignore. */
+          if (o6i->state != IFS_DR)
+            {
+              o6n = ospf6_neighbor_lookup (o6i->dr, o6i); /* find DR */
+              if (o6n == NULL || o6n->state != NBS_FULL)
+                continue;
+            }
+          else
+            {
+              count = 0;
+              o6i->foreach_nei (o6i, &count, NBS_FULL, ospf6_count_state);
+              if (count == 0)
+                continue;
+            }
+
+          router_lsd->type = OSPF6_ROUTER_LSD_TYPE_TRANSIT_NETWORK;
+          router_lsd->metric = htons (o6i->cost);
+          router_lsd->interface_id = htonl (o6i->if_id);
+          if (o6i->state != IFS_DR)
+            {
+              router_lsd->neighbor_interface_id = htonl (o6n->ifid);
+              router_lsd->neighbor_router_id = o6n->router_id;
+            }
+          else
+            {
+              router_lsd->neighbor_interface_id = htonl (o6i->if_id);
+              router_lsd->neighbor_router_id = o6i->area->ospf6->router_id;
+            }
+
+          size += sizeof (struct ospf6_router_lsd);
+          router_lsd ++;
+
+          continue;
+        }
+
+      /* Virtual links */
+        /* xxx */
+      /* Point-to-Multipoint interfaces */
+        /* xxx */
+    }
+
+  ospf6_lsa_originate (htons (OSPF6_LSA_TYPE_ROUTER),
+                       htonl (0), o6a->ospf6->router_id,
+                       (char *) router_lsa, size, o6a);
+  return 0;
+}
+
+void
+ospf6_lsa_schedule_router (struct ospf6_area *area)
+{
+  u_long elasped_time, time = 0;
+
+  if (area->thread_router_lsa)
+    {
+      if (IS_OSPF6_DUMP_LSA)
+        zlog_info ("LSA: schedule: Router-LSA for Area %s: another thread",
+                   area->str);
+      return;
+    }
+
+  elasped_time =
+    ospf6_lsa_has_elasped (htons (OSPF6_LSA_TYPE_ROUTER), htonl (0),
+                           area->ospf6->router_id, area);
+  if (elasped_time < OSPF6_MIN_LS_INTERVAL)
+    time = (u_long) (OSPF6_MIN_LS_INTERVAL - elasped_time);
+  else
+    time = 0;
+
+  if (IS_OSPF6_DUMP_LSA)
+    zlog_info ("LSA: schedule: Router-LSA for Area %s after %lu sec",
+               area->str, time);
+
+  if (time)
+    area->thread_router_lsa =
+      thread_add_timer (master, ospf6_lsa_originate_router,
+                        (void *) area->area_id, time);
+  else
+    area->thread_router_lsa =
+      thread_add_event (master, ospf6_lsa_originate_router,
+                        (void *) area->area_id, 0);
+}
+
+int
+ospf6_lsa_router_hook_neighbor (void *neighbor)
+{
+  struct ospf6_neighbor *o6n = neighbor;
+  if (o6n->ospf6_interface->area)
+    ospf6_lsa_schedule_router (o6n->ospf6_interface->area);
+  return 0;
+}
+
+int
+ospf6_lsa_router_hook_interface (void *interface)
+{
+  struct ospf6_interface *o6i = interface;
+  if (o6i->area)
+    ospf6_lsa_schedule_router (o6i->area);
+  return 0;
+}
+
+int
+ospf6_lsa_router_hook_area (void *area)
+{
+  struct ospf6_area *o6a = area;
+  ospf6_lsa_schedule_router (o6a);
+  return 0;
+}
+
+int
+ospf6_lsa_router_hook_top (void *ospf6)
+{
+  struct ospf6 *o6 = ospf6;
+  struct ospf6_area *o6a;
+  listnode node;
+
+  for (node = listhead (o6->area_list); node; nextnode (node))
+    {
+      o6a = getdata (node);
+      ospf6_lsa_schedule_router (o6a);
+    }
+  return 0;
+}
+
+int
+ospf6_lsa_router_refresh (void *old)
+{
+  struct ospf6_lsa *lsa = old;
+  struct ospf6_area *o6a;
+
+  o6a = lsa->scope;
+  ospf6_lsa_schedule_router (o6a);
+  return 0;
+}
+
+void
+ospf6_lsa_slot_register_router ()
+{
+  struct ospf6_lsa_slot slot;
+  struct ospf6_hook hook;
+
+  memset (&slot, 0, sizeof (struct ospf6_lsa_slot));
+  slot.type              = htons (OSPF6_LSA_TYPE_ROUTER);
+  slot.name              = "Router";
+  slot.func_show         = ospf6_lsa_router_show;
+  slot.func_refresh      = ospf6_lsa_router_refresh;
+  ospf6_lsa_slot_register (&slot);
+
+  ospf6_lsdb_hook[OSPF6_LSA_TYPE_ROUTER & OSPF6_LSTYPE_CODE_MASK].hook = 
+    ospf6_spf_database_hook;
+
+  memset (&hook, 0, sizeof (hook));
+  hook.name = "OriginateRouter";
+  hook.hook_change  = ospf6_lsa_router_hook_neighbor;
+  ospf6_hook_register (&hook, &neighbor_hook);
+
+  memset (&hook, 0, sizeof (hook));
+  hook.name = "OriginateRouter";
+  hook.hook_change = ospf6_lsa_router_hook_interface;
+  ospf6_hook_register (&hook, &interface_hook);
+
+  memset (&hook, 0, sizeof (hook));
+  hook.name = "OriginateRouter";
+  hook.hook_change      = ospf6_lsa_router_hook_area;
+  ospf6_hook_register (&hook, &area_hook);
+
+  memset (&hook, 0, sizeof (hook));
+  hook.name = "OriginateRouter";
+  hook.hook_change       = ospf6_lsa_router_hook_top;
+  ospf6_hook_register (&hook, &top_hook);
+}
+
+/*******************************/
+/* RFC2740 3.4.3.2 Network-LSA */
+/*******************************/
+
+int
+ospf6_lsa_network_show (struct vty *vty, struct ospf6_lsa *lsa)
+{
+  char *start, *end, *current;
+  struct ospf6_network_lsa *network_lsa;
+  u_int32_t *router_id;
+  char buf[128], options[32];
+
+  assert (lsa->header);
+  network_lsa = (struct ospf6_network_lsa *) (lsa->header + 1);
+  router_id = (u_int32_t *)(network_lsa + 1);
+
+  ospf6_options_string (network_lsa->options, options, sizeof (options));
+  vty_out (vty, "     Options: %s%s", options, VTY_NEWLINE);
+
+  start = (char *) network_lsa + sizeof (struct ospf6_network_lsa);
+  end = (char *) lsa->header + ntohs (lsa->header->length);
+  for (current = start; current + sizeof (u_int32_t) <= end;
+       current += sizeof (u_int32_t))
+    {
+      router_id = (u_int32_t *) current;
+      inet_ntop (AF_INET, router_id, buf, sizeof (buf));
+      vty_out (vty, "     Attached Router: %s%s", buf, VTY_NEWLINE);
+    }
+  return 0;
+}
+
+void
+ospf6_lsa_network_update (char *ifname)
+{
+  char buffer [MAXLSASIZE];
+  u_int16_t size;
+  struct ospf6_lsa *old;
+  struct interface *ifp;
+  struct ospf6_interface *o6i;
+  int count;
+
+  struct ospf6_network_lsa *network_lsa;
+  struct ospf6_neighbor *o6n;
+  u_int32_t *router_id;
+  listnode node;
+
+  ifp = if_lookup_by_name (ifname);
+  if (! ifp)
+    {
+      if (IS_OSPF6_DUMP_LSA)
+        zlog_warn ("Update Network: No such Interface: %s", ifname);
+      return;
+    }
+
+  o6i = (struct ospf6_interface *) ifp->info;
+  if (! o6i || ! o6i->area)
+    {
+      if (IS_OSPF6_DUMP_LSA)
+        zlog_warn ("Update Network: Interface not enabled: %s", ifname);
+      return;
+    }
+
+  /* find previous LSA */
+  old = ospf6_lsdb_lookup (htons (OSPF6_LSA_TYPE_NETWORK),
+                           htonl (o6i->if_id),
+                           o6i->area->ospf6->router_id, o6i->area);
+
+  /* Don't originate Network-LSA if not DR */
+  if (o6i->state != IFS_DR)
+    {
+      if (IS_OSPF6_DUMP_LSA)
+        zlog_info ("Update Network: Interface %s is not DR",
+                   o6i->interface->name);
+      if (old)
+        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_LSA)
+        zlog_info ("Update Network: Interface %s is Stub",
+                   o6i->interface->name);
+      if (old)
+        ospf6_lsa_premature_aging (old);
+      return;
+    }
+
+  if (IS_OSPF6_DUMP_LSA)
+    zlog_info ("Update Network: Interface %s", o6i->interface->name);
+
+  /* prepare buffer */
+  memset (buffer, 0, sizeof (buffer));
+  size = sizeof (struct ospf6_network_lsa);
+  network_lsa = (struct ospf6_network_lsa *) buffer;
+  router_id = (u_int32_t *)(network_lsa + 1);
+
+  /* set fields of myself */
+  *router_id++ = o6i->area->ospf6->router_id;
+  size += sizeof (u_int32_t);
+  network_lsa->options[0] |= o6i->area->options[0];
+  network_lsa->options[1] |= o6i->area->options[1];
+  network_lsa->options[2] |= o6i->area->options[2];
+
+  /* Walk through neighbors */
+  for (node = listhead (o6i->neighbor_list); node; nextnode (node))
+    {
+      o6n = (struct ospf6_neighbor *) getdata (node);
+
+      if (o6n->state != NBS_FULL)
+        continue;
+
+      /* set this neighbor's Router-ID to LSA */
+      *router_id++ = o6n->router_id;
+      size += sizeof (u_int32_t);
+
+      /* options field is logical OR */
+      network_lsa->options[0] |= o6n->options[0];
+      network_lsa->options[1] |= o6n->options[1];
+      network_lsa->options[2] |= o6n->options[2];
+    }
+
+  ospf6_lsa_originate (htons (OSPF6_LSA_TYPE_NETWORK),
+                       htonl (o6i->if_id), o6i->area->ospf6->router_id,
+                       (char *) network_lsa, size, o6i->area);
+}
+
+int
+ospf6_lsa_network_hook_neighbor (void *neighbor)
+{
+  struct ospf6_neighbor *o6n = neighbor;
+  ospf6_lsa_network_update (o6n->ospf6_interface->interface->name);
+  return 0;
+}
+
+int
+ospf6_lsa_network_hook_interface (void *interface)
+{
+  struct ospf6_interface *o6i = interface;
+  if (o6i->area)
+    ospf6_lsa_network_update (o6i->interface->name);
+  return 0;
+}
+
+int
+ospf6_lsa_network_refresh (void *old)
+{
+  struct ospf6_lsa *lsa = old;
+  struct interface *ifp;
+
+  ifp = if_lookup_by_index (ntohl (lsa->header->id));
+  if (! ifp)
+    ospf6_lsa_premature_aging (old);
+  else
+    ospf6_lsa_network_update (ifp->name);
+
+  return 0;
+}
+
+void
+ospf6_lsa_slot_register_network ()
+{
+  struct ospf6_lsa_slot slot;
+  struct ospf6_hook hook;
+
+  memset (&slot, 0, sizeof (struct ospf6_lsa_slot));
+  slot.type              = htons (OSPF6_LSA_TYPE_NETWORK);
+  slot.name              = "Network";
+  slot.func_show         = ospf6_lsa_network_show;
+  slot.func_refresh      = ospf6_lsa_network_refresh;
+  ospf6_lsa_slot_register (&slot);
+
+  ospf6_lsdb_hook[OSPF6_LSA_TYPE_NETWORK & OSPF6_LSTYPE_CODE_MASK].hook = 
+    ospf6_spf_database_hook;
+
+  memset (&hook, 0, sizeof (hook));
+  hook.name  = "OriginateNetwork";
+  hook.hook_change  = ospf6_lsa_network_hook_neighbor;
+  ospf6_hook_register (&hook, &neighbor_hook);
+
+  memset (&hook, 0, sizeof (hook));
+  hook.name  = "OriginateNetwork";
+  hook.hook_change = ospf6_lsa_network_hook_interface;
+  ospf6_hook_register (&hook, &interface_hook);
+}
+
+/****************************/
+/* RFC2740 3.4.3.6 Link-LSA */
+/****************************/
+
+int
+ospf6_lsa_link_show (struct vty *vty, struct ospf6_lsa *lsa)
+{
+  char *start, *end, *current;
+  struct ospf6_link_lsa *link_lsa;
+  int prefixnum;
+  struct ospf6_prefix *prefix;
+  char buf[128];
+  struct in6_addr in6;
+
+  assert (lsa->header);
+
+  link_lsa = (struct ospf6_link_lsa *) (lsa->header + 1);
+  prefixnum = ntohl (link_lsa->llsa_prefix_num);
+
+  inet_ntop (AF_INET6, (void *)&link_lsa->llsa_linklocal, buf, sizeof (buf));
+  vty_out (vty, "     LinkLocal Address: %s%s", buf, VTY_NEWLINE);
+  vty_out (vty, "     Number of Prefix: %d%s", prefixnum, VTY_NEWLINE);
+
+  start = (char *) link_lsa + sizeof (struct ospf6_link_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_link_update (char *ifname)
+{
+  char *cp, buffer [MAXLSASIZE], buf[32];
+  u_int16_t size;
+  struct ospf6_lsa *old;
+  struct interface *ifp;
+  struct ospf6_interface *o6i;
+
+  struct ospf6_link_lsa *link_lsa;
+  struct ospf6_prefix *p;
+  list prefix_connected;
+  listnode node;
+  struct connected *c;
+
+  ifp = if_lookup_by_name (ifname);
+  if (! ifp)
+    {
+      if (IS_OSPF6_DUMP_LSA)
+        zlog_info ("Update Link: No such Interface: %s", ifname);
+      return;
+    }
+
+  o6i = (struct ospf6_interface *) ifp->info;
+  if (! o6i || ! o6i->area)
+    {
+      if (IS_OSPF6_DUMP_LSA)
+        zlog_info ("Update Link: Interface not enabled: %s", ifname);
+      return;
+    }
+
+#if 0
+  /* Link-LSA is on Broadcast or NBMA */
+  if (! if_is_broadcast (o6i->interface) /* && ! NBMA xxx */)
+    {
+      return;
+    }
+#endif /*0*/
+
+  /* find previous LSA */
+  old = ospf6_lsdb_lookup (htons (OSPF6_LSA_TYPE_LINK), htonl (o6i->if_id),
+                           ospf6->router_id, o6i->area);
+
+  /* can't make Link-LSA if linklocal address not set */
+  if (! o6i->lladdr)
+    {
+      if (IS_OSPF6_DUMP_LSA)
+        zlog_warn ("Update Link: No Linklocal Address: %s",
+                   o6i->interface->name);
+      if (old)
+        ospf6_lsa_premature_aging (old);
+      return;
+    }
+
+  if (IS_OSPF6_DUMP_LSA)
+    zlog_info ("Update Link: Interface %s", o6i->interface->name);
+
+  if (! ospf6_interface_is_enabled (o6i->interface->ifindex))
+    {
+      if (IS_OSPF6_DUMP_LSA)
+        zlog_info ("  Interface %s not enabled", o6i->interface->name);
+      if (old)
+        ospf6_lsa_premature_aging (old);
+      return;
+    }
+
+  /* check connected prefix */
+  prefix_connected = list_new ();
+  for (node = listhead (o6i->interface->connected); node; nextnode (node))
+    {
+      c = (struct connected *) getdata (node);
+
+      /* 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 if (IS_OSPF6_DUMP_LSA)
+            zlog_warn ("Update Intra-Prefix (Stub): "
+                       "Prefix list \"%s\" not found", o6i->plist_name);
+
+          if (result == PREFIX_DENY)
+            {
+              if (IS_OSPF6_DUMP_LSA)
+                zlog_info ("  Filter out Prefix-list %s: %s",
+                           o6i->plist_name, buf);
+              continue;
+            }
+        }
+
+      if (IS_OSPF6_DUMP_LSA)
+        zlog_info ("    Advertise %s", buf);
+
+      /* hold prefix in list. duplicate is filtered in ospf6_prefix_add() */
+      p = ospf6_prefix_create (0, 0, (struct prefix_ipv6 *) c->address);
+      ospf6_prefix_add (prefix_connected, p);
+    }
+
+  /* Note: even if no prefix configured, still we have to create Link-LSA
+     for next-hop resolution */
+
+  memset (buffer, 0, sizeof (buffer));
+  size = sizeof (struct ospf6_link_lsa);
+  link_lsa = (struct ospf6_link_lsa *) buffer;
+
+  /* fill Link LSA and calculate size */
+  link_lsa->llsa_rtr_pri = o6i->priority;
+  link_lsa->llsa_options[0] = o6i->area->options[0];
+  link_lsa->llsa_options[1] = o6i->area->options[1];
+  link_lsa->llsa_options[2] = o6i->area->options[2];
+
+  /* linklocal address */
+  memcpy (&link_lsa->llsa_linklocal, o6i->lladdr, sizeof (struct in6_addr));
+
+#ifdef KAME /* clear ifindex */
+  if (link_lsa->llsa_linklocal.s6_addr[3] & 0x0f)
+    link_lsa->llsa_linklocal.s6_addr[3] &= ~((char)0x0f);
+#endif /* KAME */
+
+  link_lsa->llsa_prefix_num = htonl (listcount (prefix_connected));
+  cp = (char *)(link_lsa + 1);
+  for (node = listhead (prefix_connected); node; nextnode (node))
+    {
+      p = (struct ospf6_prefix *) getdata (node);
+      size += OSPF6_PREFIX_SIZE (p);
+      memcpy (cp, p, OSPF6_PREFIX_SIZE (p));
+      cp += OSPF6_PREFIX_SIZE (p);
+    }
+
+  for (node = listhead (prefix_connected); node; nextnode (node))
+    {
+      p = (struct ospf6_prefix *) getdata (node);
+      ospf6_prefix_delete (p);
+    }
+  list_delete (prefix_connected);
+
+  ospf6_lsa_originate (htons (OSPF6_LSA_TYPE_LINK),
+                       htonl (o6i->if_id), o6i->area->ospf6->router_id,
+                       (char *) link_lsa, size, o6i);
+}
+
+int
+ospf6_lsa_link_hook_interface (void *interface)
+{
+  struct ospf6_interface *o6i = interface;
+  if (o6i->area)
+    ospf6_lsa_link_update (o6i->interface->name);
+  return 0;
+}
+
+int
+ospf6_lsa_link_refresh (void *old)
+{
+  struct ospf6_lsa *lsa = old;
+  struct interface *ifp;
+
+  ifp = if_lookup_by_index (ntohl (lsa->header->id));
+  if (! ifp)
+    ospf6_lsa_premature_aging (old);
+  else
+    ospf6_lsa_link_update (ifp->name);
+
+  return 0;
+}
+
+void
+ospf6_lsa_slot_register_link ()
+{
+  struct ospf6_lsa_slot slot;
+
+  memset (&slot, 0, sizeof (struct ospf6_lsa_slot));
+  slot.type              = htons (OSPF6_LSA_TYPE_LINK);
+  slot.name              = "Link";
+  slot.func_show         = ospf6_lsa_link_show;
+  slot.func_refresh      = ospf6_lsa_link_refresh;
+  slot.hook_interface.name = "OriginateLink";
+  slot.hook_interface.hook_change = ospf6_lsa_link_hook_interface;
+  ospf6_lsa_slot_register (&slot);
+
+  /*
+   * Link LSA handling will be shift in ospf6_intra.c
+   * Currently, only database hook only moved to ospf6_intra.c
+   */
+#if 0
+  ospf6_lsdb_hook[OSPF6_LSA_TYPE_LINK & OSPF6_LSTYPE_CODE_MASK].hook = 
+    ospf6_spf_database_hook;
+#endif /*0*/
+}
+
+int
+ospf6_lsa_add_hook (void *data)
+{
+  struct ospf6_lsa *lsa = data;
+  struct ospf6_lsa_slot *sp;
+
+  sp = ospf6_lsa_slot_get (lsa->header->type);
+  if (sp)
+    {
+      CALL_CHANGE_HOOK (&sp->database_hook, lsa);
+    }
+  else
+    zlog_warn ("Unknown LSA added to database: %s", lsa->str);
+  return 0;
+}
+
+int
+ospf6_lsa_change_hook (void *data)
+{
+  struct ospf6_lsa *lsa = data;
+  struct ospf6_lsa_slot *sp;
+
+  sp = ospf6_lsa_slot_get (lsa->header->type);
+  if (sp)
+    {
+      CALL_CHANGE_HOOK (&sp->database_hook, lsa);
+    }
+  else
+    zlog_warn ("Unknown LSA changed in database: %s", lsa->str);
+  return 0;
+}
+
+int
+ospf6_lsa_remove_hook (void *data)
+{
+  struct ospf6_lsa *lsa = data;
+  struct ospf6_lsa_slot *sp;
+
+  sp = ospf6_lsa_slot_get (lsa->header->type);
+  if (sp)
+    {
+      CALL_REMOVE_HOOK (&sp->database_hook, lsa);
+    }
+  else
+    zlog_warn ("Unknown LSA removed from database: %s", lsa->str);
+  return 0;
+}
+
+/* Initialize LSA slots */
+void
+ospf6_lsa_init ()
+{
+  struct ospf6_hook hook;
+
+  slot_head = NULL;
+  ospf6_lsa_slot_register_router ();
+  ospf6_lsa_slot_register_network ();
+  ospf6_lsa_slot_register_link ();
+#if 0
+  ospf6_lsa_slot_register_intra_prefix ();
+  ospf6_lsa_slot_register_as_external ();
+#endif /*0*/
+
+  hook.name = "LSADatabaseHook";
+  hook.hook_add = ospf6_lsa_add_hook;
+  hook.hook_change = ospf6_lsa_change_hook;
+  hook.hook_remove = ospf6_lsa_remove_hook;
+  ospf6_hook_register (&hook, &database_hook);
+}
+