]> 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 d22a60efd36c554c2492f77cce5c2a5c21649e3f..0223ccdf211b3f2b39fb1bd7352fc2bbf7f1a96c 100644 (file)
 /*
  *     BIRD -- OSPF
  *
- *     (c) 1999 - 2000 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_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_ns_names[] = {
+  "Down", "Attempt", "Init", "2-Way", "ExStart", "Exchange", "Loading", "Full"
+};
 
-/**
- * neigh_chstate - handles changes related to new or lod state of neighbor
- * @n: OSPF neighbor
- * @state: new state
- *
- * Many actions has to be taken acording to state change of neighbor. It
- * starts rxmt timers, call interface state machine etc.
- */
+const char *ospf_inm_names[] = {
+  "HelloReceived", "Start", "2-WayReceived", "NegotiationDone", "ExchangeDone",
+  "BadLSReq", "LoadingDone", "AdjOK?", "SeqNumberMismatch", "1-WayReceived",
+  "KillNbr", "InactivityTimer", "LLDown"
+};
 
-void
-neigh_chstate(struct ospf_neighbor *n, u8 state)
-{
-  u8 oldstate;
 
-  oldstate=n->state;
+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);
 
-  if(oldstate!=state)
-  {
-    struct ospf_iface *ifa=n->ifa;
-    struct proto_ospf *po=ifa->oa->po;
-    struct proto *p=&po->proto;
 
-    n->state=state;
+static void
+init_lists(struct ospf_proto *p, struct ospf_neighbor *n)
+{
+  s_init_list(&(n->lsrql));
+  n->lsrqi = SHEAD(n->lsrql);
+  n->lsrqh = ospf_top_new(p, n->pool);
 
-    OSPF_TRACE( D_EVENTS, "Neighbor %I changes state from \"%s\" to \"%s\".",
-      n->ip, ospf_ns[oldstate], ospf_ns[state]);
+  s_init_list(&(n->lsrtl));
+  n->lsrth = ospf_top_new(p, n->pool);
+}
 
-    if((state==NEIGHBOR_2WAY) && (oldstate<NEIGHBOR_2WAY))
-      ospf_int_sm(ifa, ISM_NEICH);
-    if((state<NEIGHBOR_2WAY) && (oldstate>=NEIGHBOR_2WAY))
-      ospf_int_sm(ifa, ISM_NEICH);
+static void
+release_lsrtl(struct ospf_proto *p, struct ospf_neighbor *n)
+{
+  struct top_hash_entry *ret, *en;
 
-    if(oldstate==NEIGHBOR_FULL)        /* Decrease number of adjacencies */
-    {
-      ifa->fadj--;
-      schedule_rt_lsa(ifa->oa);
-      originate_net_lsa(ifa);
-    }
-  
-    if(state==NEIGHBOR_FULL)   /* Increase number of adjacencies */
-    {
-      ifa->fadj++;
-      schedule_rt_lsa(ifa->oa);
-      originate_net_lsa(ifa);
-    }
-    if(oldstate>=NEIGHBOR_EXSTART && state<NEIGHBOR_EXSTART)
-    {
-      /* Stop RXMT timer */
-      tm_stop(n->rxmt_timer);
-    }
-    if(state==NEIGHBOR_EXSTART)
-    {
-      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;
-      tm_start(n->rxmt_timer,1);       /* Or some other number ? */
-    }
-    if(state<NEIGHBOR_EXCHANGE) tm_stop(n->lsrr_timer);
+  WALK_SLIST(ret, n->lsrtl)
+  {
+    en = ospf_hash_find_entry(p->gr, ret);
+    if (en)
+      en->ret_count--;
   }
 }
 
-struct ospf_neighbor *
-electbdr(list nl)
+/* Resets LSA request and retransmit lists.
+ * We do not reset DB summary list iterator here,
+ * it is reset during entering EXCHANGE state.
+ */
+static void
+reset_lists(struct ospf_proto *p, struct ospf_neighbor *n)
 {
-  struct ospf_neighbor *neigh,*n1,*n2;
+  release_lsrtl(p, n);
+  ospf_top_free(n->lsrqh);
+  ospf_top_free(n->lsrth);
+  ospf_reset_lsack_queue(n);
 
-  n1=NULL;
-  n2=NULL;
-  WALK_LIST (neigh, nl)                /* First try those decl. themselves */
-  {
-    if(neigh->state>=NEIGHBOR_2WAY)    /* Higher than 2WAY */
-      if(neigh->priority>0)            /* Eligible */
-        if(ipa_compare(neigh->ip,neigh->dr)!=0)        /* And not decl. itself DR */
-       {
-         if(ipa_compare(neigh->ip,neigh->bdr)==0)      /* Declaring BDR */
-          {
-            if(n1!=NULL)
-            {
-              if(neigh->priority>n1->priority) n1=neigh;
-             else if(neigh->priority==n1->priority)
-                 if(neigh->rid>n1->rid) n1=neigh;
-            }
-           else
-            {
-              n1=neigh;
-            }
-          }
-         else                          /* And NOT declaring BDR */
-          {
-            if(n2!=NULL)
-            {
-              if(neigh->priority>n2->priority) n2=neigh;
-             else if(neigh->priority==n2->priority)
-                 if(neigh->rid>n2->rid) n2=neigh;
-            }
-           else
-            {
-              n2=neigh;
-            }
-          }
-      }
-  }
-  if(n1==NULL) n1=n2;
+  tm_stop(n->dbdes_timer);
+  tm_stop(n->lsrq_timer);
+  tm_stop(n->lsrt_timer);
+  tm_stop(n->ackd_timer);
 
-  return(n1);
+  init_lists(p, n);
 }
 
 struct ospf_neighbor *
-electdr(list nl)
+ospf_neighbor_new(struct ospf_iface *ifa)
 {
-  struct ospf_neighbor *neigh,*n;
+  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;
+  n->ifa = ifa;
+  add_tail(&ifa->neigh_list, NODE n);
+  n->adj = 0;
+  n->csn = 0;
+  n->state = NEIGHBOR_DOWN;
+
+  init_lists(p, n);
+  s_init(&(n->dbsi), &(p->lsal));
+
+  init_list(&n->ackl[ACKL_DIRECT]);
+  init_list(&n->ackl[ACKL_DELAY]);
+
+  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;
 
-  n=NULL;
-  WALK_LIST (neigh, nl)        /* And now DR */
+  if ((ifa->type == OSPF_IT_NBMA) || (ifa->type == OSPF_IT_PTMP))
   {
-    if(neigh->state>=NEIGHBOR_2WAY)    /* Higher than 2WAY */
-      if(neigh->priority>0)            /* Eligible */
-        if(ipa_compare(neigh->ip,neigh->dr)==0)        /* And declaring itself DR */
-       {
-          if(n!=NULL)
-          {
-            if(neigh->priority>n->priority) n=neigh;
-           else if(neigh->priority==n->priority)
-               if(neigh->rid>n->rid) n=neigh;
-          }
-         else
-          {
-            n=neigh;
-          }
-      }
+    struct nbma_node *nn = find_nbma_node(ifa, n->ip);
+    if (nn)
+      nn->found = 0;
   }
 
-  return(n);
+  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);
 }
 
-int
-can_do_adj(struct ospf_neighbor *n)
+/**
+ * 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
+ospf_neigh_chstate(struct ospf_neighbor *n, u8 state)
 {
-  struct ospf_iface *ifa;
-  struct proto *p;
-  int i;
+  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]);
 
-  ifa=n->ifa;
-  p=(struct proto *)(ifa->proto);
-  i=0;
+  n->state = state;
 
-  switch(ifa->type)
+  /* 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)
   {
-    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;
+    /* 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);
   }
-  DBG("%s: Iface %s can_do_adj=%d\n",p->name, ifa->iface->name,i);
-  return i;
+
+  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);
 }
 
 /**
@@ -217,325 +204,477 @@ can_do_adj(struct ospf_neighbor *n)
  * @n: neighor
  * @event: actual event
  *
- * This part implements 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 using the
- * same ways as on broadcast networks. The only difference is in
- * sending hello packets. These are send to IPs listed in
+ * 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 *p=(struct proto *)(n->ifa->proto);
-  struct proto_ospf *po=n->ifa->proto;
+  struct ospf_proto *p = n->ifa->oa->po;
 
-  DBG("%s: Neighbor state machine for neighbor %I, event \"%s\".\n",
-    p->name, n->rid, ospf_inm[event]);
+  DBG("Neighbor state machine for %R on %s, event %s\n",
+      n->rid, n->ifa->ifname, ospf_inm_names[event]);
 
-  switch(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:
-          restart_inactim(n);
-         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);
-        s_init_list(&(n->lsrql));
-       n->lsrqh=ospf_top_new(n->ifa->proto);
-        s_init_list(&(n->lsrtl));
-       n->lsrth=ospf_top_new(n->ifa->proto);
-       s_init(&(n->dbsi), &(n->ifa->oa->lsal));
-       s_init(&(n->lsrqi), &(n->lsrql));
-       s_init(&(n->lsrti), &(n->lsrtl));
-       tm_start(n->lsrr_timer,n->ifa->rxmtint);
-       tm_start(n->ackd_timer,n->ifa->rxmtint/2);
-      }
-      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))
-              {
-                neigh_chstate(n,NEIGHBOR_2WAY);
-              }
-            break;
-        }
+  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);
+
+      /* Reset DB summary list iterator */
+      s_get(&(n->dbsi));
+      s_init(&(n->dbsi), &p->lsal);
+
+      /* 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;
+
+  case INM_LOADDONE:
+    ospf_neigh_chstate(n, NEIGHBOR_FULL);
+    break;
+
+  case INM_ADJOK:
+    /* Can In build adjacency? */
+    if ((n->state == NEIGHBOR_2WAY) && can_do_adj(n))
+    {
+      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;
+
+  case INM_SEQMIS:
+  case INM_BADLSREQ:
+    if (n->state >= NEIGHBOR_EXCHANGE)
+    {
+      reset_lists(p, n);
+      ospf_neigh_chstate(n, 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)
+    {
+    case OSPF_IS_DOWN:
+    case OSPF_IS_LOOP:
+      bug("%s: Iface %s in down state?", p->p.name, ifa->ifname);
       break;
-    case INM_SEQMIS:
-    case INM_BADLSREQ:
-      OSPF_TRACE(D_EVENTS, "Bad LS req!");
-      if(n->state>=NEIGHBOR_EXCHANGE)
-      {
-        neigh_chstate(n,NEIGHBOR_EXSTART);
-      }
+    case OSPF_IS_WAITING:
+      DBG("%s: Neighbor? on iface %s\n", p->p.name, ifa->ifname);
       break;
-    case INM_KILLNBR:
-    case INM_LLDOWN:
-    case INM_INACTTIM:
-      neigh_chstate(n,NEIGHBOR_DOWN);
+    case OSPF_IS_DROTHER:
+      if (((n->rid == ifa->drid) || (n->rid == ifa->bdrid))
+         && (n->state >= NEIGHBOR_2WAY))
+       i = 1;
       break;
-    case INM_1WAYREC:
-      neigh_chstate(n,NEIGHBOR_INIT);
+    case OSPF_IS_PTP:
+    case OSPF_IS_BACKUP:
+    case OSPF_IS_DR:
+      if (n->state >= NEIGHBOR_2WAY)
+       i = 1;
       break;
     default:
-      bug("%s: INM - Unknown event?",p->name);
+      bug("%s: Iface %s in unknown state?", p->p.name, ifa->ifname);
       break;
+    }
+    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 *
+elect_bdr(struct ospf_proto *p, list nl)
+{
+  struct ospf_neighbor *neigh, *n1, *n2;
+  u32 nid;
+
+  n1 = NULL;
+  n2 = NULL;
+  WALK_LIST(neigh, nl)                 /* First try those decl. themselves */
+  {
+    nid = neigh_get_id(p, neigh);
+
+    if (neigh->state >= NEIGHBOR_2WAY) /* Higher than 2WAY */
+      if (neigh->priority > 0)         /* Eligible */
+       if (neigh->dr != nid)           /* And not decl. itself DR */
+       {
+         if (neigh->bdr == nid)        /* Declaring BDR */
+         {
+           if (n1 != NULL)
+           {
+             if (neigh->priority > n1->priority)
+               n1 = neigh;
+             else if (neigh->priority == n1->priority)
+               if (neigh->rid > n1->rid)
+                 n1 = neigh;
+           }
+           else
+           {
+             n1 = neigh;
+           }
+         }
+         else                  /* And NOT declaring BDR */
+         {
+           if (n2 != NULL)
+           {
+             if (neigh->priority > n2->priority)
+               n2 = neigh;
+             else if (neigh->priority == n2->priority)
+               if (neigh->rid > n2->rid)
+                 n2 = neigh;
+           }
+           else
+           {
+             n2 = neigh;
+           }
+         }
+       }
+  }
+  if (n1 == NULL)
+    n1 = n2;
+
+  return (n1);
+}
+
+static struct ospf_neighbor *
+elect_dr(struct ospf_proto *p, list nl)
+{
+  struct ospf_neighbor *neigh, *n;
+  u32 nid;
+
+  n = NULL;
+  WALK_LIST(neigh, nl)                 /* And now DR */
+  {
+    nid = neigh_get_id(p, neigh);
+
+    if (neigh->state >= NEIGHBOR_2WAY) /* Higher than 2WAY */
+      if (neigh->priority > 0)         /* Eligible */
+       if (neigh->dr == nid)           /* And declaring itself DR */
+       {
+         if (n != NULL)
+         {
+           if (neigh->priority > n->priority)
+             n = neigh;
+           else if (neigh->priority == n->priority)
+             if (neigh->rid > n->rid)
+               n = neigh;
+         }
+         else
+         {
+           n = neigh;
+         }
+       }
+  }
+
+  return (n);
 }
 
 /**
- * bdr_election - (Backup) Designed Router election
+ * ospf_dr_election - (Backup) Designed Router election
  * @ifa: actual interface
  *
- * When wait time fires, it time to elect (Backup) Designed Router.
- * Structure describing me is added to this list so every electing router
- * has the same list. Backup Designed Router is elected before Designed
- * Router. This process is described in 9.4 of RFC 2328.
+ * 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. 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 ospf_neighbor *neigh,*ndr,*nbdr,me,*tmp;
-  u32 myid;
-  ip_addr ndrip, nbdrip;
-  int doadj;
-  struct proto *p=&ifa->proto->proto;
+  struct ospf_proto *p = ifa->oa->po;
+  struct ospf_neighbor *neigh, *ndr, *nbdr, me;
+  u32 myid = p->router_id;
 
   DBG("(B)DR election.\n");
 
-  myid=p->cf->global->router_id;
+  me.state = NEIGHBOR_2WAY;
+  me.rid = myid;
+  me.priority = ifa->priority;
+  me.ip = ifa->addr->ip;
 
-  me.state=NEIGHBOR_2WAY;
-  me.rid=myid;
-  me.priority=ifa->priority;
-  me.dr=ifa->drip;
-  me.bdr=ifa->bdrip;
-  me.ip=ifa->iface->addr->ip;
+  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);
+  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;
+  if (ndr == NULL)
+    ndr = nbdr;
 
-  if(((ifa->drid==myid) && (ndr!=&me))
-    || ((ifa->drid!=myid) && (ndr==&me))
-    || ((ifa->bdrid==myid) && (nbdr!=&me)) 
-    || ((ifa->bdrid!=myid) && (nbdr==&me)))
+  /* 9.4. (4) */
+  if (((ifa->drid == myid) && (ndr != &me))
+      || ((ifa->drid != myid) && (ndr == &me))
+      || ((ifa->bdrid == myid) && (nbdr != &me))
+      || ((ifa->bdrid != myid) && (nbdr == &me)))
   {
-    if(ndr==NULL) ifa->drip=me.dr=ipa_from_u32(0);
-    else ifa->drip=me.dr=ndr->ip;
+    me.dr = ndr ? neigh_get_id(p, ndr) : 0;
+    me.bdr = nbdr ? neigh_get_id(p, nbdr) : 0;
 
-    if(nbdr==NULL) ifa->bdrip=me.bdr=ipa_from_u32(0);
-    else ifa->bdrip=me.bdr=nbdr->ip;
+    nbdr = elect_bdr(p, ifa->neigh_list);
+    ndr = elect_dr(p, ifa->neigh_list);
 
-    nbdr=electbdr(ifa->neigh_list);
-    ndr=electdr(ifa->neigh_list);
+    if (ndr == NULL)
+      ndr = nbdr;
   }
 
-  if(ndr==NULL) ndrip=ipa_from_u32(0);
-  else ndrip=ndr->ip;
+  rem_node(NODE & me);
 
-  if(nbdr==NULL) nbdrip=ipa_from_u32(0);
-  else nbdrip=nbdr->ip;
 
-  doadj=0;
-  if((ipa_compare(ifa->drip,ndrip)!=0) || (ipa_compare(ifa->bdrip,nbdrip)!=0))
-    doadj=1;
+  u32 old_drid = ifa->drid;
+  u32 old_bdrid = ifa->bdrid;
 
-  if(ndr==NULL)
-  {
-    ifa->drid=0;
-    ifa->drip=ipa_from_u32(0);
-  }
-  else
-  {
-    ifa->drid=ndr->rid;
-    ifa->drip=ndr->ip;
-  }
+  ifa->drid = ndr ? ndr->rid : 0;
+  ifa->drip = ndr ? ndr->ip  : IPA_NONE;
+  ifa->dr_iface_id = ndr ? ndr->iface_id : 0;
 
-  if(nbdr==NULL)
-  {
-    ifa->bdrid=0;
-    ifa->bdrip=ipa_from_u32(0);
-  }
-  else
-  {
-    ifa->bdrid=nbdr->rid;
-    ifa->bdrip=nbdr->ip;
-  }
+  ifa->bdrid = nbdr ? nbdr->rid : 0;
+  ifa->bdrip = nbdr ? nbdr->ip  : IPA_NONE;
 
-  DBG("DR=%I, BDR=%I\n", ifa->drid, ifa->bdrid);
+  DBG("DR=%R, BDR=%R\n", ifa->drid, ifa->bdrid);
 
-  if(myid==ifa->drid) iface_chstate(ifa, OSPF_IS_DR);
+  /* 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) iface_chstate(ifa, OSPF_IS_BACKUP);
-    else iface_chstate(ifa, OSPF_IS_DROTHER);
-  }
+    ospf_iface_chstate(ifa, OSPF_IS_DROTHER);
 
-  rem_node(NODE &me);
+  /* Review neighbor adjacencies if DR or BDR changed */
+  if ((ifa->drid != old_drid) || (ifa->bdrid != old_bdrid))
+    WALK_LIST(neigh, ifa->neigh_list)
+      if (neigh->state >= NEIGHBOR_2WAY)
+       ospf_neigh_sm(neigh, INM_ADJOK);
 
-  if(doadj)
-  {
-    WALK_LIST (neigh, ifa->neigh_list)
-    {
-      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)
+  WALK_LIST(n, ifa->neigh_list)
+    if (n->rid == rid)
       return n;
   return NULL;
 }
 
 struct ospf_neighbor *
-find_neigh_noifa(struct proto_ospf *po, u32 rid)
+find_neigh_by_ip(struct ospf_iface *ifa, ip_addr ip)
 {
   struct ospf_neighbor *n;
-  struct ospf_iface *ifa;
-
-  WALK_LIST (ifa, po->iface_list)
-    if((n=find_neigh(ifa, rid))!=NULL)
+  WALK_LIST(n, ifa->neigh_list)
+    if (ipa_equal(n->ip, ip))
       return n;
   return NULL;
 }
 
-struct ospf_area *
-ospf_find_area(struct proto_ospf *po, u32 aid)
+static void
+inactivity_timer_hook(timer * timer)
 {
-  struct ospf_area *oa;
-  WALK_LIST(NODE oa,po->area_list)
-    if(((struct ospf_area *)oa)->areaid==aid) return oa;
-  return NULL;
+  struct ospf_neighbor *n = (struct ospf_neighbor *) timer->data;
+  struct ospf_proto *p = n->ifa->oa->po;
+
+  OSPF_TRACE(D_EVENTS, "Inactivity timer expired for nbr %R on %s",
+            n->rid, n->ifa->ifname);
+  ospf_neigh_sm(n, INM_INACTTIM);
 }
 
-/* Neighbor is inactive for a long time. Remove it. */
-void
-neighbor_timer_hook(timer *timer)
+static void
+ospf_neigh_bfd_hook(struct bfd_request *req)
 {
-  struct ospf_neighbor *n;
-  struct ospf_iface *ifa;
-  struct proto *p;
-
-  n=(struct ospf_neighbor *)timer->data;
-  ifa=n->ifa;
-  p=(struct proto *)(ifa->proto);
-  OSPF_TRACE(D_EVENTS,"Inactivity timer fired on interface %s for neighbor %I.",
-    ifa->iface->name, n->ip);
-  ospf_neigh_remove(n);
+  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;
-  struct proto *p;
-
-  ifa=n->ifa;
-  p=(struct proto *)(ifa->proto);
-  neigh_chstate(n, NEIGHBOR_DOWN);
-  tm_stop(n->inactim);
-  rfree(n->inactim);
-  if(n->rxmt_timer!=NULL)
-  {
-    tm_stop(n->rxmt_timer);
-    rfree(n->rxmt_timer);
-  }
-  if(n->lsrr_timer!=NULL)
-  {
-    tm_stop(n->lsrr_timer);
-    rfree(n->lsrr_timer);
-  }
-  if(n->ackd_timer!=NULL)
+  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);
+
+  if (!use_bfd && n->bfd_req)
   {
-    tm_stop(n->ackd_timer);
-    rfree(n->ackd_timer);
+    rfree(n->bfd_req);
+    n->bfd_req = NULL;
   }
-  if(n->ldbdes!=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 = "PtP  ";
+  char etime[6];
+  int exp, sec, min;
+
+  exp = n->inactim->expires - now;
+  sec = exp % 60;
+  min = exp / 60;
+  if (min > 59)
   {
-    mb_free(n->ldbdes);
+    bsprintf(etime, "-Inf-");
   }
-  if(n->lsrqh!=NULL)
+  else
   {
-    ospf_top_free(n->lsrqh);
+    bsprintf(etime, "%02u:%02u", min, sec);
   }
-  if(n->lsrth!=NULL)
+
+  if ((ifa->type == OSPF_IT_BCAST) || (ifa->type == OSPF_IT_NBMA))
   {
-    ospf_top_free(n->lsrth);
+    if (n->rid == ifa->drid)
+      pos = "DR   ";
+    else if (n->rid == ifa->bdrid)
+      pos = "BDR  ";
+    else
+      pos = "Other";
   }
-  rem_node(NODE n);
-  mb_free(n);
-  OSPF_TRACE(D_EVENTS, "Deleting neigbor.");
-}
 
-void
-ospf_sh_neigh_info(struct ospf_neighbor *n)
-{
-   struct ospf_iface *ifa=n->ifa;
-   char *pos="other";
-   char etime[6];
-   int exp,sec,min;
-
-   exp=n->inactim->expires-now;
-   sec=exp-(exp/60);
-   min=(exp-sec)/60;
-   if(min>59)
-   {
-     bsprintf(etime,"-Inf-");
-   }
-   else
-   {
-     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) pos="ptp  ";
-
-   cli_msg(-1013,"%-18I\t%3u\t%s/%s\t%-5s\t%-18I\t%-10s",n->rid, n->priority,
-     ospf_ns[n->state], pos, etime, n->ip,ifa->iface->name);
+  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);
 }