]> git.ipfire.org Git - thirdparty/bird.git/blobdiff - proto/ospf/neighbor.c
OSPF: Fix reading from freed memory
[thirdparty/bird.git] / proto / ospf / neighbor.c
index 2563abd480ebd1292d88bee0e7e8e61e4179d43a..0223ccdf211b3f2b39fb1bd7352fc2bbf7f1a96c 100644 (file)
@@ -1,67 +1,84 @@
 /*
  *     BIRD -- OSPF
  *
- *     (c) 1999 - 2004 Ondrej Filip <feela@network.cz>
+ *     (c) 1999--2004 Ondrej Filip <feela@network.cz>
+ *     (c) 2009--2014 Ondrej Zajicek <santiago@crfreenet.org>
+ *     (c) 2009--2014 CZ.NIC z.s.p.o.
  *
  *     Can be freely distributed and used under the terms of the GNU GPL.
  */
 
 #include "ospf.h"
 
-char *ospf_ns[] = { "    down",
-  " attempt",
-  "    init",
-  "    2way",
-  " exstart",
-  "exchange",
-  " loading",
-  "    full"
+
+const char *ospf_ns_names[] = {
+  "Down", "Attempt", "Init", "2-Way", "ExStart", "Exchange", "Loading", "Full"
 };
 
-const char *ospf_inm[] =
-  { "hello received", "neighbor start", "2-way received",
-  "negotiation done", "exstart done", "bad ls request", "load done",
-  "adjacency ok?", "sequence mismatch", "1-way received", "kill neighbor",
-  "inactivity timer", "line down"
+const char *ospf_inm_names[] = {
+  "HelloReceived", "Start", "2-WayReceived", "NegotiationDone", "ExchangeDone",
+  "BadLSReq", "LoadingDone", "AdjOK?", "SeqNumberMismatch", "1-WayReceived",
+  "KillNbr", "InactivityTimer", "LLDown"
 };
 
-static void neigh_chstate(struct ospf_neighbor *n, u8 state);
-static struct ospf_neighbor *electbdr(list nl);
-static struct ospf_neighbor *electdr(list nl);
-static void neighbor_timer_hook(timer * timer);
-static void rxmt_timer_hook(timer * timer);
-static void ackd_timer_hook(timer * t);
+
+static int can_do_adj(struct ospf_neighbor *n);
+static void inactivity_timer_hook(timer * timer);
+static void dbdes_timer_hook(timer *t);
+static void lsrq_timer_hook(timer *t);
+static void lsrt_timer_hook(timer *t);
+static void ackd_timer_hook(timer *t);
+
 
 static void
-init_lists(struct ospf_neighbor *n)
+init_lists(struct ospf_proto *p, struct ospf_neighbor *n)
 {
   s_init_list(&(n->lsrql));
-  n->lsrqh = ospf_top_new(n->pool);
-  s_init(&(n->lsrqi), &(n->lsrql));
+  n->lsrqi = SHEAD(n->lsrql);
+  n->lsrqh = ospf_top_new(p, n->pool);
 
   s_init_list(&(n->lsrtl));
-  n->lsrth = ospf_top_new(n->pool);
-  s_init(&(n->lsrti), &(n->lsrtl));
+  n->lsrth = ospf_top_new(p, n->pool);
+}
+
+static void
+release_lsrtl(struct ospf_proto *p, struct ospf_neighbor *n)
+{
+  struct top_hash_entry *ret, *en;
+
+  WALK_SLIST(ret, n->lsrtl)
+  {
+    en = ospf_hash_find_entry(p->gr, ret);
+    if (en)
+      en->ret_count--;
+  }
 }
 
 /* Resets LSA request and retransmit lists.
- * We do not reset DB summary list iterator here, 
+ * We do not reset DB summary list iterator here,
  * it is reset during entering EXCHANGE state.
  */
 static void
-reset_lists(struct ospf_neighbor *n)
+reset_lists(struct ospf_proto *p, struct ospf_neighbor *n)
 {
+  release_lsrtl(p, n);
   ospf_top_free(n->lsrqh);
   ospf_top_free(n->lsrth);
-  init_lists(n);
+  ospf_reset_lsack_queue(n);
+
+  tm_stop(n->dbdes_timer);
+  tm_stop(n->lsrq_timer);
+  tm_stop(n->lsrt_timer);
+  tm_stop(n->ackd_timer);
+
+  init_lists(p, n);
 }
 
 struct ospf_neighbor *
 ospf_neighbor_new(struct ospf_iface *ifa)
 {
-  struct proto *p = (struct proto *) (ifa->oa->po);
-  struct proto_ospf *po = ifa->oa->po;
-  struct pool *pool = rp_new(p->pool, "OSPF Neighbor");
+  struct ospf_proto *p = ifa->oa->po;
+  struct pool *pool = rp_new(p->p.pool, "OSPF Neighbor");
   struct ospf_neighbor *n = mb_allocz(pool, sizeof(struct ospf_neighbor));
 
   n->pool = pool;
@@ -69,106 +86,284 @@ ospf_neighbor_new(struct ospf_iface *ifa)
   add_tail(&ifa->neigh_list, NODE n);
   n->adj = 0;
   n->csn = 0;
-  n->ldbdes = mb_allocz(pool, ifa->iface->mtu);
   n->state = NEIGHBOR_DOWN;
 
-  init_lists(n);
-  s_init(&(n->dbsi), &(po->lsal));
-
-  n->inactim = tm_new(pool);
-  n->inactim->data = n;
-  n->inactim->randomize = 0;
-  n->inactim->hook = neighbor_timer_hook;
-  n->inactim->recurrent = 0;
-  DBG("%s: Installing inactivity timer.\n", p->name);
-
-  n->rxmt_timer = tm_new(pool);
-  n->rxmt_timer->data = n;
-  n->rxmt_timer->randomize = 0;
-  n->rxmt_timer->hook = rxmt_timer_hook;
-  n->rxmt_timer->recurrent = ifa->rxmtint;
-  tm_start(n->rxmt_timer, n->ifa->rxmtint);
-  DBG("%s: Installing rxmt timer.\n", p->name);
-
-  n->ackd_timer = tm_new(pool);
-  n->ackd_timer->data = n;
-  n->ackd_timer->randomize = 0;
-  n->ackd_timer->hook = ackd_timer_hook;
-  n->ackd_timer->recurrent = ifa->rxmtint / 2;
+  init_lists(p, n);
+  s_init(&(n->dbsi), &(p->lsal));
+
   init_list(&n->ackl[ACKL_DIRECT]);
   init_list(&n->ackl[ACKL_DELAY]);
-  tm_start(n->ackd_timer, n->ifa->rxmtint / 2);
-  DBG("%s: Installing ackd timer.\n", p->name);
+
+  n->inactim = tm_new_set(pool, inactivity_timer_hook, n, 0, 0);
+  n->dbdes_timer = tm_new_set(pool, dbdes_timer_hook, n, 0, ifa->rxmtint);
+  n->lsrq_timer = tm_new_set(pool, lsrq_timer_hook, n, 0, ifa->rxmtint);
+  n->lsrt_timer = tm_new_set(pool, lsrt_timer_hook, n, 0, ifa->rxmtint);
+  n->ackd_timer = tm_new_set(pool, ackd_timer_hook, n, 0, ifa->rxmtint / 2);
 
   return (n);
 }
 
+static void
+ospf_neigh_down(struct ospf_neighbor *n)
+{
+  struct ospf_iface *ifa = n->ifa;
+  struct ospf_proto *p = ifa->oa->po;
+  u32 rid = n->rid;
+
+  if ((ifa->type == OSPF_IT_NBMA) || (ifa->type == OSPF_IT_PTMP))
+  {
+    struct nbma_node *nn = find_nbma_node(ifa, n->ip);
+    if (nn)
+      nn->found = 0;
+  }
+
+  s_get(&(n->dbsi));
+  release_lsrtl(p, n);
+  rem_node(NODE n);
+  rfree(n->pool);
+
+  OSPF_TRACE(D_EVENTS, "Neighbor %R on %s removed", rid, ifa->ifname);
+}
+
 /**
- * neigh_chstate - handles changes related to new or lod state of neighbor
+ * ospf_neigh_chstate - handles changes related to new or lod state of neighbor
  * @n: OSPF neighbor
  * @state: new state
  *
  * Many actions have to be taken acording to a change of state of a neighbor. It
  * starts rxmt timers, call interface state machine etc.
  */
-
 static void
-neigh_chstate(struct ospf_neighbor *n, u8 state)
+ospf_neigh_chstate(struct ospf_neighbor *n, u8 state)
+{
+  struct ospf_iface *ifa = n->ifa;
+  struct ospf_proto *p = ifa->oa->po;
+  u8 old_state = n->state;
+  int old_fadj = ifa->fadj;
+
+  if (state == old_state)
+    return;
+
+  OSPF_TRACE(D_EVENTS, "Neighbor %R on %s changed state from %s to %s",
+            n->rid, ifa->ifname, ospf_ns_names[old_state], ospf_ns_names[state]);
+
+  n->state = state;
+
+  /* Increase number of partial adjacencies */
+  if ((state == NEIGHBOR_EXCHANGE) || (state == NEIGHBOR_LOADING))
+    p->padj++;
+
+  /* Decrease number of partial adjacencies */
+  if ((old_state == NEIGHBOR_EXCHANGE) || (old_state == NEIGHBOR_LOADING))
+    p->padj--;
+
+  /* Increase number of full adjacencies */
+  if (state == NEIGHBOR_FULL)
+    ifa->fadj++;
+
+  /* Decrease number of full adjacencies */
+  if (old_state == NEIGHBOR_FULL)
+    ifa->fadj--;
+
+  if (ifa->fadj != old_fadj)
+  {
+    /* RFC 2328 12.4 Event 4 - neighbor enters/leaves Full state */
+    ospf_notify_rt_lsa(ifa->oa);
+    ospf_notify_net_lsa(ifa);
+
+    /* RFC 2328 12.4 Event 8 - vlink state change */
+    if (ifa->type == OSPF_IT_VLINK)
+      ospf_notify_rt_lsa(ifa->voa);
+  }
+
+  if (state == NEIGHBOR_EXSTART)
+  {
+    /* First time adjacency */
+    if (n->adj == 0)
+      n->dds = random_u32();
+
+    n->dds++;
+    n->myimms = DBDES_IMMS;
+
+    tm_start(n->dbdes_timer, 0);
+    tm_start(n->ackd_timer, ifa->rxmtint / 2);
+  }
+
+  if (state > NEIGHBOR_EXSTART)
+    n->myimms &= ~DBDES_I;
+
+  /* Generate NeighborChange event if needed, see RFC 2328 9.2 */
+  if ((state == NEIGHBOR_2WAY) && (old_state < NEIGHBOR_2WAY))
+    ospf_iface_sm(ifa, ISM_NEICH);
+  if ((state < NEIGHBOR_2WAY) && (old_state >= NEIGHBOR_2WAY))
+    ospf_iface_sm(ifa, ISM_NEICH);
+}
+
+/**
+ * ospf_neigh_sm - ospf neighbor state machine
+ * @n: neighor
+ * @event: actual event
+ *
+ * This part implements the neighbor state machine as described in 10.3 of
+ * RFC 2328. The only difference is that state %NEIGHBOR_ATTEMPT is not
+ * used. We discover neighbors on nonbroadcast networks in the
+ * same way as on broadcast networks. The only difference is in
+ * sending hello packets. These are sent to IPs listed in
+ * @ospf_iface->nbma_list .
+ */
+void
+ospf_neigh_sm(struct ospf_neighbor *n, int event)
 {
-  u8 oldstate;
+  struct ospf_proto *p = n->ifa->oa->po;
 
-  oldstate = n->state;
+  DBG("Neighbor state machine for %R on %s, event %s\n",
+      n->rid, n->ifa->ifname, ospf_inm_names[event]);
 
-  if (oldstate != state)
+  switch (event)
   {
-    struct ospf_iface *ifa = n->ifa;
-    struct proto_ospf *po = ifa->oa->po;
-    struct proto *p = &po->proto;
+  case INM_START:
+    ospf_neigh_chstate(n, NEIGHBOR_ATTEMPT);
+    /* NBMA are used different way */
+    break;
+
+  case INM_HELLOREC:
+    if (n->state < NEIGHBOR_INIT)
+      ospf_neigh_chstate(n, NEIGHBOR_INIT);
+
+    /* Restart inactivity timer */
+    tm_start(n->inactim, n->ifa->deadint);
+    break;
+
+  case INM_2WAYREC:
+    if (n->state < NEIGHBOR_2WAY)
+      ospf_neigh_chstate(n, NEIGHBOR_2WAY);
+    if ((n->state == NEIGHBOR_2WAY) && can_do_adj(n))
+      ospf_neigh_chstate(n, NEIGHBOR_EXSTART);
+    break;
+
+  case INM_NEGDONE:
+    if (n->state == NEIGHBOR_EXSTART)
+    {
+      ospf_neigh_chstate(n, NEIGHBOR_EXCHANGE);
 
-    n->state = state;
+      /* Reset DB summary list iterator */
+      s_get(&(n->dbsi));
+      s_init(&(n->dbsi), &p->lsal);
 
-    OSPF_TRACE(D_EVENTS, "Neighbor %I changes state from \"%s\" to \"%s\".",
-              n->ip, ospf_ns[oldstate], ospf_ns[state]);
+      /* Add MaxAge LSA entries to retransmission list */
+      ospf_add_flushed_to_lsrt(p, n);
+    }
+    else
+      bug("NEGDONE and I'm not in EXSTART?");
+    break;
+
+  case INM_EXDONE:
+    if (!EMPTY_SLIST(n->lsrql))
+      ospf_neigh_chstate(n, NEIGHBOR_LOADING);
+    else
+      ospf_neigh_chstate(n, NEIGHBOR_FULL);
+    break;
 
-    if ((state == NEIGHBOR_2WAY) && (oldstate < NEIGHBOR_2WAY))
-      ospf_iface_sm(ifa, ISM_NEICH);
-    if ((state < NEIGHBOR_2WAY) && (oldstate >= NEIGHBOR_2WAY))
-      ospf_iface_sm(ifa, ISM_NEICH);
+  case INM_LOADDONE:
+    ospf_neigh_chstate(n, NEIGHBOR_FULL);
+    break;
 
-    if (oldstate == NEIGHBOR_FULL)     /* Decrease number of adjacencies */
+  case INM_ADJOK:
+    /* Can In build adjacency? */
+    if ((n->state == NEIGHBOR_2WAY) && can_do_adj(n))
     {
-      ifa->fadj--;
-      schedule_rt_lsa(ifa->oa);
-      if (ifa->type == OSPF_IT_VLINK) schedule_rt_lsa(ifa->voa);
-      schedule_net_lsa(ifa);
+      ospf_neigh_chstate(n, NEIGHBOR_EXSTART);
     }
+    else if ((n->state >= NEIGHBOR_EXSTART) && !can_do_adj(n))
+    {
+      reset_lists(p, n);
+      ospf_neigh_chstate(n, NEIGHBOR_2WAY);
+    }
+    break;
 
-    if (state == NEIGHBOR_FULL)        /* Increase number of adjacencies */
+  case INM_SEQMIS:
+  case INM_BADLSREQ:
+    if (n->state >= NEIGHBOR_EXCHANGE)
     {
-      ifa->fadj++;
-      schedule_rt_lsa(ifa->oa);
-      if (ifa->type == OSPF_IT_VLINK) schedule_rt_lsa(ifa->voa);
-      schedule_net_lsa(ifa);
+      reset_lists(p, n);
+      ospf_neigh_chstate(n, NEIGHBOR_EXSTART);
     }
-    if (state == NEIGHBOR_EXSTART)
+    break;
+
+  case INM_KILLNBR:
+  case INM_LLDOWN:
+  case INM_INACTTIM:
+    /* No need for reset_lists() */
+    ospf_neigh_chstate(n, NEIGHBOR_DOWN);
+    ospf_neigh_down(n);
+    break;
+
+  case INM_1WAYREC:
+    reset_lists(p, n);
+    ospf_neigh_chstate(n, NEIGHBOR_INIT);
+    break;
+
+  default:
+    bug("%s: INM - Unknown event?", p->p.name);
+    break;
+  }
+}
+
+static int
+can_do_adj(struct ospf_neighbor *n)
+{
+  struct ospf_iface *ifa = n->ifa;
+  struct ospf_proto *p = ifa->oa->po;
+  int i = 0;
+
+  switch (ifa->type)
+  {
+  case OSPF_IT_PTP:
+  case OSPF_IT_PTMP:
+  case OSPF_IT_VLINK:
+    i = 1;
+    break;
+  case OSPF_IT_BCAST:
+  case OSPF_IT_NBMA:
+    switch (ifa->state)
     {
-      if (n->adj == 0)         /* First time adjacency */
-      {
-       n->dds = random_u32();
-      }
-      n->dds++;
-      n->myimms.byte = 0;
-      n->myimms.bit.ms = 1;
-      n->myimms.bit.m = 1;
-      n->myimms.bit.i = 1;
+    case OSPF_IS_DOWN:
+    case OSPF_IS_LOOP:
+      bug("%s: Iface %s in down state?", p->p.name, ifa->ifname);
+      break;
+    case OSPF_IS_WAITING:
+      DBG("%s: Neighbor? on iface %s\n", p->p.name, ifa->ifname);
+      break;
+    case OSPF_IS_DROTHER:
+      if (((n->rid == ifa->drid) || (n->rid == ifa->bdrid))
+         && (n->state >= NEIGHBOR_2WAY))
+       i = 1;
+      break;
+    case OSPF_IS_PTP:
+    case OSPF_IS_BACKUP:
+    case OSPF_IS_DR:
+      if (n->state >= NEIGHBOR_2WAY)
+       i = 1;
+      break;
+    default:
+      bug("%s: Iface %s in unknown state?", p->p.name, ifa->ifname);
+      break;
     }
-    if (state > NEIGHBOR_EXSTART)
-      n->myimms.bit.i = 0;
+    break;
+  default:
+    bug("%s: Iface %s is unknown type?", p->p.name, ifa->ifname);
+    break;
   }
+  DBG("%s: Iface %s can_do_adj=%d\n", p->p.name, ifa->ifname, i);
+  return i;
 }
 
+
+static inline u32 neigh_get_id(struct ospf_proto *p, struct ospf_neighbor *n)
+{ return ospf_is_v2(p) ? ipa_to_u32(n->ip) : n->rid; }
+
 static struct ospf_neighbor *
-electbdr(list nl)
+elect_bdr(struct ospf_proto *p, list nl)
 {
   struct ospf_neighbor *neigh, *n1, *n2;
   u32 nid;
@@ -177,11 +372,7 @@ electbdr(list nl)
   n2 = NULL;
   WALK_LIST(neigh, nl)                 /* First try those decl. themselves */
   {
-#ifdef OSPFv2
-    nid = ipa_to_u32(neigh->ip);
-#else /* OSPFv3 */
-    nid = neigh->rid;
-#endif
+    nid = neigh_get_id(p, neigh);
 
     if (neigh->state >= NEIGHBOR_2WAY) /* Higher than 2WAY */
       if (neigh->priority > 0)         /* Eligible */
@@ -226,7 +417,7 @@ electbdr(list nl)
 }
 
 static struct ospf_neighbor *
-electdr(list nl)
+elect_dr(struct ospf_proto *p, list nl)
 {
   struct ospf_neighbor *neigh, *n;
   u32 nid;
@@ -234,11 +425,7 @@ electdr(list nl)
   n = NULL;
   WALK_LIST(neigh, nl)                 /* And now DR */
   {
-#ifdef OSPFv2
-    nid = ipa_to_u32(neigh->ip);
-#else /* OSPFv3 */
-    nid = neigh->rid;
-#endif
+    nid = neigh_get_id(p, neigh);
 
     if (neigh->state >= NEIGHBOR_2WAY) /* Higher than 2WAY */
       if (neigh->priority > 0)         /* Eligible */
@@ -262,187 +449,23 @@ electdr(list nl)
   return (n);
 }
 
-static int
-can_do_adj(struct ospf_neighbor *n)
-{
-  struct ospf_iface *ifa;
-  struct proto *p;
-  int i;
-
-  ifa = n->ifa;
-  p = (struct proto *) (ifa->oa->po);
-  i = 0;
-
-  switch (ifa->type)
-  {
-  case OSPF_IT_PTP:
-  case OSPF_IT_VLINK:
-    i = 1;
-    break;
-  case OSPF_IT_BCAST:
-  case OSPF_IT_NBMA:
-    switch (ifa->state)
-    {
-    case OSPF_IS_DOWN:
-      bug("%s: Iface %s in down state?", p->name, ifa->iface->name);
-      break;
-    case OSPF_IS_WAITING:
-      DBG("%s: Neighbor? on iface %s\n", p->name, ifa->iface->name);
-      break;
-    case OSPF_IS_DROTHER:
-      if (((n->rid == ifa->drid) || (n->rid == ifa->bdrid))
-         && (n->state >= NEIGHBOR_2WAY))
-       i = 1;
-      break;
-    case OSPF_IS_PTP:
-    case OSPF_IS_BACKUP:
-    case OSPF_IS_DR:
-      if (n->state >= NEIGHBOR_2WAY)
-       i = 1;
-      break;
-    default:
-      bug("%s: Iface %s in unknown state?", p->name, ifa->iface->name);
-      break;
-    }
-    break;
-  default:
-    bug("%s: Iface %s is unknown type?", p->name, ifa->iface->name);
-    break;
-  }
-  DBG("%s: Iface %s can_do_adj=%d\n", p->name, ifa->iface->name, i);
-  return i;
-}
-
-/**
- * ospf_neigh_sm - ospf neighbor state machine
- * @n: neighor
- * @event: actual event
- *
- * This part implements the neighbor state machine as described in 10.3 of
- * RFC 2328. The only difference is that state %NEIGHBOR_ATTEMPT is not
- * used. We discover neighbors on nonbroadcast networks in the
- * same way as on broadcast networks. The only difference is in
- * sending hello packets. These are sent to IPs listed in
- * @ospf_iface->nbma_list .
- */
-void
-ospf_neigh_sm(struct ospf_neighbor *n, int event)
-{
-  struct proto_ospf *po = n->ifa->oa->po;
-  struct proto *p = &po->proto;
-
-  DBG("Neighbor state machine for neighbor %I, event '%s'\n", n->ip,
-            ospf_inm[event]);
-
-  switch (event)
-  {
-  case INM_START:
-    neigh_chstate(n, NEIGHBOR_ATTEMPT);
-    /* NBMA are used different way */
-    break;
-  case INM_HELLOREC:
-    switch (n->state)
-    {
-    case NEIGHBOR_ATTEMPT:
-    case NEIGHBOR_DOWN:
-      neigh_chstate(n, NEIGHBOR_INIT);
-    default:
-      tm_start(n->inactim, n->ifa->dead);      /* Restart inactivity timer */
-      break;
-    }
-    break;
-  case INM_2WAYREC:
-    if (n->state < NEIGHBOR_2WAY)
-      neigh_chstate(n, NEIGHBOR_2WAY);
-    if ((n->state == NEIGHBOR_2WAY) && can_do_adj(n))
-      neigh_chstate(n, NEIGHBOR_EXSTART);
-    break;
-  case INM_NEGDONE:
-    if (n->state == NEIGHBOR_EXSTART)
-    {
-      neigh_chstate(n, NEIGHBOR_EXCHANGE);
-
-      /* Reset DB summary list iterator */
-      s_get(&(n->dbsi));
-      s_init(&(n->dbsi), &po->lsal);
-
-      while (!EMPTY_LIST(n->ackl[ACKL_DELAY]))
-      {
-       struct lsah_n *no;
-       no = (struct lsah_n *) HEAD(n->ackl[ACKL_DELAY]);
-       rem_node(NODE no);
-       mb_free(no);
-      }
-    }
-    else
-      bug("NEGDONE and I'm not in EXSTART?");
-    break;
-  case INM_EXDONE:
-    neigh_chstate(n, NEIGHBOR_LOADING);
-    break;
-  case INM_LOADDONE:
-    neigh_chstate(n, NEIGHBOR_FULL);
-    break;
-  case INM_ADJOK:
-    switch (n->state)
-    {
-    case NEIGHBOR_2WAY:
-      /* Can In build adjacency? */
-      if (can_do_adj(n))
-      {
-       neigh_chstate(n, NEIGHBOR_EXSTART);
-      }
-      break;
-    default:
-      if (n->state >= NEIGHBOR_EXSTART)
-       if (!can_do_adj(n))
-       {
-         reset_lists(n);
-         neigh_chstate(n, NEIGHBOR_2WAY);
-       }
-      break;
-    }
-    break;
-  case INM_SEQMIS:
-  case INM_BADLSREQ:
-    if (n->state >= NEIGHBOR_EXCHANGE)
-    {
-      reset_lists(n);
-      neigh_chstate(n, NEIGHBOR_EXSTART);
-    }
-    break;
-  case INM_KILLNBR:
-  case INM_LLDOWN:
-  case INM_INACTTIM:
-    reset_lists(n);
-    neigh_chstate(n, NEIGHBOR_DOWN);
-    break;
-  case INM_1WAYREC:
-    reset_lists(n);
-    neigh_chstate(n, NEIGHBOR_INIT);
-    break;
-  default:
-    bug("%s: INM - Unknown event?", p->name);
-    break;
-  }
-}
-
 /**
- * bdr_election - (Backup) Designed Router election
+ * ospf_dr_election - (Backup) Designed Router election
  * @ifa: actual interface
  *
  * When the wait timer fires, it is time to elect (Backup) Designated Router.
- * Structure describing me is added to this list so every electing router
- * has the same list. Backup Designated Router is elected before Designated
- * Router. This process is described in 9.4 of RFC 2328.
+ * Structure describing me is added to this list so every electing router has
+ * the same list. Backup Designated Router is elected before Designated
+ * Router. This process is described in 9.4 of RFC 2328. The function is
+ * supposed to be called only from ospf_iface_sm() as a part of the interface
+ * state machine.
  */
 void
-bdr_election(struct ospf_iface *ifa)
+ospf_dr_election(struct ospf_iface *ifa)
 {
-  struct proto_ospf *po = ifa->oa->po;
-  u32 myid = po->router_id;
+  struct ospf_proto *p = ifa->oa->po;
   struct ospf_neighbor *neigh, *ndr, *nbdr, me;
-  int doadj;
+  u32 myid = p->router_id;
 
   DBG("(B)DR election.\n");
 
@@ -451,19 +474,14 @@ bdr_election(struct ospf_iface *ifa)
   me.priority = ifa->priority;
   me.ip = ifa->addr->ip;
 
-#ifdef OSPFv2
-  me.dr = ipa_to_u32(ifa->drip);
-  me.bdr = ipa_to_u32(ifa->bdrip);
-#else /* OSPFv3 */
-  me.dr = ifa->drid;
-  me.bdr = ifa->bdrid;
-  me.iface_id = ifa->iface->index;
-#endif
+  me.dr  = ospf_is_v2(p) ? ipa_to_u32(ifa->drip) : ifa->drid;
+  me.bdr = ospf_is_v2(p) ? ipa_to_u32(ifa->bdrip) : ifa->bdrid;
+  me.iface_id = ifa->iface_id;
 
   add_tail(&ifa->neigh_list, NODE & me);
 
-  nbdr = electbdr(ifa->neigh_list);
-  ndr = electdr(ifa->neigh_list);
+  nbdr = elect_bdr(p, ifa->neigh_list);
+  ndr = elect_dr(p, ifa->neigh_list);
 
   if (ndr == NULL)
     ndr = nbdr;
@@ -474,110 +492,164 @@ bdr_election(struct ospf_iface *ifa)
       || ((ifa->bdrid == myid) && (nbdr != &me))
       || ((ifa->bdrid != myid) && (nbdr == &me)))
   {
-#ifdef OSPFv2
-    me.dr = ndr ? ipa_to_u32(ndr->ip) : 0;
-    me.bdr = nbdr ? ipa_to_u32(nbdr->ip) : 0;
-#else /* OSPFv3 */
-    me.dr = ndr ? ndr->rid : 0;
-    me.bdr = nbdr ? nbdr->rid : 0;
-#endif
+    me.dr = ndr ? neigh_get_id(p, ndr) : 0;
+    me.bdr = nbdr ? neigh_get_id(p, nbdr) : 0;
 
-    nbdr = electbdr(ifa->neigh_list);
-    ndr = electdr(ifa->neigh_list);
+    nbdr = elect_bdr(p, ifa->neigh_list);
+    ndr = elect_dr(p, ifa->neigh_list);
 
     if (ndr == NULL)
       ndr = nbdr;
   }
 
-  u32 odrid = ifa->drid;
-  u32 obdrid = ifa->bdrid;
+  rem_node(NODE & me);
+
+
+  u32 old_drid = ifa->drid;
+  u32 old_bdrid = ifa->bdrid;
+
   ifa->drid = ndr ? ndr->rid : 0;
   ifa->drip = ndr ? ndr->ip  : IPA_NONE;
+  ifa->dr_iface_id = ndr ? ndr->iface_id : 0;
+
   ifa->bdrid = nbdr ? nbdr->rid : 0;
   ifa->bdrip = nbdr ? nbdr->ip  : IPA_NONE;
 
-#ifdef OSPFv3
-  ifa->dr_iface_id = ndr ? ndr->iface_id : 0;
-#endif
-
   DBG("DR=%R, BDR=%R\n", ifa->drid, ifa->bdrid);
 
-  doadj = ((ifa->drid != odrid) || (ifa->bdrid != obdrid));
-
-  if (myid == ifa->drid)
+  /* We are part of the interface state machine */
+  if (ifa->drid == myid)
     ospf_iface_chstate(ifa, OSPF_IS_DR);
+  else if (ifa->bdrid == myid)
+    ospf_iface_chstate(ifa, OSPF_IS_BACKUP);
   else
-  {
-    if (myid == ifa->bdrid)
-      ospf_iface_chstate(ifa, OSPF_IS_BACKUP);
-    else
-      ospf_iface_chstate(ifa, OSPF_IS_DROTHER);
-  }
-
-  rem_node(NODE & me);
+    ospf_iface_chstate(ifa, OSPF_IS_DROTHER);
 
-  if (doadj)
-  {
+  /* Review neighbor adjacencies if DR or BDR changed */
+  if ((ifa->drid != old_drid) || (ifa->bdrid != old_bdrid))
     WALK_LIST(neigh, ifa->neigh_list)
-    {
-      ospf_neigh_sm(neigh, INM_ADJOK);
-    }
-  }
+      if (neigh->state >= NEIGHBOR_2WAY)
+       ospf_neigh_sm(neigh, INM_ADJOK);
+
+  /* RFC 2328 12.4 Event 3 - DR change */
+  if (ifa->drid != old_drid)
+    ospf_notify_rt_lsa(ifa->oa);
 }
 
 struct ospf_neighbor *
 find_neigh(struct ospf_iface *ifa, u32 rid)
 {
   struct ospf_neighbor *n;
-
-  WALK_LIST(n, ifa->neigh_list) if (n->rid == rid)
-    return n;
+  WALK_LIST(n, ifa->neigh_list)
+    if (n->rid == rid)
+      return n;
   return NULL;
 }
 
-struct ospf_area *
-ospf_find_area(struct proto_ospf *po, u32 aid)
+struct ospf_neighbor *
+find_neigh_by_ip(struct ospf_iface *ifa, ip_addr ip)
 {
-  struct ospf_area *oa;
-  WALK_LIST(oa, po->area_list)
-    if (((struct ospf_area *) oa)->areaid == aid)
-    return oa;
+  struct ospf_neighbor *n;
+  WALK_LIST(n, ifa->neigh_list)
+    if (ipa_equal(n->ip, ip))
+      return n;
   return NULL;
 }
 
-/* Neighbor is inactive for a long time. Remove it. */
 static void
-neighbor_timer_hook(timer * timer)
+inactivity_timer_hook(timer * timer)
 {
   struct ospf_neighbor *n = (struct ospf_neighbor *) timer->data;
-  struct ospf_iface *ifa = n->ifa;
-  struct proto *p = &ifa->oa->po->proto;
+  struct ospf_proto *p = n->ifa->oa->po;
 
-  OSPF_TRACE(D_EVENTS,
-            "Inactivity timer fired on interface %s for neighbor %I.",
-            ifa->iface->name, n->ip);
-  ospf_neigh_remove(n);
+  OSPF_TRACE(D_EVENTS, "Inactivity timer expired for nbr %R on %s",
+            n->rid, n->ifa->ifname);
+  ospf_neigh_sm(n, INM_INACTTIM);
+}
+
+static void
+ospf_neigh_bfd_hook(struct bfd_request *req)
+{
+  struct ospf_neighbor *n = req->data;
+  struct ospf_proto *p = n->ifa->oa->po;
+
+  if (req->down)
+  {
+    OSPF_TRACE(D_EVENTS, "BFD session down for nbr %R on %s",
+              n->rid, n->ifa->ifname);
+    ospf_neigh_sm(n, INM_INACTTIM);
+  }
 }
 
 void
-ospf_neigh_remove(struct ospf_neighbor *n)
+ospf_neigh_update_bfd(struct ospf_neighbor *n, int use_bfd)
 {
-  struct ospf_iface *ifa = n->ifa;
-  struct proto *p = &ifa->oa->po->proto;
+  if (use_bfd && !n->bfd_req)
+    n->bfd_req = bfd_request_session(n->pool, n->ip, n->ifa->addr->ip, n->ifa->iface,
+                                    ospf_neigh_bfd_hook, n);
 
-  s_get(&(n->dbsi));
-  neigh_chstate(n, NEIGHBOR_DOWN);
-  rem_node(NODE n);
-  rfree(n->pool);
-  OSPF_TRACE(D_EVENTS, "Deleting neigbor.");
+  if (!use_bfd && n->bfd_req)
+  {
+    rfree(n->bfd_req);
+    n->bfd_req = NULL;
+  }
+}
+
+
+static void
+dbdes_timer_hook(timer *t)
+{
+  struct ospf_neighbor *n = t->data;
+  struct ospf_proto *p = n->ifa->oa->po;
+
+  // OSPF_TRACE(D_EVENTS, "DBDES timer expired for nbr %R on %s", n->rid, n->ifa->ifname);
+
+  if (n->state == NEIGHBOR_EXSTART)
+    ospf_send_dbdes(p, n);
+
+  if ((n->state == NEIGHBOR_EXCHANGE) && (n->myimms & DBDES_MS))
+    ospf_rxmt_dbdes(p, n);
 }
 
+static void
+lsrq_timer_hook(timer *t)
+{
+  struct ospf_neighbor *n = t->data;
+  struct ospf_proto *p = n->ifa->oa->po;
+
+  // OSPF_TRACE(D_EVENTS, "LSRQ timer expired for nbr %R on %s", n->rid, n->ifa->ifname);
+
+  if ((n->state >= NEIGHBOR_EXCHANGE) && !EMPTY_SLIST(n->lsrql))
+    ospf_send_lsreq(p, n);
+}
+
+static void
+lsrt_timer_hook(timer *t)
+{
+  struct ospf_neighbor *n = t->data;
+  struct ospf_proto *p = n->ifa->oa->po;
+
+  // OSPF_TRACE(D_EVENTS, "LSRT timer expired for nbr %R on %s", n->rid, n->ifa->ifname);
+
+  if ((n->state >= NEIGHBOR_EXCHANGE) && !EMPTY_SLIST(n->lsrtl))
+    ospf_rxmt_lsupd(p, n);
+}
+
+static void
+ackd_timer_hook(timer *t)
+{
+  struct ospf_neighbor *n = t->data;
+  struct ospf_proto *p = n->ifa->oa->po;
+
+  ospf_send_lsack(p, n, ACKL_DELAY);
+}
+
+
 void
 ospf_sh_neigh_info(struct ospf_neighbor *n)
 {
   struct ospf_iface *ifa = n->ifa;
-  char *pos = "other";
+  char *pos = "PtP  ";
   char etime[6];
   int exp, sec, min;
 
@@ -593,74 +665,16 @@ ospf_sh_neigh_info(struct ospf_neighbor *n)
     bsprintf(etime, "%02u:%02u", min, sec);
   }
 
-  if (n->rid == ifa->drid)
-    pos = "dr   ";
-  if (n->rid == ifa->bdrid)
-    pos = "bdr  ";
-  if ((n->ifa->type == OSPF_IT_PTP) || (n->ifa->type == OSPF_IT_VLINK))
-    pos = "ptp  ";
-
-  cli_msg(-1013, "%-1R\t%3u\t%s/%s\t%-5s\t%-10s %-1I", n->rid, n->priority,
-         ospf_ns[n->state], pos, etime,
-          (ifa->type == OSPF_IT_VLINK ? "vlink" : ifa->iface->name), n->ip);
-}
-
-static void
-rxmt_timer_hook(timer * timer)
-{
-  struct ospf_neighbor *n = (struct ospf_neighbor *) timer->data;
-  // struct proto *p = &n->ifa->oa->po->proto;
-  struct top_hash_entry *en;
-
-  DBG("%s: RXMT timer fired on interface %s for neigh: %I.\n",
-      p->name, n->ifa->iface->name, n->ip);
-
-  if(n->state < NEIGHBOR_EXSTART) return;
-
-  if (n->state == NEIGHBOR_EXSTART)
-  {
-    ospf_dbdes_send(n, 1);
-    return;
-  }
-
-  if ((n->state == NEIGHBOR_EXCHANGE) && n->myimms.bit.ms)     /* I'm master */
-    ospf_dbdes_send(n, 0);
-
-
-  if (n->state < NEIGHBOR_FULL)        
-    ospf_lsreq_send(n);        /* EXCHANGE or LOADING */
-  else
+  if ((ifa->type == OSPF_IT_BCAST) || (ifa->type == OSPF_IT_NBMA))
   {
-    if (!EMPTY_SLIST(n->lsrtl))        /* FULL */
-    {
-      list uplist;
-      slab *upslab;
-      struct l_lsr_head *llsh;
-
-      init_list(&uplist);
-      upslab = sl_new(n->pool, sizeof(struct l_lsr_head));
-
-      WALK_SLIST(en, n->lsrtl)
-      {
-       if ((SNODE en)->next == (SNODE en))
-         bug("RTList is cycled");
-       llsh = sl_alloc(upslab);
-       llsh->lsh.id = en->lsa.id;
-       llsh->lsh.rt = en->lsa.rt;
-       llsh->lsh.type = en->lsa.type;
-       DBG("Working on ID: %R, RT: %R, Type: %u\n",
-           en->lsa.id, en->lsa.rt, en->lsa.type);
-       add_tail(&uplist, NODE llsh);
-      }
-      ospf_lsupd_send_list(n, &uplist);
-      rfree(upslab);
-    }
+    if (n->rid == ifa->drid)
+      pos = "DR   ";
+    else if (n->rid == ifa->bdrid)
+      pos = "BDR  ";
+    else
+      pos = "Other";
   }
-}
 
-static void
-ackd_timer_hook(timer * t)
-{
-  struct ospf_neighbor *n = t->data;
-  ospf_lsack_send(n, ACKL_DELAY);
+  cli_msg(-1013, "%-1R\t%3u\t%s/%s\t%-5s\t%-10s %-1I", n->rid, n->priority,
+         ospf_ns_names[n->state], pos, etime, ifa->ifname, n->ip);
 }