]> git.ipfire.org Git - thirdparty/bird.git/blobdiff - proto/ospf/neighbor.c
Temporary integrated OSPF commit.
[thirdparty/bird.git] / proto / ospf / neighbor.c
index 359050485021937511fbbcb7a6643113ef1a617e..392f1d64b710ddcbea0e8e6d0dbdd4f1e4db2d40 100644 (file)
 /*
  *     BIRD -- OSPF
  *
- *     (c) 1999 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",
+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" };
+  "inactivity timer", "line down"
+};
 
-void
+static void neigh_chstate(struct ospf_neighbor *n, u8 state);
+static void neighbor_timer_hook(timer * timer);
+static void rxmt_timer_hook(timer * timer);
+static void ackd_timer_hook(timer * t);
+
+static void
+init_lists(struct ospf_neighbor *n)
+{
+  s_init_list(&(n->lsrql));
+  n->lsrqh = ospf_top_new(n->pool);
+
+  s_init_list(&(n->lsrtl));
+  n->lsrth = ospf_top_new(n->pool);
+}
+
+/* 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_neighbor *n)
+{
+  ospf_top_free(n->lsrqh);
+  ospf_top_free(n->lsrth);
+  init_lists(n);
+}
+
+struct ospf_neighbor *
+ospf_neighbor_new(struct ospf_iface *ifa)
+{
+  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(n);
+  s_init(&(n->dbsi), &(p->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->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->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_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->p.name);
+
+  return (n);
+}
+
+/**
+ * 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)
 {
-  u8 oldstate;
+  struct ospf_iface *ifa = n->ifa;
+  struct ospf_proto *p = ifa->oa->po;
+  u8 old_state = n->state;
+  int old_fadj = ifa->fadj;
 
-  oldstate=n->state;
+  if (state == old_state)
+    return;
 
-  if(oldstate!=state)
-  {
-    struct ospf_iface *ifa=n->ifa;
-    struct proto_ospf *po=ifa->oa->po;
-    struct proto *p=&po->proto;
+  OSPF_TRACE(D_EVENTS, "Neighbor %I changes state from %s to %s",
+            n->ip, ospf_ns[old_state], ospf_ns[state]);
 
-    n->state=state;
+  n->state = state;
 
-    debug("%s: Neighbor %I changes state from \"%s\" to \"%s\".\n",
-      p->name, n->ip, ospf_ns[oldstate], ospf_ns[state]);
+  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);
 
-    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);
+  /* Increase number of partial adjacencies */
+  if ((state == NEIGHBOR_EXCHANGE) || (state == NEIGHBOR_LOADING))
+    p->padj++;
 
-    if(oldstate==NEIGHBOR_FULL)        /* Decrease number of adjacencies */
-    {
-      ifa->fadj--;
-      schedule_rt_lsa(ifa->oa);
-      originate_net_lsa(ifa,po);
-    }
-  
-    if(state==NEIGHBOR_FULL)   /* Increase number of adjacencies */
-    {
-      ifa->fadj++;
-      schedule_rt_lsa(ifa->oa);
-      originate_net_lsa(ifa,po);
-    }
-    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);
+  /* 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;
+  }
+
+  if (state > NEIGHBOR_EXSTART)
+    n->myimms &= ~DBDES_I;
 }
 
-struct ospf_neighbor *
-electbdr(list nl)
+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;
+  struct ospf_neighbor *neigh, *n1, *n2;
+  u32 nid;
 
-  n1=NULL;
-  n2=NULL;
-  WALK_LIST (neigh, nl)                /* First try those decl. themselves */
+  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(neigh->rid!=neigh->dr)      /* And not declaring itself 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 not decl. itself DR */
        {
-         if(neigh->rid==neigh->bdr)    /* 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;
-            }
+         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;
-            }
+           {
+             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;
-            }
-          }
-      }
+           {
+             n2 = neigh;
+           }
+         }
+       }
   }
-  if(n1==NULL) n1=n2;
+  if (n1 == NULL)
+    n1 = n2;
 
-  return(n1);
+  return (n1);
 }
 
-struct ospf_neighbor *
-electdr(list nl)
+static struct ospf_neighbor *
+elect_dr(struct ospf_proto *p, list nl)
 {
-  struct ospf_neighbor *neigh,*n;
+  struct ospf_neighbor *neigh, *n;
+  u32 nid;
 
-  n=NULL;
-  WALK_LIST (neigh, nl)        /* And now DR */
+  n = NULL;
+  WALK_LIST(neigh, nl)                 /* And now DR */
   {
-    if(neigh->state>=NEIGHBOR_2WAY)    /* Higher than 2WAY */
-      if(neigh->priority>0)            /* Eligible */
-        if(neigh->rid==neigh->dr)      /* And declaring itself 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;
-          }
+         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;
-          }
-      }
+         {
+           n = neigh;
+         }
+       }
   }
 
-  return(n);
+  return (n);
 }
 
-int
+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->proto);
-  i=0;
+  struct ospf_iface *ifa = n->ifa;
+  struct ospf_proto *p = ifa->oa->po;
+  int i = 0;
 
-  switch(ifa->type)
+  switch (ifa->type)
   {
-    case OSPF_IT_PTP:
-    case OSPF_IT_VLINK:
-      i=1;
+  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 OSPF_IT_BCAST:
-    case OSPF_IT_NBMA:
-      switch(ifa->state)
-      {
-        case OSPF_IS_DOWN:
-          die("%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:
-          die("%s: Iface %s in unknown state?",p->name, ifa->iface->name);
-          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:
-      die("%s: Iface %s is unknown type?",p->name, ifa->iface->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->name, ifa->iface->name,i);
+  DBG("%s: Iface %s can_do_adj=%d\n", p->p.name, ifa->ifname, 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)
-       /* Interface state machine */
 {
-  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 neighbor %I, event '%s'\n", n->ip,
+            ospf_inm[event]);
 
-  switch(event)
+  switch (event)
   {
-    case INM_START:
-      neigh_chstate(n,NEIGHBOR_ATTEMPT);
-      /* FIXME No NBMA now */
-      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 die("NEGDONE and I'm not in EXSTART?\n");
-      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;
-        }
-      break;
-    case INM_SEQMIS:
-    case INM_BADLSREQ:
-      debug("%s: Bad LS req!\n", p->name);
-      if(n->state>=NEIGHBOR_EXCHANGE)
+  case INM_START:
+    neigh_chstate(n, NEIGHBOR_ATTEMPT);
+    /* NBMA are used different way */
+    break;
+
+  case INM_HELLOREC:
+    if ((n->state == NEIGHBOR_DOWN) ||
+       (n->state == NEIGHBOR_ATTEMPT))
+      neigh_chstate(n, NEIGHBOR_INIT);
+
+    /* Restart inactivity timer */
+    tm_start(n->inactim, n->ifa->deadint);
+    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), &p->lsal);
+
+      ospf_reset_lsack_queue(n);
+    }
+    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);
+       neigh_chstate(n, NEIGHBOR_EXSTART);
       }
       break;
-    case INM_KILLNBR:
-    case INM_LLDOWN:
-    case INM_INACTTIM:
-      neigh_chstate(n,NEIGHBOR_DOWN);
-      break;
-    case INM_1WAYREC:
-      neigh_chstate(n,NEIGHBOR_INIT);
-      break;
     default:
-      die("%s: INM - Unknown event?",p->name);
+      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->p.name);
+    break;
   }
 }
 
+/**
+ * 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.
+ */
 void
-bdr_election(struct ospf_iface *ifa, struct proto *p)
+ospf_dr_election(struct ospf_iface *ifa)
 {
-  struct ospf_neighbor *neigh,*ndr,*nbdr,me,*tmp;
-  u32 myid, ndrid, nbdrid;
-  int doadj;
-
-  p=(struct proto *)(ifa->proto);
+  struct ospf_proto *p = ifa->oa->po;
+  struct ospf_neighbor *neigh, *ndr, *nbdr, me;
+  u32 myid = p->router_id;
 
-  DBG("%s: (B)DR election.\n",p->name);
+  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->drid;
-  me.bdr=ifa->bdrid;
-  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;              /* FIXME is this correct? */
+  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->drid=me.dr=0;
-    else ifa->drid=me.dr=ndr->rid;
+    me.dr = ndr ? neigh_get_id(p, ndr) : 0;
+    me.bdr = nbdr ? neigh_get_id(p, nbdr) : 0;
 
-    if(nbdr==NULL) ifa->bdrid=me.bdr=0;
-    else ifa->bdrid=me.bdr=nbdr->rid;
+    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) ifa->drid=0;
-  if(ndr==NULL) ndrid=0;
-  else ndrid=ndr->rid;
+  rem_node(NODE & me);
 
-  if(nbdr==NULL) nbdrid=0;
-  else nbdrid=nbdr->rid;
 
-  doadj=0;
-  if((ifa->drid!=ndrid) || (ifa->bdrid!=nbdrid)) doadj=1;
-  ifa->drid=ndrid;
-  if(ndrid==0)
-  {
-    ifa->drid=0;
-    ifa->drip=ipa_from_u32(0);
-  }
-  else
-  {
-    if((tmp=find_neigh(ifa,ndrid))==NULL)
-      die("Error in DR election.\n");
-    ifa->drid=ndrid;
-    ifa->drip=tmp->ip;
-  }
+  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;
 
-  if(nbdrid==0)
-  {
-    ifa->bdrid=0;
-    ifa->bdrip=ipa_from_u32(0);
-  }
-  else
-  {
-    if((tmp=find_neigh(ifa,nbdrid))==NULL)
-      die("Error in BDR election.\n");
-    ifa->bdrid=nbdrid;
-    ifa->bdrip=tmp->ip;
-  }
+  ifa->bdrid = nbdr ? nbdr->rid : 0;
+  ifa->bdrip = nbdr ? nbdr->ip  : IPA_NONE;
 
-  DBG("%s: DR=%I, BDR=%I\n",p->name, ifa->drid, ifa->bdrid);
+  DBG("DR=%R, BDR=%R\n", ifa->drid, ifa->bdrid);
 
-  if(myid==ifa->drid) iface_chstate(ifa, OSPF_IS_DR);
+  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)
-{
-  struct ospf_area *oa;
-  WALK_LIST(NODE oa,po->area_list)
-    if(((struct ospf_area *)oa)->areaid==aid) return oa;
-  return NULL;
-}
-
 /* Neighbor is inactive for a long time. Remove it. */
-void
-neighbor_timer_hook(timer *timer)
+static void
+neighbor_timer_hook(timer * timer)
 {
-  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);
-  debug("%s: Inactivity timer fired on interface %s for neighbor %I.\n",
-    p->name, ifa->iface->name, n->ip);
+  struct ospf_neighbor *n = (struct ospf_neighbor *) timer->data;
+  struct ospf_iface *ifa = n->ifa;
+  struct ospf_proto *p = ifa->oa->po;
+
+  OSPF_TRACE(D_EVENTS, "Inactivity timer fired on interface %s for neighbor %I",
+            ifa->ifname, n->ip);
   ospf_neigh_remove(n);
 }
 
 void
 ospf_neigh_remove(struct ospf_neighbor *n)
 {
-  struct ospf_iface *ifa;
-  struct proto *p;
+  struct ospf_iface *ifa = n->ifa;
+  struct ospf_proto *p = ifa->oa->po;
 
-  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)
+  if ((ifa->type == OSPF_IT_NBMA) || (ifa->type == OSPF_IT_PTMP))
   {
-    tm_stop(n->rxmt_timer);
-    rfree(n->rxmt_timer);
+    struct nbma_node *nn = find_nbma_node(ifa, n->ip);
+    if (nn)
+      nn->found = 0;
   }
-  if(n->lsrr_timer!=NULL)
+
+  s_get(&(n->dbsi));
+  neigh_chstate(n, NEIGHBOR_DOWN);
+  rem_node(NODE n);
+  rfree(n->pool);
+  OSPF_TRACE(D_EVENTS, "Deleting neigbor %R", n->rid);
+}
+
+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)
   {
-    tm_stop(n->lsrr_timer);
-    rfree(n->lsrr_timer);
+    OSPF_TRACE(D_EVENTS, "BFD session down for %I on %s", n->ip, n->ifa->ifname);
+    ospf_neigh_remove(n);
   }
-  if(n->ackd_timer!=NULL)
+}
+
+void
+ospf_neigh_update_bfd(struct ospf_neighbor *n, int use_bfd)
+{
+  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)
+}
+
+
+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 % 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 (n->rid == ifa->drid)
+    pos = "dr   ";
+  else if (n->rid == ifa->bdrid)
+    pos = "bdr  ";
+  else if ((n->ifa->type == OSPF_IT_PTP) || (n->ifa->type == OSPF_IT_PTMP) ||
+          (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->ifname, n->ip);
+}
+
+static void
+rxmt_timer_hook(timer * timer)
+{
+  struct ospf_neighbor *n = (struct ospf_neighbor *) timer->data;
+  struct ospf_proto *p = n->ifa->oa->po;
+
+  DBG("%s: RXMT timer fired on interface %s for neigh %I\n",
+      p->p.name, n->ifa->ifname, n->ip);
+
+  switch (n->state)
   {
-    ospf_top_free(n->lsrth);
+  case NEIGHBOR_EXSTART:
+    ospf_send_dbdes(n, 1);
+    return;
+
+  case NEIGHBOR_EXCHANGE:
+  if (n->myimms & DBDES_MS)
+    ospf_send_dbdes(n, 0);
+  case NEIGHBOR_LOADING:
+    ospf_send_lsreq(p, n);
+    return;
+
+  case NEIGHBOR_FULL:
+    /* LSA retransmissions */
+    if (!EMPTY_SLIST(n->lsrtl))
+      ospf_rxmt_lsupd(p, n);
+    return;
+
+  default:
+    return;
   }
-  rem_node(NODE n);
-  mb_free(n);
-  debug("%s: Deleting neigbor.\n", p->name);
 }
 
-void
-ospf_sh_neigh_info(struct ospf_neighbor *n)
+static void
+ackd_timer_hook(timer * t)
 {
-   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  ";
-
-
-   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);
+  struct ospf_neighbor *n = t->data;
+  ospf_lsack_send(n, ACKL_DELAY);
 }