diff options
author | Ondrej Zajicek (work) <santiago@crfreenet.org> | 2019-06-30 20:12:59 +0200 |
---|---|---|
committer | Ondrej Zajicek (work) <santiago@crfreenet.org> | 2019-06-30 21:30:41 +0200 |
commit | 1a2ad348f660b150265f6df759a07de8a2b6de2f (patch) | |
tree | d64846d56a79178a34e865440b3b87d970c13fd4 /proto/ospf/neighbor.c | |
parent | 8a68316eb96be1fecf91ca395f3321aa99997ad2 (diff) |
OSPF: Support for graceful restart
Implement OSPFv2 (RFC 3623) and OSPFv3 (RFC 5187) graceful restart,
for both restarting and helper sides. Graceful restart is initiated
by 'graceful down' command.
Diffstat (limited to 'proto/ospf/neighbor.c')
-rw-r--r-- | proto/ospf/neighbor.c | 194 |
1 files changed, 191 insertions, 3 deletions
diff --git a/proto/ospf/neighbor.c b/proto/ospf/neighbor.c index c143b130..50ef6a49 100644 --- a/proto/ospf/neighbor.c +++ b/proto/ospf/neighbor.c @@ -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 @@ -163,7 +165,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); @@ -182,6 +184,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); @@ -191,9 +194,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); } @@ -291,6 +294,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); @@ -356,6 +370,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; } |