Initial revision
diff --git a/ospf6d/ospf6_dbex.c b/ospf6d/ospf6_dbex.c
new file mode 100644
index 0000000..b10d9ae
--- /dev/null
+++ b/ospf6d/ospf6_dbex.c
@@ -0,0 +1,704 @@
+/*
+ * 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"
+
+/* check validity and put lsa in reqestlist if needed. */
+/* returns -1 if SeqNumMismatch required. */
+int
+ospf6_dbex_check_dbdesc_lsa_header (struct ospf6_lsa_header *lsa_header,
+                                    struct ospf6_neighbor *from)
+{
+  struct ospf6_lsa *received = NULL;
+  struct ospf6_lsa *have = NULL;
+
+  received = ospf6_lsa_summary_create
+    ((struct ospf6_lsa_header__ *) lsa_header);
+
+  /* case when received is AS-External though neighbor belongs stub area */
+  if (lsa_header->type == htons (OSPF6_LSA_TYPE_AS_EXTERNAL) &&
+      ospf6_area_is_stub (from->ospf6_interface->area))
+    {
+      zlog_err ("DbDesc %s receive from %s", from->str, received->str);
+      zlog_err ("    E-bit mismatch: %s", received->str);
+      ospf6_lsa_delete (received);
+      return -1;
+    }
+
+  /* if already have newer database copy, check next LSA */
+  have = ospf6_lsdb_lookup (lsa_header->type, lsa_header->ls_id,
+                            lsa_header->advrtr,
+                            ospf6_lsa_get_scope (lsa_header->type,
+                                                 from->ospf6_interface));
+  if (! have)
+    {
+      /* if we don't have database copy, add request */
+      if (IS_OSPF6_DUMP_DBEX)
+        zlog_info ("Have no database copy, Request");
+      ospf6_neighbor_request_add (received, from);
+    }
+  else if (have)
+    {
+      /* if database copy is less recent, add request */
+      if (ospf6_lsa_check_recent (received, have) < 0)
+        {
+          if (IS_OSPF6_DUMP_DBEX)
+            zlog_info ("Database copy less recent, Request");
+          ospf6_neighbor_request_add (received, from);
+        }
+    }
+
+  return 0;
+}
+
+/* Direct acknowledgement */
+static void
+ospf6_dbex_acknowledge_direct (struct ospf6_lsa *lsa,
+                               struct ospf6_neighbor *o6n)
+{
+  struct iovec directack[MAXIOVLIST];
+  assert (lsa);
+
+  if (IS_OSPF6_DUMP_DBEX)
+    zlog_info ("DBEX: [%s:%s] direct ack %s ",
+               o6n->str, o6n->ospf6_interface->interface->name,
+               lsa->str);
+
+  /* clear pointers to fragments of packet for direct acknowledgement */
+  iov_clear (directack, MAXIOVLIST);
+
+  /* set pointer of LSA to send */
+  OSPF6_MESSAGE_ATTACH (directack, lsa->header,
+                        sizeof (struct ospf6_lsa_header));
+
+  /* age update and add InfTransDelay */
+  ospf6_lsa_age_update_to_send (lsa, o6n->ospf6_interface->transdelay);
+
+  /* send unicast packet to neighbor's ipaddress */
+  ospf6_message_send (OSPF6_MESSAGE_TYPE_LSACK, directack, &o6n->hisaddr,
+                      o6n->ospf6_interface->if_id);
+}
+
+/* Delayed  acknowledgement */
+void
+ospf6_dbex_acknowledge_delayed (struct ospf6_lsa *lsa,
+                                struct ospf6_interface *o6i)
+{
+  assert (o6i);
+
+  if (IS_OSPF6_DUMP_DBEX)
+    zlog_info ("DBEX: [%s] delayed ack %s", o6i->interface->name, lsa->str);
+
+  /* attach delayed acknowledge list */
+  ospf6_lsa_age_current (lsa);
+  ospf6_interface_delayed_ack_add (lsa, o6i);
+
+  /* if not yet, schedule delayed acknowledge RxmtInterval later.
+     timers should be *less than* RxmtInterval
+     or needless retrans will ensue */
+  if (o6i->thread_send_lsack_delayed == NULL)
+    o6i->thread_send_lsack_delayed
+      = thread_add_timer (master, ospf6_send_lsack_delayed,
+                          o6i, o6i->rxmt_interval - 1);
+
+  return;
+}
+
+/* RFC2328 section 13 (4):
+   if MaxAge LSA and if we have no instance, and no neighbor
+   is in states Exchange or Loading */
+/* returns 1 if match this case, else returns 0 */
+static int
+ospf6_dbex_is_maxage_to_be_dropped (struct ospf6_lsa *received,
+                                    struct ospf6_neighbor *from)
+{
+  int count;
+
+  if (! IS_LSA_MAXAGE (received))
+    return 0;
+
+  if (ospf6_lsdb_lookup (received->header->type, received->header->id,
+                         received->header->adv_router,
+                         ospf6_lsa_get_scope (received->header->type,
+                                              from->ospf6_interface)))
+    return 0;
+
+  if (OSPF6_LSA_IS_SCOPE_LINKLOCAL (ntohs (received->header->type)))
+    {
+      count = 0;
+      (*from->ospf6_interface->foreach_nei)
+        (from->ospf6_interface, &count, NBS_EXCHANGE, ospf6_count_state);
+      (*from->ospf6_interface->foreach_nei)
+        (from->ospf6_interface, &count, NBS_LOADING, ospf6_count_state);
+      if (count)
+        return 0;
+    }
+  else if (OSPF6_LSA_IS_SCOPE_AREA (ntohs (received->header->type)))
+    {
+      count = 0;
+      (*from->ospf6_interface->area->foreach_nei)
+         (from->ospf6_interface->area, &count, NBS_EXCHANGE, ospf6_count_state);
+      (*from->ospf6_interface->area->foreach_nei)
+         (from->ospf6_interface->area, &count, NBS_LOADING, ospf6_count_state);
+      if (count)
+        return 0;
+    }
+  else if (OSPF6_LSA_IS_SCOPE_AS (ntohs (received->header->type)))
+    {
+      count = 0;
+      (*from->ospf6_interface->area->ospf6->foreach_nei)
+         (from->ospf6_interface->area->ospf6, &count, NBS_EXCHANGE,
+          ospf6_count_state);
+      (*from->ospf6_interface->area->ospf6->foreach_nei)
+         (from->ospf6_interface->area->ospf6, &count, NBS_LOADING,
+          ospf6_count_state);
+      if (count)
+        return 0;
+    }
+
+  return 1;
+}
+
+static void
+ospf6_dbex_remove_retrans (void *arg, int val, void *obj)
+{
+  struct ospf6_lsa *rem;
+  struct ospf6_neighbor *nei = (struct ospf6_neighbor *) obj;
+  struct ospf6_lsa *lsa = (struct ospf6_lsa *) arg;
+
+  rem = ospf6_lsdb_lookup_lsdb (lsa->header->type, lsa->header->id,
+                                lsa->header->adv_router, nei->retrans_list);
+  if (rem)
+    {
+      ospf6_neighbor_retrans_remove (rem, nei);
+      ospf6_maxage_remover ();
+    }
+}
+
+void
+ospf6_dbex_remove_from_all_retrans_list (struct ospf6_lsa *lsa)
+{
+  struct ospf6_interface *o6i;
+  struct ospf6_area *o6a;
+
+  if (OSPF6_LSA_IS_SCOPE_LINKLOCAL (htons (lsa->header->type)))
+    {
+      o6i = lsa->scope;
+      (*o6i->foreach_nei) (o6i, lsa, 0, ospf6_dbex_remove_retrans);
+    }
+  else if (OSPF6_LSA_IS_SCOPE_AREA (htons (lsa->header->type)))
+    {
+      o6a = lsa->scope;
+      (*o6a->foreach_nei) (o6a, lsa, 0, ospf6_dbex_remove_retrans);
+    }
+  else if (OSPF6_LSA_IS_SCOPE_AS (htons (lsa->header->type)))
+    {
+      (*ospf6->foreach_nei) (ospf6, lsa, 0, ospf6_dbex_remove_retrans);
+    }
+}
+
+/* RFC2328 section 13 */
+void
+ospf6_dbex_receive_lsa (struct ospf6_lsa_header *lsa_header,
+                        struct ospf6_neighbor *from)
+{
+  struct ospf6_lsa *received, *have, *rem;
+  struct timeval now;
+  int ismore_recent, acktype;
+  unsigned short cksum;
+  struct ospf6_lsa_slot *slot;
+
+  received = have = (struct ospf6_lsa *)NULL;
+  ismore_recent = -1;
+  recent_reason = "no instance";
+
+  zlog_info ("Receive LSA (header -> %p)", lsa_header);
+
+  /* make lsa structure for received lsa */
+  received = ospf6_lsa_create (lsa_header);
+
+  /* set LSA scope */
+  if (OSPF6_LSA_IS_SCOPE_LINKLOCAL (htons (lsa_header->type)))
+    received->scope = from->ospf6_interface;
+  else if (OSPF6_LSA_IS_SCOPE_AREA (htons (lsa_header->type)))
+    received->scope = from->ospf6_interface->area;
+  else if (OSPF6_LSA_IS_SCOPE_AS (htons (lsa_header->type)))
+    received->scope = from->ospf6_interface->area->ospf6;
+
+  /* (1) LSA Checksum */
+  cksum = ntohs (lsa_header->checksum);
+  if (ntohs (ospf6_lsa_checksum (lsa_header)) != cksum)
+    {
+      if (IS_OSPF6_DUMP_DBEX)
+        zlog_info ("DBEX: received %s from %s%%%s"
+                   ": wrong checksum, drop",
+                   received->str, from->str,
+                   from->ospf6_interface->interface->name);
+      ospf6_lsa_delete (received);
+      return;
+    }
+
+  /* (3) Ebit Missmatch: AS-External-LSA */
+  if (lsa_header->type == htons (OSPF6_LSA_TYPE_AS_EXTERNAL) &&
+      ospf6_area_is_stub (from->ospf6_interface->area))
+    {
+      if (IS_OSPF6_DUMP_DBEX)
+        zlog_info ("DBEX: received %s from %s%%%s"
+                   ": E-bit mismatch, drop",
+                   received->str, from->str,
+                   from->ospf6_interface->interface->name);
+      ospf6_lsa_delete (received);
+      return;
+    }
+
+  /* (4) if MaxAge LSA and if we have no instance, and no neighbor
+         is in states Exchange or Loading */
+  if (ospf6_dbex_is_maxage_to_be_dropped (received, from))
+    {
+      /* log */
+      if (IS_OSPF6_DUMP_DBEX)
+        zlog_info ("DBEX: received %s from %s%%%s"
+                   ": MaxAge, no instance, no neighbor exchange, drop",
+                   received->str, from->str,
+                   from->ospf6_interface->interface->name);
+
+      /* a) Acknowledge back to neighbor (13.5) */
+        /* Direct Acknowledgement */
+      ospf6_dbex_acknowledge_direct (received, from);
+
+      /* b) Discard */
+      ospf6_lsa_delete (received);
+      return;
+    }
+
+  /* (5) */
+  /* lookup the same database copy in lsdb */
+  have = ospf6_lsdb_lookup (lsa_header->type, lsa_header->ls_id,
+                            lsa_header->advrtr,
+                            ospf6_lsa_get_scope (lsa_header->type,
+                                                 from->ospf6_interface));
+  if (have)
+    {
+      ismore_recent = ospf6_lsa_check_recent (received, have);
+      if (ntohl (received->header->seqnum) == ntohl (have->header->seqnum))
+        SET_FLAG (received->flag, OSPF6_LSA_FLAG_DUPLICATE);
+    }
+
+  /* if no database copy or received is more recent */
+  if (!have || ismore_recent < 0)
+    {
+      /* in case we have no database copy */
+      ismore_recent = -1;
+
+      /* (a) MinLSArrival check */
+      gettimeofday (&now, (struct timezone *)NULL);
+      if (have && SEC_TVDIFF (&now, &have->installed) < OSPF6_MIN_LS_ARRIVAL)
+        {
+          //if (IS_OSPF6_DUMP_DBEX)
+            zlog_info ("DBEX: Receive new LSA from %s: %s seq: %#x age: %d "
+                       "within MinLSArrival, drop: %ld.%06ld",
+                       from->str, received->str,
+                       ntohl (received->header->seqnum),
+                       ntohs (received->header->age),
+                       now.tv_sec, now.tv_usec);
+
+          /* this will do free this lsa */
+          ospf6_lsa_delete (received);
+          return;   /* examin next lsa */
+        }
+
+      //if (IS_OSPF6_DUMP_DBEX)
+        zlog_info ("DBEX: Receive new LSA from %s: %s seq: %#x age: %d: "
+                   "%ld.%06ld",
+                   from->str, received->str,
+                   ntohl (received->header->seqnum),
+                   ntohs (received->header->age),
+                   now.tv_sec, now.tv_usec);
+
+      /* (b) immediately flood */
+      ospf6_dbex_flood (received, from);
+
+#if 0
+      /* Because New LSDB do not permit two LSA having the same identifier
+         exist in a LSDB list, above ospf6_dbex_flood() will remove
+         the old instance automatically. thus bellow is not needed. */
+      /* (c) remove database copy from all neighbor's retranslist */
+      if (have)
+        ospf6_dbex_remove_from_all_retrans_list (have);
+#endif
+
+      /* (d), installing lsdb, which may cause routing
+              table calculation (replacing database copy) */
+      ospf6_lsdb_install (received);
+
+      /* (e) possibly acknowledge */
+      acktype = ack_type (received, ismore_recent, from);
+      if (acktype == DIRECT_ACK)
+        {
+          if (IS_OSPF6_DUMP_DBEX)
+            zlog_info ("DBEX: Direct Ack to %s", from->str);
+          ospf6_dbex_acknowledge_direct (received, from);
+        }
+      else if (acktype == DELAYED_ACK)
+        {
+          if (IS_OSPF6_DUMP_DBEX)
+            zlog_info ("DBEX: Delayed Ack to %s", from->str);
+          ospf6_dbex_acknowledge_delayed (received, from->ospf6_interface);
+        }
+      else
+        {
+          if (IS_OSPF6_DUMP_DBEX)
+            zlog_info ("DBEX: No Ack to %s", from->str);
+        }
+
+      /* (f) */
+      /* Self Originated LSA, section 13.4 */
+      if (received->lsa_hdr->lsh_advrtr == ospf6->router_id
+          && (! have || ismore_recent < 0))
+        {
+          /* we're going to make new lsa or to flush this LSA. */
+          if (IS_OSPF6_DUMP_DBEX)
+            zlog_info ("DBEX: Self-originated LSA %s from %s:%s",
+                       received->str, from->str,
+                       from->ospf6_interface->interface->name);
+          if (IS_OSPF6_DUMP_DBEX)
+            zlog_info ("DBEX: %s: Make new one/Flush", received->str);
+
+          SET_FLAG (received->flag, OSPF6_LSA_FLAG_REFRESH);
+          slot = ospf6_lsa_slot_get (received->header->type);
+          if (slot && slot->func_refresh)
+            {
+              (*slot->func_refresh) (received);
+              return;
+            }
+
+          zlog_warn ("Can't Refresh LSA: Unknown type: %#x, Flush",
+                     ntohs (received->header->type));
+          ospf6_lsa_premature_aging (received);
+          return;
+        }
+    }
+  else if (ospf6_lsdb_lookup_lsdb (received->header->type,
+                                   received->header->id,
+                                   received->header->adv_router,
+                                   from->request_list))
+    /* (6) if there is instance on sending neighbor's request list */
+    {
+      /* if no database copy, should go above state (5) */
+      assert (have);
+
+      zlog_warn ("DBEX: [%s:%s] received LSA %s is not newer,"
+                 " and is on his requestlist: Generate BadLSReq",
+                 from->str, from->ospf6_interface->interface->name,
+                 received->str);
+
+      /* BadLSReq */
+      thread_add_event (master, bad_lsreq, from, 0);
+
+      ospf6_lsa_delete (received);
+    }
+  else if (ismore_recent == 0) /* (7) if neither is more recent */
+    {
+      /* (a) if on retranslist, Treat this LSA as an Ack: Implied Ack */
+      rem = ospf6_lsdb_lookup_lsdb (received->header->type,
+                                    received->header->id,
+                                    received->header->adv_router,
+                                    from->retrans_list);
+      if (rem)
+        {
+          if (IS_OSPF6_DUMP_DBEX)
+            zlog_info ("DBEX: Implied Ack from %s, (remove retrans)",
+                       from->str);
+          SET_FLAG (received->flag, OSPF6_LSA_FLAG_IMPLIEDACK);
+          ospf6_neighbor_retrans_remove (rem, from);
+        }
+
+      /* (b) possibly acknowledge */
+      acktype = ack_type (received, ismore_recent, from);
+      if (acktype == DIRECT_ACK)
+        {
+          if (IS_OSPF6_DUMP_DBEX)
+            zlog_info ("DBEX: Direct Ack to %s", from->str);
+          ospf6_dbex_acknowledge_direct (received, from);
+        }
+      else if (acktype == DELAYED_ACK)
+        {
+          if (IS_OSPF6_DUMP_DBEX)
+            zlog_info ("DBEX: Delayed Ack to %s", from->str);
+          ospf6_dbex_acknowledge_delayed (received, from->ospf6_interface);
+        }
+      else
+        {
+          if (IS_OSPF6_DUMP_DBEX)
+            zlog_info ("DBEX: No Ack to %s", from->str);
+        }
+      ospf6_lsa_delete (received);
+    }
+  else /* (8) previous database copy is more recent */
+    {
+      /* If Seqnumber Wrapping, simply discard
+         Otherwise, Send database copy of this LSA to this neighbor */
+      if (! IS_LSA_MAXAGE (received) ||
+          received->lsa_hdr->lsh_seqnum != MAX_SEQUENCE_NUMBER)
+        {
+          if (IS_OSPF6_DUMP_DBEX)
+            zlog_info ("DBEX: database is more recent: send back to %s",
+                       from->str);
+          ospf6_send_lsupdate_direct (have, from);
+        }
+      ospf6_lsa_delete (received);
+    }
+}
+
+/* RFC2328: Table 19: Sending link state acknowledgements. */
+int 
+ack_type (struct ospf6_lsa *newp, int ismore_recent,
+          struct ospf6_neighbor *from)
+{
+  struct ospf6_interface *ospf6_interface;
+  struct ospf6_lsa *have;
+  int count;
+
+  assert (from && from->ospf6_interface);
+  ospf6_interface = from->ospf6_interface;
+
+  if (CHECK_FLAG (newp->flag, OSPF6_LSA_FLAG_FLOODBACK))
+    return NO_ACK;
+
+  if (ismore_recent < 0)
+    {
+      if (ospf6_interface->state != IFS_BDR)
+        return DELAYED_ACK;
+
+      if (ospf6_interface->dr == from->router_id)
+        return DELAYED_ACK;
+      return NO_ACK;
+    }
+
+  if (CHECK_FLAG (newp->flag, OSPF6_LSA_FLAG_DUPLICATE) &&
+      CHECK_FLAG (newp->flag, OSPF6_LSA_FLAG_IMPLIEDACK))
+    {
+      if (ospf6_interface->state != IFS_BDR)
+        return NO_ACK;
+
+      if (ospf6_interface->dr == from->router_id)
+        return DELAYED_ACK;
+
+      return NO_ACK;
+    }
+
+  if (CHECK_FLAG (newp->flag, OSPF6_LSA_FLAG_DUPLICATE) &&
+      ! CHECK_FLAG (newp->flag, OSPF6_LSA_FLAG_IMPLIEDACK))
+    return DIRECT_ACK;
+
+  have = ospf6_lsdb_lookup (newp->header->type, newp->header->id,
+                            newp->header->adv_router,
+                            ospf6_lsa_get_scope (newp->header->type,
+                                                 from->ospf6_interface));
+
+  count = 0;
+  ospf6->foreach_nei (ospf6, &count, NBS_EXCHANGE, ospf6_count_state);
+  ospf6->foreach_nei (ospf6, &count, NBS_LOADING, ospf6_count_state);
+
+  if (IS_LSA_MAXAGE (newp) && have == NULL && count == 0)
+    return DIRECT_ACK;
+ 
+  return NO_ACK;
+}
+
+static void
+ospf6_dbex_flood_linklocal (struct ospf6_lsa *lsa, struct ospf6_interface *o6i,
+                            struct ospf6_neighbor *from)
+{
+  struct ospf6_neighbor *o6n = (struct ospf6_neighbor *) NULL;
+  int ismore_recent, addretrans = 0;
+  listnode n;
+  struct ospf6_lsa *req;
+
+  /* (1) for each neighbor */
+  for (n = listhead (o6i->neighbor_list); n; nextnode (n))
+    {
+      o6n = (struct ospf6_neighbor *) getdata (n);
+
+      /* (a) */
+      if (o6n->state < NBS_EXCHANGE)
+        continue;  /* examin next neighbor */
+
+      /* (b) */
+      if (o6n->state == NBS_EXCHANGE
+          || o6n->state == NBS_LOADING)
+        {
+          req = ospf6_lsdb_lookup_lsdb (lsa->header->type,
+                                        lsa->header->id,
+                                        lsa->header->adv_router,
+                                        o6n->request_list);
+          if (req)
+            {
+              ismore_recent = ospf6_lsa_check_recent (lsa, req);
+              if (ismore_recent > 0)
+                {
+                  continue; /* examin next neighbor */
+                }
+              else if (ismore_recent == 0)
+                {
+                  ospf6_neighbor_request_remove (req, o6n);
+                  continue; /* examin next neighbor */
+                }
+              else /* ismore_recent < 0 (the new LSA is more recent) */
+                {
+                  ospf6_neighbor_request_remove (req, o6n);
+                }
+            }
+        }
+
+      /* (c) */
+      if (from && from->router_id == o6n->router_id)
+        continue; /* examin next neighbor */
+
+      /* (d) add retranslist */
+      if (IS_OSPF6_DUMP_DBEX)
+        zlog_info ("DBEX: schedule flooding [%s:%s]: %s",
+                   o6n->str, o6n->ospf6_interface->interface->name,
+                   lsa->str);
+      ospf6_neighbor_retrans_add (lsa, o6n);
+      addretrans++;
+      if (o6n->send_update == (struct thread *) NULL)
+        o6n->send_update =
+          thread_add_timer (master, ospf6_send_lsupdate_rxmt, o6n,
+                            o6n->ospf6_interface->rxmt_interval);
+    }
+
+  /* (2) */
+  if (addretrans == 0)
+    return; /* examin next interface */
+
+  if (from && from->ospf6_interface == o6i)
+    {
+      if (IS_OSPF6_DUMP_DBEX)
+        zlog_info ("DBEX: flood back %s to %s",
+                   lsa->str, o6i->interface->name);
+      /* note occurence of floodback */
+      SET_FLAG (lsa->flag, OSPF6_LSA_FLAG_FLOODBACK);
+    }
+
+  /* (3) */
+  if (from && from->ospf6_interface == o6i)
+    {
+      /* if from DR or BDR, don't need to flood this interface */
+      if (from->router_id == from->ospf6_interface->dr ||
+          from->router_id == from->ospf6_interface->bdr)
+        return; /* examin next interface */
+    }
+
+  /* (4) if I'm BDR, DR will flood this interface */
+  if (from && from->ospf6_interface == o6i
+      && o6i->state == IFS_BDR)
+    return; /* examin next interface */
+
+  if (IS_OSPF6_DUMP_DBEX)
+    zlog_info ("Flood to interface %s", o6i->interface->name);
+
+  /* (5) send LinkState Update */
+  ospf6_send_lsupdate_flood (lsa, o6i);
+
+  return;
+}
+
+/* RFC2328 section 13.3 */
+static void
+ospf6_dbex_flood_area (struct ospf6_lsa *lsa, struct ospf6_area *area,
+                       struct ospf6_neighbor *from)
+{
+  listnode n;
+  struct ospf6_interface *ospf6_interface;
+
+  assert (lsa && lsa->lsa_hdr && area);
+
+  /* for each eligible ospf_ifs */
+  for (n = listhead (area->if_list); n; nextnode (n))
+    {
+      ospf6_interface = (struct ospf6_interface *) getdata (n);
+      ospf6_dbex_flood_linklocal (lsa, ospf6_interface, from);
+    }
+}
+
+static void
+ospf6_dbex_flood_as (struct ospf6_lsa *lsa, struct ospf6 *ospf6,
+                     struct ospf6_neighbor *from)
+{
+  listnode n;
+  struct ospf6_area *o6a;
+
+  assert (lsa && lsa->lsa_hdr && ospf6);
+
+  /* for each attached area */
+  for (n = listhead (ospf6->area_list); n; nextnode (n))
+    {
+      o6a = (struct ospf6_area *) getdata (n);
+      ospf6_dbex_flood_area (lsa, o6a, from);
+    }
+}
+
+/* flood ospf6_lsa within appropriate scope */
+void
+ospf6_dbex_flood (struct ospf6_lsa *lsa, struct ospf6_neighbor *from)
+{
+  struct ospf6_area *o6a;
+  struct ospf6_interface *o6i;
+  struct ospf6 *o6;
+  struct ospf6_lsa_header *lsa_header;
+
+  lsa_header = (struct ospf6_lsa_header *) lsa->lsa_hdr;
+
+  if (OSPF6_LSA_IS_SCOPE_LINKLOCAL (ntohs (lsa_header->type)))
+    {
+      o6i = (struct ospf6_interface *) lsa->scope;
+      assert (o6i);
+
+      if (IS_OSPF6_DUMP_DBEX)
+        zlog_info ("Flood Linklocal: %s", o6i->interface->name);
+      ospf6_dbex_flood_linklocal (lsa, o6i, from);
+    }
+  else if (OSPF6_LSA_IS_SCOPE_AREA (ntohs (lsa_header->type)))
+    {
+      o6a = (struct ospf6_area *) lsa->scope;
+      assert (o6a);
+
+      if (IS_OSPF6_DUMP_DBEX)
+        zlog_info ("Flood Area: %s", o6a->str);
+      ospf6_dbex_flood_area (lsa, o6a, from);
+    }
+  else if (OSPF6_LSA_IS_SCOPE_AS (ntohs (lsa_header->type)))
+    {
+      o6 = (struct ospf6 *) lsa->scope;
+      assert (o6);
+
+      if (IS_OSPF6_DUMP_DBEX)
+        zlog_info ("Flood AS");
+      ospf6_dbex_flood_as (lsa, o6, from);
+    }
+  else
+    {
+      zlog_warn ("Can't Flood %s: scope unknown", lsa->str);
+    }
+}
+
+