]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
OSPF: Support for graceful restart
authorOndrej Zajicek (work) <santiago@crfreenet.org>
Sun, 30 Jun 2019 20:23:57 +0000 (22:23 +0200)
committerOndrej Zajicek (work) <santiago@crfreenet.org>
Sun, 30 Jun 2019 20:23:57 +0000 (22:23 +0200)
Implement OSPFv2 (RFC 3623) and OSPFv3 (RFC 5187) graceful restart,
for both restarting and helper sides. Graceful restart is initiated
by 'graceful down' command.

13 files changed:
proto/ospf/config.Y
proto/ospf/dbdes.c
proto/ospf/iface.c
proto/ospf/lsalib.c
proto/ospf/lsalib.h
proto/ospf/lsupd.c
proto/ospf/neighbor.c
proto/ospf/ospf.c
proto/ospf/ospf.h
proto/ospf/rt.c
proto/ospf/rt.h
proto/ospf/topology.c
proto/ospf/topology.h

index 5672ce364fbd264d565856dcd222315944ccc486..110987619e15eae6a8a73b80019df2721a02b686 100644 (file)
@@ -197,6 +197,7 @@ CF_KEYWORDS(RX, BUFFER, LARGE, NORMAL, STUBNET, HIDDEN, SUMMARY, TAG, EXTERNAL)
 CF_KEYWORDS(WAIT, DELAY, LSADB, ECMP, LIMIT, WEIGHT, NSSA, TRANSLATOR, STABILITY)
 CF_KEYWORDS(GLOBAL, LSID, ROUTER, SELF, INSTANCE, REAL, NETMASK, TX, PRIORITY, LENGTH)
 CF_KEYWORDS(SECONDARY, MERGE, LSA, SUPPRESSION, MULTICAST, RFC5838, VPN, PE)
+CF_KEYWORDS(GRACEFUL, RESTART, AWARE, TIME)
 
 %type <ld> lsadb_args
 %type <i> ospf_variant ospf_af_mc nbma_eligible
@@ -223,6 +224,8 @@ ospf_proto_start: proto_start ospf_variant
   OSPF_CFG->tick = OSPF_DEFAULT_TICK;
   OSPF_CFG->ospf2 = $2;
   OSPF_CFG->af_ext = !$2;
+  OSPF_CFG->gr_mode = OSPF_GR_AWARE;
+  OSPF_CFG->gr_time = OSPF_DEFAULT_GR_TIME;
 };
 
 ospf_proto:
@@ -255,6 +258,9 @@ ospf_proto_item:
  | RFC5838 bool { OSPF_CFG->af_ext = $2; if (!ospf_cfg_is_v3()) cf_error("RFC5838 option requires OSPFv3"); }
  | VPN PE bool { OSPF_CFG->vpn_pe = $3; }
  | STUB ROUTER bool { OSPF_CFG->stub_router = $3; }
+ | GRACEFUL RESTART bool { OSPF_CFG->gr_mode = $3; }
+ | GRACEFUL RESTART AWARE { OSPF_CFG->gr_mode = OSPF_GR_AWARE; }
+ | GRACEFUL RESTART TIME expr { OSPF_CFG->gr_time = $4; if (($4 < 1) || ($4 > 1800)) cf_error("Graceful restart time must be in range 1-1800"); }
  | ECMP bool { OSPF_CFG->ecmp = $2 ? OSPF_DEFAULT_ECMP_LIMIT : 0; }
  | ECMP bool LIMIT expr { OSPF_CFG->ecmp = $2 ? $4 : 0; }
  | MERGE EXTERNAL bool { OSPF_CFG->merge_external = $3; }
index f0873da3f1f603762c1bb64a5ba820e0fde7277a..463ad6de46d14e9c0ac71a6768d7afff7528638f 100644 (file)
@@ -209,7 +209,7 @@ ospf_send_dbdes(struct ospf_proto *p, struct ospf_neighbor *n)
 
   ASSERT((n->state == NEIGHBOR_EXSTART) || (n->state == NEIGHBOR_EXCHANGE));
 
-  if (n->ifa->oa->rt == NULL)
+  if (!n->ifa->oa->rt && !p->gr_recovery)
     return;
 
   ospf_prepare_dbdes(p, n);
@@ -277,6 +277,10 @@ ospf_process_dbdes(struct ospf_proto *p, struct ospf_packet *pkt, struct ospf_ne
     if (LSA_SCOPE(lsa_type) == LSA_SCOPE_RES)
       DROP1(bad_dbdes, "LSA with invalid scope");
 
+    /* RFC 3623 2.2 (2) special case - check for my router-LSA (GR recovery) */
+    if ((lsa_type == LSA_T_RT) && (lsa.rt == p->router_id))
+      n->got_my_rt_lsa = 1;
+
     en = ospf_hash_find(p->gr, lsa_domain, lsa.id, lsa.rt, lsa_type);
     if (!en || (lsa_comp(&lsa, &(en->lsa)) == CMP_NEWER))
     {
index f7961514020ad54b182ce8b1e26e703d6baeaaa3..09b43d9fabccd38ccfd1fe685f60aa53cf7345b1 100644 (file)
@@ -765,6 +765,14 @@ ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new)
   ifa->cf = new;
   ifa->marked = 0;
 
+  /* Cancel GR peers if GR is disabled */
+  if (!p->gr_mode && p->gr_count)
+  {
+    struct ospf_neighbor *n, *nx;
+    WALK_LIST_DELSAFE(n, nx, ifa->neigh_list)
+      if (n->gr_active)
+       ospf_neigh_cancel_graceful_restart(n);
+  }
 
   /* HELLO TIMER */
   if (ifa->helloint != new->helloint)
index 7ddf64e3c9353ba4574d643d97fdec505e9c73c2..7767700f8a7ddffdbfd62f79a5853d40776eac20 100644 (file)
@@ -12,6 +12,9 @@
 
 #include "lib/fletcher16.h"
 
+#define HDRLEN sizeof(struct ospf_lsa_header)
+
+
 #ifndef CPU_BIG_ENDIAN
 void
 lsa_hton_hdr(struct ospf_lsa_header *h, struct ospf_lsa_header *n)
@@ -61,7 +64,6 @@ lsa_ntoh_body(void *n, void *h, u16 len)
 #endif /* little endian */
 
 
-
 int
 lsa_flooding_allowed(u32 type, u32 domain, struct ospf_iface *ifa)
 {
@@ -147,11 +149,13 @@ static const u16 lsa_v2_types[] = {
 
 /* Maps OSPFv2 opaque types to OSPFv3 function codes */
 static const u16 opaque_lsa_types[] = {
+  [LSA_OT_GR] = LSA_T_GR,
   [LSA_OT_RI] = LSA_T_RI_,
 };
 
 /* Maps (subset of) OSPFv3 function codes to OSPFv2 opaque types */
 static const u8 opaque_lsa_types_inv[] = {
+  [LSA_T_GR] = LSA_OT_GR,
   [LSA_T_RI_] = LSA_OT_RI,
 };
 
@@ -168,7 +172,13 @@ lsa_get_type_domain_(u32 type, u32 id, struct ospf_iface *ifa, u32 *otype, u32 *
     uint code;
     if (LSA_FUNCTION(type) == LSA_T_OPAQUE_)
       if (code = LOOKUP(opaque_lsa_types, id >> 24))
+      {
        type = code | LSA_UBIT | LSA_SCOPE(type);
+
+       /* Hack for Grace-LSA: It does not use U-bit for link-scoped LSAs */
+       if (type == (LSA_T_GR | LSA_UBIT))
+         type = LSA_T_GR;
+      }
   }
   else
   {
@@ -196,6 +206,13 @@ lsa_get_type_domain_(u32 type, u32 id, struct ospf_iface *ifa, u32 *otype, u32 *
   }
 }
 
+int
+lsa_is_opaque(u32 type)
+{
+  u32 fn = LSA_FUNCTION(type);
+  return LOOKUP(opaque_lsa_types_inv, fn) || (fn == LSA_T_OPAQUE_);
+}
+
 u32
 lsa_get_opaque_type(u32 type)
 {
@@ -267,6 +284,51 @@ lsa_comp(struct ospf_lsa_header *l1, struct ospf_lsa_header *l2)
 }
 
 
+#define LSA_TLV_LENGTH(tlv) \
+  (sizeof(struct ospf_tlv) + BIRD_ALIGN((tlv)->length, 4))
+
+#define LSA_NEXT_TLV(tlv) \
+  ((struct ospf_tlv *) ((byte *) (tlv) + LSA_TLV_LENGTH(tlv)))
+
+#define LSA_WALK_TLVS(tlv,buf,len)                                     \
+  for(struct ospf_tlv *tlv = (void *) (buf);                           \
+      (byte *) tlv < (byte *) (buf) + (len);                           \
+      tlv = LSA_NEXT_TLV(tlv))
+
+struct ospf_tlv *
+lsa_get_tlv(struct top_hash_entry *en, uint type)
+{
+  LSA_WALK_TLVS(tlv, en->lsa_body, en->lsa.length - HDRLEN)
+    if (tlv->type == type)
+      return tlv;
+
+  return NULL;
+}
+
+int
+lsa_validate_tlvs(byte *buf, uint len)
+{
+  byte *pos = buf;
+  byte *end = buf + len;
+
+  while (pos < end)
+  {
+    if ((pos + sizeof(struct ospf_tlv)) > end)
+      return 0;
+
+    struct ospf_tlv *tlv = (void *) pos;
+    uint len = LSA_TLV_LENGTH(tlv);
+
+    if ((pos + len) > end)
+      return 0;
+
+    pos += len;
+  }
+
+  return 1;
+}
+
+
 static inline int
 lsa_walk_rt2(struct ospf_lsa_rt_walk *rt)
 {
@@ -408,7 +470,6 @@ lsa_parse_ext(struct top_hash_entry *en, int ospf2, int af, struct ospf_lsa_ext_
   }
 }
 
-#define HDRLEN sizeof(struct ospf_lsa_header)
 
 static int
 lsa_validate_rt2(struct ospf_lsa_header *lsa, struct ospf_lsa_rt *body)
@@ -603,6 +664,12 @@ lsa_validate_prefix(struct ospf_lsa_header *lsa, struct ospf_lsa_prefix *body)
   return lsa_validate_pxlist(lsa, body->pxcount, sizeof(struct ospf_lsa_prefix), (u8 *) body);
 }
 
+static int
+lsa_validate_gr(struct ospf_lsa_header *lsa, void *body)
+{
+  return lsa_validate_tlvs(body, lsa->length - HDRLEN);
+}
+
 static int
 lsa_validate_ri(struct ospf_lsa_header *lsa UNUSED, struct ospf_lsa_net *body UNUSED)
 {
@@ -643,6 +710,8 @@ lsa_validate(struct ospf_lsa_header *lsa, u32 lsa_type, int ospf2, void *body)
     case LSA_T_EXT:
     case LSA_T_NSSA:
       return lsa_validate_ext2(lsa, body);
+    case LSA_T_GR:
+      return lsa_validate_gr(lsa, body);
     case LSA_T_RI_LINK:
     case LSA_T_RI_AREA:
     case LSA_T_RI_AS:
@@ -674,6 +743,8 @@ lsa_validate(struct ospf_lsa_header *lsa, u32 lsa_type, int ospf2, void *body)
       return lsa_validate_link(lsa, body);
     case LSA_T_PREFIX:
       return lsa_validate_prefix(lsa, body);
+    case LSA_T_GR:
+      return lsa_validate_gr(lsa, body);
     case LSA_T_RI_LINK:
     case LSA_T_RI_AREA:
     case LSA_T_RI_AS:
index af8901cebe17789f9ccf92f3bd7ba9dd013e20d0..eca138d7e32cc5fafde90b5243b6c18f79c561ad 100644 (file)
@@ -44,10 +44,7 @@ static inline void lsa_get_type_domain(struct ospf_lsa_header *lsa, struct ospf_
 static inline u32 lsa_get_etype(struct ospf_lsa_header *h, struct ospf_proto *p)
 { return ospf_is_v2(p) ? (h->type_raw & LSA_T_V2_MASK) : h->type_raw; }
 
-/* Assuming OSPFv2 - All U-bit LSAs are mapped to Opaque LSAs */
-static inline int lsa_is_opaque(u32 type)
-{ return !!(type & LSA_UBIT); }
-
+int lsa_is_opaque(u32 type);
 u32 lsa_get_opaque_type(u32 type);
 int lsa_flooding_allowed(u32 type, u32 domain, struct ospf_iface *ifa);
 int lsa_is_acceptable(u32 type, struct ospf_neighbor *n, struct ospf_proto *p);
@@ -58,6 +55,16 @@ u16 lsa_verify_checksum(const void *lsa_n, int lsa_len);
 #define CMP_SAME 0
 #define CMP_OLDER -1
 int lsa_comp(struct ospf_lsa_header *l1, struct ospf_lsa_header *l2);
+
+struct ospf_tlv * lsa_get_tlv(struct top_hash_entry *en, uint type);
+
+static inline u32
+lsa_get_tlv_u32(struct top_hash_entry *en, uint type)
+{
+  struct ospf_tlv *tlv = lsa_get_tlv(en, type);
+  return (tlv && (tlv->length == 4)) ? tlv->data[0] : 0;
+}
+
 void lsa_walk_rt_init(struct ospf_proto *po, struct top_hash_entry *act, struct ospf_lsa_rt_walk *rt);
 int lsa_walk_rt(struct ospf_lsa_rt_walk *rt);
 void lsa_parse_sum_net(struct top_hash_entry *en, int ospf2, int af, net_addr *net, u8 *pxopts, u32 *metric);
index 36be7f55de07e48006d87cc8d4c7ed2b28b8e5c7..7a0897909e03a48630b04baad77301bb59a75f6a 100644 (file)
@@ -187,6 +187,13 @@ static int ospf_flood_lsupd(struct ospf_proto *p, struct top_hash_entry **lsa_li
 static void
 ospf_enqueue_lsa(struct ospf_proto *p, struct top_hash_entry *en, struct ospf_iface *ifa)
 {
+  /* Exception for local Grace-LSA, they are flooded synchronously */
+  if ((en->lsa_type == LSA_T_GR) && (en->lsa.rt == p->router_id))
+  {
+    ospf_flood_lsupd(p, &en, 1, 1, ifa);
+    return;
+  }
+
   if (ifa->flood_queue_used == ifa->flood_queue_size)
   {
     /* If we already have full queue, we send some packets */
@@ -596,8 +603,9 @@ ospf_receive_lsupd(struct ospf_packet *pkt, struct ospf_iface *ifa,
       }
 
       /* 13. (5f) - handle self-originated LSAs, see also 13.4. */
-      if ((lsa.rt == p->router_id) ||
-         (ospf_is_v2(p) && (lsa_type == LSA_T_NET) && ospf_addr_is_local(p, ifa->oa, ipa_from_u32(lsa.id))))
+      if (!p->gr_recovery &&
+         ((lsa.rt == p->router_id) ||
+          (ospf_is_v2(p) && (lsa_type == LSA_T_NET) && ospf_addr_is_local(p, ifa->oa, ipa_from_u32(lsa.id)))))
       {
        OSPF_TRACE(D_EVENTS, "Received unexpected self-originated LSA");
        ospf_advance_lsa(p, en, &lsa, lsa_type, lsa_domain, body);
@@ -634,6 +642,14 @@ ospf_receive_lsupd(struct ospf_packet *pkt, struct ospf_iface *ifa,
       if (lsa_type == LSA_T_LINK)
        ospf_notify_net_lsa(ifa);
 
+      /* RFC 3623 3.1 - entering graceful restart helper mode */
+      if (lsa_type == LSA_T_GR)
+       ospf_neigh_notify_grace_lsa(n, en);
+
+      /* Link received pre-restart router LSA */
+      if (p->gr_recovery && (lsa_type == LSA_T_RT) && (lsa.rt == p->router_id))
+       ifa->oa->rt = en;
+
       /* 13. (5b) - flood new LSA */
       int flood_back = ospf_flood_lsa(p, en, n);
 
index 6d2f1fb2e401a745bade894165ee43b3a4359c53..f40a4afa843389bb66432d947e8371aabfb26961 100644 (file)
@@ -28,6 +28,8 @@ 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 ospf_neigh_stop_graceful_restart_(struct ospf_neighbor *n);
+static void graceful_restart_timeout(timer *t);
 
 
 static void
@@ -165,7 +167,7 @@ ospf_neigh_chstate(struct ospf_neighbor *n, u8 state)
   if (old_state == NEIGHBOR_FULL)
     ifa->fadj--;
 
-  if (ifa->fadj != old_fadj)
+  if ((ifa->fadj != old_fadj) && !n->gr_active)
   {
     /* RFC 2328 12.4 Event 4 - neighbor enters/leaves Full state */
     ospf_notify_rt_lsa(ifa->oa);
@@ -184,6 +186,7 @@ ospf_neigh_chstate(struct ospf_neighbor *n, u8 state)
 
     n->dds++;
     n->myimms = DBDES_IMMS;
+    n->got_my_rt_lsa = 0;
 
     tm_start(n->dbdes_timer, 0);
     tm_start(n->ackd_timer, ifa->rxmtint S / 2);
@@ -193,9 +196,9 @@ ospf_neigh_chstate(struct ospf_neighbor *n, u8 state)
     n->myimms &= ~DBDES_I;
 
   /* Generate NeighborChange event if needed, see RFC 2328 9.2 */
-  if ((state == NEIGHBOR_2WAY) && (old_state < NEIGHBOR_2WAY))
+  if ((state == NEIGHBOR_2WAY) && (old_state < NEIGHBOR_2WAY) && !n->gr_active)
     ospf_iface_sm(ifa, ISM_NEICH);
-  if ((state < NEIGHBOR_2WAY) && (old_state >= NEIGHBOR_2WAY))
+  if ((state < NEIGHBOR_2WAY) && (old_state >= NEIGHBOR_2WAY) && !n->gr_active)
     ospf_iface_sm(ifa, ISM_NEICH);
 }
 
@@ -293,6 +296,17 @@ ospf_neigh_sm(struct ospf_neighbor *n, int event)
   case INM_KILLNBR:
   case INM_LLDOWN:
   case INM_INACTTIM:
+    if (n->gr_active && (event == INM_INACTTIM))
+    {
+      /* Just down the neighbor, but do not remove it */
+      reset_lists(p, n);
+      ospf_neigh_chstate(n, NEIGHBOR_DOWN);
+      break;
+    }
+
+    if (n->gr_active)
+      ospf_neigh_stop_graceful_restart_(n);
+
     /* No need for reset_lists() */
     ospf_neigh_chstate(n, NEIGHBOR_DOWN);
     ospf_neigh_down(n);
@@ -358,6 +372,180 @@ can_do_adj(struct ospf_neighbor *n)
   return i;
 }
 
+static void
+ospf_neigh_start_graceful_restart(struct ospf_neighbor *n, uint gr_time)
+{
+  struct ospf_proto *p = n->ifa->oa->po;
+
+  OSPF_TRACE(D_EVENTS, "Neighbor %R on %s started graceful restart",
+            n->rid, n->ifa->ifname);
+
+  n->gr_active = 1;
+  p->gr_count++;
+
+  n->gr_timer = tm_new_init(n->pool, graceful_restart_timeout, n, 0, 0);
+  tm_start(n->gr_timer, gr_time S);
+}
+
+static void
+ospf_neigh_stop_graceful_restart_(struct ospf_neighbor *n)
+{
+  struct ospf_proto *p = n->ifa->oa->po;
+  struct ospf_iface *ifa = n->ifa;
+
+  n->gr_active = 0;
+  p->gr_count--;
+
+  rfree(n->gr_timer);
+  n->gr_timer = NULL;
+
+  ospf_notify_rt_lsa(ifa->oa);
+  ospf_notify_net_lsa(ifa);
+
+  if (ifa->type == OSPF_IT_VLINK)
+    ospf_notify_rt_lsa(ifa->voa);
+
+  ospf_iface_sm(ifa, ISM_NEICH);
+}
+
+static void
+ospf_neigh_stop_graceful_restart(struct ospf_neighbor *n)
+{
+  struct ospf_proto *p = n->ifa->oa->po;
+
+  OSPF_TRACE(D_EVENTS, "Neighbor %R on %s finished graceful restart",
+            n->rid, n->ifa->ifname);
+
+  ospf_neigh_stop_graceful_restart_(n);
+}
+
+void
+ospf_neigh_cancel_graceful_restart(struct ospf_neighbor *n)
+{
+  struct ospf_proto *p = n->ifa->oa->po;
+
+  OSPF_TRACE(D_EVENTS, "Graceful restart canceled for nbr %R on %s",
+            n->rid, n->ifa->ifname);
+
+  ospf_neigh_stop_graceful_restart_(n);
+
+  if (n->state == NEIGHBOR_DOWN)
+    ospf_neigh_down(n);
+}
+
+static void
+graceful_restart_timeout(timer *t)
+{
+  struct ospf_neighbor *n = t->data;
+  struct ospf_proto *p = n->ifa->oa->po;
+
+  OSPF_TRACE(D_EVENTS, "Graceful restart timer expired for nbr %R on %s",
+            n->rid, n->ifa->ifname);
+
+  ospf_neigh_stop_graceful_restart_(n);
+
+  if (n->state == NEIGHBOR_DOWN)
+    ospf_neigh_down(n);
+}
+
+static inline int
+changes_in_lsrtl(struct ospf_neighbor *n)
+{
+  /* This could be improved, see RFC 3623 3.1 (2) */
+
+  struct top_hash_entry *en;
+  WALK_SLIST(en, n->lsrtl)
+    if (LSA_FUNCTION(en->lsa_type) <= LSA_FUNCTION(LSA_T_NSSA))
+      return 1;
+
+  return 0;
+}
+
+void
+ospf_neigh_notify_grace_lsa(struct ospf_neighbor *n, struct top_hash_entry *en)
+{
+  struct ospf_iface *ifa = n->ifa;
+  struct ospf_proto *p = ifa->oa->po;
+
+  /* In OSPFv2, neighbors are identified by either IP or Router ID, based on network type */
+  uint t = ifa->type;
+  if (ospf_is_v2(p) && ((t == OSPF_IT_BCAST) || (t == OSPF_IT_NBMA) || (t == OSPF_IT_PTMP)))
+  {
+    struct ospf_tlv *tlv = lsa_get_tlv(en, LSA_GR_ADDRESS);
+    if (!tlv || tlv->length != 4)
+      return;
+
+    ip_addr addr = ipa_from_u32(tlv->data[0]);
+    if (!ipa_equal(n->ip, addr))
+      n = find_neigh_by_ip(ifa, addr);
+  }
+  else
+  {
+    if (n->rid != en->lsa.rt)
+      n = find_neigh(ifa, en->lsa.rt);
+  }
+
+  if (!n)
+    return;
+
+  if (en->lsa.age < LSA_MAXAGE)
+  {
+    u32 period = lsa_get_tlv_u32(en, LSA_GR_PERIOD);
+
+    /* Exception for updating grace period */
+    if (n->gr_active)
+    {
+      tm_start(n->gr_timer, (period S) - (en->lsa.age S));
+      return;
+    }
+
+    /* RFC 3623 3.1 (1) - full adjacency */
+    if (n->state != NEIGHBOR_FULL)
+      return;
+
+    /* RFC 3623 3.1 (2) - no changes in LSADB */
+    if (changes_in_lsrtl(n))
+      return;
+
+    /* RFC 3623 3.1 (3) - grace period not expired */
+    if (en->lsa.age >= period)
+      return;
+
+    /* RFC 3623 3.1 (4) - helper mode allowed */
+    if (!p->gr_mode)
+      return;
+
+    /* RFC 3623 3.1 (5) - no local graceful restart */
+    if (p->p.gr_recovery)
+      return;
+
+    ospf_neigh_start_graceful_restart(n, period - en->lsa.age);
+  }
+  else /* Grace-LSA is flushed */
+  {
+    if (n->gr_active)
+      ospf_neigh_stop_graceful_restart(n);
+  }
+}
+
+void
+ospf_neigh_lsadb_changed_(struct ospf_proto *p, struct top_hash_entry *en)
+{
+  struct ospf_iface *ifa;
+  struct ospf_neighbor *n, *nx;
+
+  if (LSA_FUNCTION(en->lsa_type) > LSA_FUNCTION(LSA_T_NSSA))
+    return;
+
+  /* RFC 3623 3.2 (3) - cancel graceful restart when LSdb changed */
+  WALK_LIST(ifa, p->iface_list)
+    if (lsa_flooding_allowed(en->lsa_type, en->domain, ifa))
+      WALK_LIST_DELSAFE(n, nx, ifa->neigh_list)
+       if (n->gr_active)
+         ospf_neigh_cancel_graceful_restart(n);
+}
+
+
 
 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; }
index 2e5e8affbd3a62b1fe6890f7adfcda4b76f7e750..5b3f286fdd199a410c43ddd2a0fb591bad164b4c 100644 (file)
@@ -92,7 +92,9 @@
  * - RFC 2328 - main OSPFv2 standard
  * - RFC 5340 - main OSPFv3 standard
  * - RFC 3101 - OSPFv2 NSSA areas
+ * - RFC 3623 - OSPFv2 Graceful Restart
  * - RFC 4576 - OSPFv2 VPN loop prevention
+ * - RFC 5187 - OSPFv3 Graceful Restart
  * - RFC 5250 - OSPFv2 Opaque LSAs
  * - RFC 5709 - OSPFv2 HMAC-SHA Cryptographic Authentication
  * - RFC 5838 - OSPFv3 Support of Address Families
@@ -206,7 +208,6 @@ ospf_area_remove(struct ospf_area *oa)
   mb_free(oa);
 }
 
-
 struct ospf_area *
 ospf_find_area(struct ospf_proto *p, u32 aid)
 {
@@ -227,6 +228,37 @@ ospf_find_vlink(struct ospf_proto *p, u32 voa, u32 vid)
   return NULL;
 }
 
+static void
+ospf_start_gr_recovery(struct ospf_proto *p)
+{
+  OSPF_TRACE(D_EVENTS, "Graceful restart started");
+
+  p->gr_recovery = 1;
+  p->gr_timeout = current_time() + (p->gr_time S);
+  channel_graceful_restart_lock(p->p.main_channel);
+  p->p.main_channel->gr_wait = 1;
+
+  /* NOTE: We should get end of grace period from non-volatile storage */
+}
+
+void
+ospf_stop_gr_recovery(struct ospf_proto *p)
+{
+  p->gr_recovery = 0;
+  p->gr_timeout = 0;
+  channel_graceful_restart_unlock(p->p.main_channel);
+
+  /* Reorigination of router/network LSAs is already scheduled */
+  ospf_mark_lsadb(p);
+
+  /*
+   * NOTE: We should move channel_graceful_restart_unlock() to the end of
+   * ospf_disp() in order to have local LSA reorigination / LSAdb cleanup /
+   * routing table recomputation before official end of GR. It does not matter
+   * when we are single-threaded.
+   */
+}
+
 static int
 ospf_start(struct proto *P)
 {
@@ -245,6 +277,8 @@ ospf_start(struct proto *P)
   p->asbr = c->asbr;
   p->vpn_pe = c->vpn_pe;
   p->ecmp = c->ecmp;
+  p->gr_mode = c->gr_mode;
+  p->gr_time = c->gr_time;
   p->tick = c->tick;
   p->disp_timer = tm_new_init(P->pool, ospf_disp, p, p->tick S, 0);
   tm_start(p->disp_timer, 100 MS);
@@ -271,6 +305,10 @@ ospf_start(struct proto *P)
   p->log_pkt_tbf = (struct tbf){ .rate = 1, .burst = 5 };
   p->log_lsa_tbf = (struct tbf){ .rate = 4, .burst = 20 };
 
+  /* Lock the channel when in GR recovery mode */
+  if (p->p.gr_recovery && (p->gr_mode == OSPF_GR_ABLE))
+    ospf_start_gr_recovery(p);
+
   WALK_LIST(ac, c->area_list)
     ospf_area_add(p, ac);
 
@@ -431,6 +469,9 @@ ospf_disp(timer * timer)
 {
   struct ospf_proto *p = timer->data;
 
+  if (p->gr_recovery)
+    ospf_update_gr_recovery(p);
+
   /* Originate or flush local topology LSAs */
   ospf_update_topology(p);
 
@@ -516,9 +557,18 @@ ospf_shutdown(struct proto *P)
 
   OSPF_TRACE(D_EVENTS, "Shutdown requested");
 
-  /* And send to all my neighbors 1WAY */
-  WALK_LIST(ifa, p->iface_list)
-    ospf_iface_shutdown(ifa);
+  if ((P->down_code == PDC_CMD_GR_DOWN) && (p->gr_mode == OSPF_GR_ABLE))
+  {
+    /* Originate Grace LSAs */
+    WALK_LIST(ifa, p->iface_list)
+      ospf_originate_gr_lsa(p, ifa);
+  }
+  else
+  {
+    /* Send to all my neighbors 1WAY */
+    WALK_LIST(ifa, p->iface_list)
+      ospf_iface_shutdown(ifa);
+  }
 
   /* Cleanup locked rta entries */
   FIB_WALK(&p->rtf, ort, nf)
@@ -705,6 +755,8 @@ ospf_reconfigure(struct proto *P, struct proto_config *CF)
   p->merge_external = new->merge_external;
   p->asbr = new->asbr;
   p->ecmp = new->ecmp;
+  p->gr_mode = new->gr_mode;
+  p->gr_time = new->gr_time;
   p->tick = new->tick;
   p->disp_timer->recurrent = p->tick S;
   tm_start(p->disp_timer, 10 MS);
index a57b84f82df4ac2b9cd295b0b12548e8c55a279d..7795a589da72b32cc12a1261157d7f11fea342a8 100644 (file)
@@ -74,6 +74,7 @@
 #define OSPF_DEFAULT_TICK 1
 #define OSPF_DEFAULT_STUB_COST 1000
 #define OSPF_DEFAULT_ECMP_LIMIT 16
+#define OSPF_DEFAULT_GR_TIME 120
 #define OSPF_DEFAULT_TRANSINT 40
 
 #define OSPF_MIN_PKT_SIZE 256
@@ -81,6 +82,9 @@
 
 #define OSPF_VLINK_ID_OFFSET 0x80000000
 
+#define OSPF_GR_ABLE           1
+#define OSPF_GR_AWARE          2
+
 struct ospf_config
 {
   struct proto_config c;
@@ -96,7 +100,9 @@ struct ospf_config
   u8 abr;
   u8 asbr;
   u8 vpn_pe;
-  int ecmp;
+  u8 gr_mode;                  /* Graceful restart mode (OSPF_GR_*) */
+  uint gr_time;                        /* Graceful restart interval */
+  uint ecmp;
   list area_list;              /* list of area configs (struct ospf_area_config) */
   list vlink_list;             /* list of configured vlinks (struct ospf_iface_patt) */
 };
@@ -223,6 +229,9 @@ struct ospf_proto
   list area_list;              /* List of OSPF areas (struct ospf_area) */
   int areano;                  /* Number of area I belong to */
   int padj;                    /* Number of neighbors in Exchange or Loading state */
+  int gr_count;                        /* Number of neighbors in graceful restart state */
+  int gr_recovery;             /* Graceful restart recovery is active */
+  btime gr_timeout;            /* The end time of grace restart recovery */
   struct fib rtf;              /* Routing table */
   struct idm idm;              /* OSPFv3 LSA ID map */
   u8 ospf2;                    /* OSPF v2 or v3 */
@@ -235,6 +244,8 @@ struct ospf_proto
   u8 asbr;                     /* May i originate any ext/NSSA lsa? */
   u8 vpn_pe;                   /* Should we do VPN PE specific behavior (RFC 4577)? */
   u8 ecmp;                     /* Maximal number of nexthops in ECMP route, or 0 */
+  u8 gr_mode;                  /* Graceful restart mode (OSPF_GR_*) */
+  uint gr_time;                        /* Graceful restart interval */
   struct ospf_area *backbone;  /* If exists */
   event *flood_event;          /* Event for flooding LS updates */
   void *lsab;                  /* LSA buffer used when originating router LSAs */
@@ -357,6 +368,8 @@ struct ospf_neighbor
   pool *pool;
   struct ospf_iface *ifa;
   u8 state;
+  u8 gr_active;                        /* We act as GR helper for the neighbor */
+  u8 got_my_rt_lsa;            /* Received my Rt-LSA in DBDES exchanged */
   timer *inactim;              /* Inactivity timer */
   u8 imms;                     /* I, M, Master/slave received */
   u8 myimms;                   /* I, M Master/slave */
@@ -400,6 +413,7 @@ struct ospf_neighbor
 #define ACKL_DIRECT 0
 #define ACKL_DELAY 1
   timer *ackd_timer;           /* Delayed ack timer */
+  timer *gr_timer;             /* Graceful restart timer, non-NULL only if gr_active */
   struct bfd_request *bfd_req; /* BFD request, if BFD is used */
   void *ldd_buffer;            /* Last database description packet */
   u32 ldd_bsize;               /* Buffer size for ldd_buffer */
@@ -545,6 +559,7 @@ union ospf_auth
 #define LSA_T_NSSA             0x2007
 #define LSA_T_LINK             0x0008
 #define LSA_T_PREFIX           0x2009
+#define LSA_T_GR               0x000B
 #define LSA_T_RI_              0x000C
 #define LSA_T_RI_LINK          0x800C
 #define LSA_T_RI_AREA          0xA00C
@@ -559,6 +574,7 @@ union ospf_auth
 
 /* OSPFv2 Opaque LSA Types */
 /* https://www.iana.org/assignments/ospf-opaque-types/ospf-opaque-types.xhtml#ospf-opaque-types-2 */
+#define LSA_OT_GR              0x03
 #define LSA_OT_RI              0x04
 
 #define LSA_FUNCTION_MASK      0x1FFF
@@ -603,6 +619,12 @@ union ospf_auth
 #define LSA_EXT3_FBIT  0x02000000
 #define LSA_EXT3_TBIT  0x01000000
 
+/* OSPF Grace LSA (GR) TLVs */
+/* https://www.iana.org/assignments/ospfv2-parameters/ospfv2-parameters.xhtml#ospfv2-parameters-13 */
+#define LSA_GR_PERIOD          1
+#define LSA_GR_REASON          2
+#define LSA_GR_ADDRESS         3
+
 /* OSPF Router Information (RI) TLVs */
 /* https://www.iana.org/assignments/ospf-parameters/ospf-parameters.xhtml#ri-tlv */
 #define LSA_RI_RIC             1
@@ -949,6 +971,8 @@ static inline int oa_is_ext(struct ospf_area *oa)
 static inline int oa_is_nssa(struct ospf_area *oa)
 { return oa->options & OPT_N; }
 
+void ospf_stop_gr_recovery(struct ospf_proto *p);
+
 void ospf_sh_neigh(struct proto *P, char *iff);
 void ospf_sh(struct proto *P);
 void ospf_sh_iface(struct proto *P, char *iff);
@@ -981,12 +1005,18 @@ static inline struct nbma_node * find_nbma_node(struct ospf_iface *ifa, ip_addr
 /* neighbor.c */
 struct ospf_neighbor *ospf_neighbor_new(struct ospf_iface *ifa);
 void ospf_neigh_sm(struct ospf_neighbor *n, int event);
+void ospf_neigh_cancel_graceful_restart(struct ospf_neighbor *n);
+void ospf_neigh_notify_grace_lsa(struct ospf_neighbor *n, struct top_hash_entry *en);
+void ospf_neigh_lsadb_changed_(struct ospf_proto *p, struct top_hash_entry *en);
 void ospf_dr_election(struct ospf_iface *ifa);
 struct ospf_neighbor *find_neigh(struct ospf_iface *ifa, u32 rid);
 struct ospf_neighbor *find_neigh_by_ip(struct ospf_iface *ifa, ip_addr ip);
 void ospf_neigh_update_bfd(struct ospf_neighbor *n, int use_bfd);
 void ospf_sh_neigh_info(struct ospf_neighbor *n);
 
+static inline void ospf_neigh_lsadb_changed(struct ospf_proto *p, struct top_hash_entry *en)
+{ if (p->gr_count) ospf_neigh_lsadb_changed_(p, en); }
+
 /* packet.c */
 void ospf_pkt_fill_hdr(struct ospf_iface *ifa, void *buf, u8 h_type);
 int ospf_rx_hook(sock * sk, uint size);
index 42b0abde6381b5a559b8e20799baae99af863861..766e1265319b9f04509617be979c250cc46ed30f 100644 (file)
@@ -10,7 +10,7 @@
 
 #include "ospf.h"
 
-static void add_cand(struct ospf_area *oa, struct top_hash_entry *en, struct top_hash_entry *par, u32 dist, int i, uint lif, uint nif);
+static void add_cand(struct ospf_area *oa, struct top_hash_entry *en, struct top_hash_entry *par, u32 dist, int i, uint data, uint lif, uint nif);
 static void rt_sync(struct ospf_proto *p);
 
 
@@ -392,6 +392,40 @@ px_pos_to_ifa(struct ospf_area *oa, int pos)
   return NULL;
 }
 
+static inline struct ospf_iface *
+rt_find_iface2(struct ospf_area *oa, uint data)
+{
+  ip_addr addr = ipa_from_u32(data);
+
+  /* We should handle it differently for unnumbered PTP links */
+  struct ospf_iface *ifa;
+  WALK_LIST(ifa, oa->po->iface_list)
+    if ((ifa->oa == oa) && ifa->addr && (ipa_equal(ifa->addr->ip, addr)))
+      return ifa;
+
+  return NULL;
+}
+
+static inline struct ospf_iface *
+rt_find_iface3(struct ospf_area *oa, uint lif)
+{
+  struct ospf_iface *ifa;
+  WALK_LIST(ifa, oa->po->iface_list)
+    if ((ifa->oa == oa) && (ifa->iface_id == lif))
+      return ifa;
+
+  return NULL;
+}
+
+static struct ospf_iface *
+rt_find_iface(struct ospf_area *oa, int pos, uint data, uint lif)
+{
+  if (0)
+    return rt_pos_to_ifa(oa, pos);
+  else
+    return ospf_is_v2(oa->po) ? rt_find_iface2(oa, data) : rt_find_iface3(oa, lif);
+}
+
 
 static void
 add_network(struct ospf_area *oa, net_addr *net, int metric, struct top_hash_entry *en, int pos)
@@ -507,7 +541,7 @@ spfa_process_rt(struct ospf_proto *p, struct ospf_area *oa, struct top_hash_entr
       break;
     }
 
-    add_cand(oa, tmp, act, act->dist + rtl.metric, i, rtl.lif, rtl.nif);
+    add_cand(oa, tmp, act, act->dist + rtl.metric, i, rtl.data, rtl.lif, rtl.nif);
   }
 }
 
@@ -530,7 +564,7 @@ spfa_process_net(struct ospf_proto *p, struct ospf_area *oa, struct top_hash_ent
   for (i = 0; i < cnt; i++)
   {
     tmp = ospf_hash_find_rt(p->gr, oa->areaid, ln->routers[i]);
-    add_cand(oa, tmp, act, act->dist, -1, 0, 0);
+    add_cand(oa, tmp, act, act->dist, -1, 0, 0, 0);
   }
 }
 
@@ -1715,7 +1749,7 @@ link_lsa_lladdr(struct ospf_proto *p, struct top_hash_entry *en)
 
 static struct nexthop *
 calc_next_hop(struct ospf_area *oa, struct top_hash_entry *en,
-             struct top_hash_entry *par, int pos, uint lif, uint nif)
+             struct top_hash_entry *par, int pos, uint data, uint lif, uint nif)
 {
   struct ospf_proto *p = oa->po;
   struct nexthop *pn = par->nhs;
@@ -1742,7 +1776,7 @@ calc_next_hop(struct ospf_area *oa, struct top_hash_entry *en,
   /* The first case - local network */
   if ((en->lsa_type == LSA_T_NET) && (par == oa->rt))
   {
-    ifa = rt_pos_to_ifa(oa, pos);
+    ifa = rt_find_iface(oa, pos, data, lif);
     if (!ifa)
       return NULL;
 
@@ -1755,7 +1789,7 @@ calc_next_hop(struct ospf_area *oa, struct top_hash_entry *en,
   /* The second case - ptp or ptmp neighbor */
   if ((en->lsa_type == LSA_T_RT) && (par == oa->rt))
   {
-    ifa = rt_pos_to_ifa(oa, pos);
+    ifa = rt_find_iface(oa, pos, data, lif);
     if (!ifa)
       return NULL;
 
@@ -1845,7 +1879,7 @@ calc_next_hop(struct ospf_area *oa, struct top_hash_entry *en,
 /* Add LSA into list of candidates in Dijkstra's algorithm */
 static void
 add_cand(struct ospf_area *oa, struct top_hash_entry *en, struct top_hash_entry *par,
-        u32 dist, int pos, uint lif, uint nif)
+        u32 dist, int pos, uint data, uint lif, uint nif)
 {
   struct ospf_proto *p = oa->po;
   node *prev, *n;
@@ -1878,7 +1912,7 @@ add_cand(struct ospf_area *oa, struct top_hash_entry *en, struct top_hash_entry
   if (!link_back(oa, en, par, lif, nif))
     return;
 
-  struct nexthop *nhs = calc_next_hop(oa, en, par, pos, lif, nif);
+  struct nexthop *nhs = calc_next_hop(oa, en, par, pos, data, lif, nif);
   if (!nhs)
   {
     log(L_WARN "%s: Cannot find next hop for LSA (Type: %04x, Id: %R, Rt: %R)",
@@ -2086,3 +2120,135 @@ again2:
     if (en->mode == LSA_M_STALE)
       ospf_flush_lsa(p, en);
 }
+
+
+/* RFC 3623 2.2 - checking for graceful restart termination conditions */
+void
+ospf_update_gr_recovery(struct ospf_proto *p)
+{
+  struct top_hash_entry *rt, *net, *nbr;
+  struct ospf_lsa_rt_walk rtl;
+  struct ospf_neighbor *n;
+  struct ospf_iface *ifa;
+  struct ospf_area *oa;
+  const char *err_dsc = NULL;
+  uint i, j, missing = 0, err_val = 0;
+
+  /*
+   * We check here for three cases:
+   * RFC 3623 2.2 (1) - success when all adjacencies are established
+   * RFC 3623 2.2 (2) - failure when inconsistent LSA was received
+   * RFC 3623 2.2 (3) - grace period timeout
+   *
+   * It is handled by processing pre-restart local router-LSA and adjacent
+   * network-LSAs, checking neighbor association for referenced routers (1)
+   * and checking back links from their router-LSAs (2).
+   *
+   * TODO: Use timer for grace period timeout. We avoided that as function
+   * ospf_stop_gr_recovery() called from ospf_disp() makes ending of graceful
+   * restart uninterrupted by other events.
+   */
+
+  #undef DROP
+  #define DROP(DSC,VAL) do { err_dsc = DSC; err_val = VAL; goto drop; } while(0)
+  #define CONTINUE { missing++; continue; }
+
+  if (current_time() > p->gr_timeout)
+    goto timeout;
+
+  WALK_LIST(oa, p->area_list)
+  {
+    /* Get the router-LSA */
+    rt = oa->rt;
+    if (!rt || (rt->lsa.age == LSA_MAXAGE))
+      CONTINUE;
+
+    for (lsa_walk_rt_init(p, rt, &rtl), i = 0; lsa_walk_rt(&rtl); i++)
+    {
+      if (rtl.type == LSART_STUB)
+       continue;
+
+      ifa = rt_find_iface(oa, i, rtl.data, rtl.lif);
+      if (!ifa)
+       DROP("inconsistent interface", ospf_is_v2(p) ? rtl.data : rtl.lif);
+
+      switch (rtl.type)
+      {
+      case LSART_NET:
+       /* Find the network-LSA */
+       net = ospf_hash_find_net(p->gr, oa->areaid, rtl.id, rtl.nif);
+       if (!net)
+         CONTINUE;
+
+       if (!link_back(oa, net, rt, rtl.lif, rtl.nif))
+         DROP("Inconsistent network-LSA", net->lsa.id);
+
+       if (ifa->state == OSPF_IS_DR)
+       {
+         /* Find all neighbors from the network-LSA */
+         struct ospf_lsa_net *net_body = net->lsa_body;
+         uint cnt = lsa_net_count(&net->lsa);
+         for (j = 0; j < cnt; i++)
+         {
+           n = find_neigh(ifa, net_body->routers[j]);
+           if (!n || (n->state != NEIGHBOR_FULL))
+             CONTINUE;
+
+           if (!n->got_my_rt_lsa)
+             DROP("not received my router-LSA", n->rid);
+
+           nbr = ospf_hash_find_rt(p->gr, oa->areaid, n->rid);
+           if (!link_back(oa, nbr, net, 0, 0))
+             DROP("inconsistent router-LSA", n->rid);
+         }
+       }
+       else
+       {
+         /* Find the DR (by IP for OSPFv2) */
+         n = ospf_is_v2(p) ?
+           find_neigh_by_ip(ifa, ipa_from_u32(rtl.id)) :
+           find_neigh(ifa, rtl.id);
+         if (!n || (n->state != NEIGHBOR_FULL))
+           CONTINUE;
+
+         if (!n->got_my_rt_lsa)
+           DROP("not received my router-LSA", n->rid);
+       }
+       break;
+
+      case LSART_VLNK:
+      case LSART_PTP:
+       /* Find the PtP peer */
+       n = find_neigh(ifa, rtl.id);
+       if (!n || (n->state != NEIGHBOR_FULL))
+         CONTINUE;
+
+       if (!n->got_my_rt_lsa)
+         DROP("not received my router-LSA", n->rid);
+
+       nbr = ospf_hash_find_rt(p->gr, oa->areaid, rtl.id);
+       if (!link_back(oa, nbr, rt, rtl.lif, rtl.nif))
+         DROP("inconsistent router-LSA", rtl.id);
+      }
+    }
+  }
+
+  #undef CONTINUE
+
+  if (missing)
+    return;
+
+  OSPF_TRACE(D_EVENTS, "Graceful restart finished");
+  ospf_stop_gr_recovery(p);
+  return;
+
+drop:
+  log(L_INFO "%s: Graceful restart ended - %s (%R)", p->p.name, err_dsc, err_val);
+  ospf_stop_gr_recovery(p);
+  return;
+
+timeout:
+  log(L_INFO "%s: Graceful restart ended - grace period expired", p->p.name);
+  ospf_stop_gr_recovery(p);
+  return;
+}
index 589d2bc5ddde089110d5de0c14083fd0c938db75..094e125bf5e191052384dc4ea5af98d3eda51108 100644 (file)
@@ -130,6 +130,7 @@ static inline int rt_is_nssa(ort *nf)
 
 void ospf_rt_spf(struct ospf_proto *p);
 void ospf_rt_initort(struct fib_node *fn);
+void ospf_update_gr_recovery(struct ospf_proto *p);
 
 
 #endif /* _BIRD_OSPF_RT_H_ */
index 4a08132561de4631d064b3927737ed12e30d66f8..8f313e87739cf82d992d88b24e5996bdc9b04c3d 100644 (file)
@@ -83,7 +83,10 @@ ospf_install_lsa(struct ospf_proto *p, struct ospf_lsa_header *lsa, u32 type, u3
             en->lsa_type, en->lsa.id, en->lsa.rt, en->lsa.sn, en->lsa.age);
 
   if (change)
+  {
+    ospf_neigh_lsadb_changed(p, en);
     ospf_schedule_rtcalc(p);
+  }
 
   return en;
 }
@@ -243,6 +246,7 @@ ospf_do_originate_lsa(struct ospf_proto *p, struct top_hash_entry *en, void *lsa
   en->lsa.age = 0;
   en->init_age = 0;
   en->inst_time = current_time();
+  en->dirty = 0;
   lsa_generate_checksum(&en->lsa, en->lsa_body);
 
   OSPF_TRACE(D_EVENTS, "Originating LSA: Type: %04x, Id: %R, Rt: %R, Seq: %08x",
@@ -251,7 +255,10 @@ ospf_do_originate_lsa(struct ospf_proto *p, struct top_hash_entry *en, void *lsa
   ospf_flood_lsa(p, en, NULL);
 
   if (en->mode == LSA_M_BASIC)
+  {
+    ospf_neigh_lsadb_changed(p, en);
     ospf_schedule_rtcalc(p);
+  }
 
   return 1;
 }
@@ -321,7 +328,8 @@ ospf_originate_lsa(struct ospf_proto *p, struct ospf_new_lsa *lsa)
   if ((en->lsa.age < LSA_MAXAGE) &&
       (lsa_length == en->lsa.length) &&
       !memcmp(lsa_body, en->lsa_body, lsa_blen) &&
-      (!ospf_is_v2(p) || (lsa->opts == lsa_get_options(&en->lsa))))
+      (!ospf_is_v2(p) || (lsa->opts == lsa_get_options(&en->lsa))) &&
+      !en->dirty)
     goto drop;
 
   lsa_body = lsab_flush(p);
@@ -433,7 +441,10 @@ ospf_flush_lsa(struct ospf_proto *p, struct top_hash_entry *en)
   ospf_flood_lsa(p, en, NULL);
 
   if (en->mode == LSA_M_BASIC)
+  {
+    ospf_neigh_lsadb_changed(p, en);
     ospf_schedule_rtcalc(p);
+  }
 
   en->mode = LSA_M_BASIC;
 }
@@ -509,6 +520,12 @@ ospf_update_lsadb(struct ospf_proto *p)
       continue;
     }
 
+    if (en->dirty)
+    {
+      ospf_flush_lsa(p, en);
+      continue;
+    }
+
     if ((en->lsa.rt == p->router_id) && (real_age >= LSREFRESHTIME))
     {
       ospf_refresh_lsa(p, en);
@@ -525,6 +542,16 @@ ospf_update_lsadb(struct ospf_proto *p)
   }
 }
 
+void
+ospf_mark_lsadb(struct ospf_proto *p)
+{
+  struct top_hash_entry *en;
+
+  /* Mark all local LSAs as dirty */
+  WALK_SLIST(en, p->lsal)
+    if (en->lsa.rt == p->router_id)
+      en->dirty = 1;
+}
 
 static u32
 ort_to_lsaid(struct ospf_proto *p, ort *nf)
@@ -1649,6 +1676,59 @@ ospf_originate_prefix_net_lsa(struct ospf_proto *p, struct ospf_iface *ifa)
 }
 
 
+/*
+ *     Grace LSA handling
+ *     Type = LSA_T_GR, opaque type = LSA_OT_GR
+ */
+
+static inline void
+ospf_add_gr_period_tlv(struct ospf_proto *p, uint period)
+{
+  struct ospf_tlv *tlv = lsab_allocz(p, sizeof(struct ospf_tlv) + sizeof(u32));
+  tlv->type = LSA_GR_PERIOD;
+  tlv->length = 4;
+  tlv->data[0] = period;
+}
+
+static inline void
+ospf_add_gr_reason_tlv(struct ospf_proto *p, uint reason)
+{
+  struct ospf_tlv *tlv = lsab_allocz(p, sizeof(struct ospf_tlv) + sizeof(u32));
+  tlv->type = LSA_GR_REASON;
+  tlv->length = 1;
+  tlv->data[0] = reason << 24;
+}
+
+static inline void
+ospf_add_gr_address_tlv(struct ospf_proto *p, ip4_addr addr)
+{
+  struct ospf_tlv *tlv = lsab_allocz(p, sizeof(struct ospf_tlv) + sizeof(u32));
+  tlv->type = LSA_GR_ADDRESS;
+  tlv->length = 4;
+  tlv->data[0] = ip4_to_u32(addr);
+}
+
+void
+ospf_originate_gr_lsa(struct ospf_proto *p, struct ospf_iface *ifa)
+{
+  struct ospf_new_lsa lsa = {
+    .type = LSA_T_GR,
+    .dom  = ifa->iface_id,
+    .id   = ospf_is_v2(p) ? 0 : ifa->iface_id,
+    .ifa  = ifa
+  };
+
+  ospf_add_gr_period_tlv(p, p->gr_time);
+  ospf_add_gr_reason_tlv(p, 0);
+
+  uint t = ifa->type;
+  if (ospf_is_v2(p) && ((t == OSPF_IT_BCAST) || (t == OSPF_IT_NBMA) || (t == OSPF_IT_PTMP)))
+    ospf_add_gr_address_tlv(p, ipa_to_ip4(ifa->addr->ip));
+
+  ospf_originate_lsa(p, &lsa);
+}
+
+
 /*
  *     Router Information LSA handling
  *     Type = LSA_T_RI_AREA, opaque type = LSA_OT_RI
@@ -1692,6 +1772,10 @@ ospf_update_topology(struct ospf_proto *p)
   struct ospf_area *oa;
   struct ospf_iface *ifa;
 
+  /* No LSA reorigination during GR recovery */
+  if (p->gr_recovery)
+    return;
+
   WALK_LIST(oa, p->area_list)
   {
     if (oa->update_rt_lsa)
index 923ca5995b389234e46e0fd8cbea42a7536cdd41..5a784702b2eebeee15254abfd0651c54faf37a24 100644 (file)
@@ -33,6 +33,7 @@ struct top_hash_entry
   u32 lb_id;                   /* Interface ID of link back iface (for bcast or NBMA networks) */
   u32 dist;                    /* Distance from the root */
   int ret_count;               /* Number of retransmission lists referencing the entry */
+  u8 dirty;                    /* Will be flushed during next LSAdb update unless reoriginated*/
   u8 color;
 #define OUTSPF 0
 #define CANDIDATE 1
@@ -180,6 +181,7 @@ struct top_hash_entry * ospf_originate_lsa(struct ospf_proto *p, struct ospf_new
 void ospf_advance_lsa(struct ospf_proto *p, struct top_hash_entry *en, struct ospf_lsa_header *lsa, u32 type, u32 domain, void *body);
 void ospf_flush_lsa(struct ospf_proto *p, struct top_hash_entry *en);
 void ospf_update_lsadb(struct ospf_proto *p);
+void ospf_mark_lsadb(struct ospf_proto *p);
 
 static inline void ospf_flush2_lsa(struct ospf_proto *p, struct top_hash_entry **en)
 { if (*en) { ospf_flush_lsa(p, *en); *en = NULL; } }
@@ -187,6 +189,7 @@ static inline void ospf_flush2_lsa(struct ospf_proto *p, struct top_hash_entry *
 void ospf_originate_sum_net_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf, int metric);
 void ospf_originate_sum_rt_lsa(struct ospf_proto *p, struct ospf_area *oa, u32 drid, int metric, u32 options);
 void ospf_originate_ext_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf, u8 mode, u32 metric, u32 ebit, ip_addr fwaddr, u32 tag, int pbit, int dn);
+void ospf_originate_gr_lsa(struct ospf_proto *p, struct ospf_iface *ifa);
 
 void ospf_rt_notify(struct proto *P, struct channel *ch, net *n, rte *new, rte *old, ea_list *attrs);
 void ospf_update_topology(struct ospf_proto *p);