From 1a2ad348f660b150265f6df759a07de8a2b6de2f Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Sun, 30 Jun 2019 20:12:59 +0200 Subject: [PATCH] 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. --- proto/ospf/config.Y | 6 ++ proto/ospf/dbdes.c | 6 +- proto/ospf/iface.c | 8 ++ proto/ospf/lsalib.c | 75 +++++++++++++++- proto/ospf/lsalib.h | 15 +++- proto/ospf/lsupd.c | 20 ++++- proto/ospf/neighbor.c | 194 +++++++++++++++++++++++++++++++++++++++++- proto/ospf/ospf.c | 60 ++++++++++++- proto/ospf/ospf.h | 32 ++++++- proto/ospf/rt.c | 180 +++++++++++++++++++++++++++++++++++++-- proto/ospf/rt.h | 1 + proto/ospf/topology.c | 86 ++++++++++++++++++- proto/ospf/topology.h | 3 + 13 files changed, 660 insertions(+), 26 deletions(-) diff --git a/proto/ospf/config.Y b/proto/ospf/config.Y index 36fbd5f1..e4bacc29 100644 --- a/proto/ospf/config.Y +++ b/proto/ospf/config.Y @@ -200,6 +200,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(MERGE, LSA, SUPPRESSION, MULTICAST, RFC5838, VPN, PE) +CF_KEYWORDS(GRACEFUL, RESTART, AWARE, TIME) %type lsadb_args %type ospf_variant ospf_af_mc nbma_eligible @@ -226,6 +227,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: @@ -258,6 +261,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; } diff --git a/proto/ospf/dbdes.c b/proto/ospf/dbdes.c index a1559782..b39595d9 100644 --- a/proto/ospf/dbdes.c +++ b/proto/ospf/dbdes.c @@ -215,7 +215,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); @@ -279,6 +279,10 @@ ospf_process_dbdes(struct ospf_proto *p, struct ospf_packet *pkt, struct ospf_ne if (LSA_SCOPE(lsa_type) == LSA_SCOPE_RES) DROP1("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)) { diff --git a/proto/ospf/iface.c b/proto/ospf/iface.c index 388c91c8..f5c69199 100644 --- a/proto/ospf/iface.c +++ b/proto/ospf/iface.c @@ -772,6 +772,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) diff --git a/proto/ospf/lsalib.c b/proto/ospf/lsalib.c index 7ddf64e3..7767700f 100644 --- a/proto/ospf/lsalib.c +++ b/proto/ospf/lsalib.c @@ -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: diff --git a/proto/ospf/lsalib.h b/proto/ospf/lsalib.h index af8901ce..eca138d7 100644 --- a/proto/ospf/lsalib.h +++ b/proto/ospf/lsalib.h @@ -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); diff --git a/proto/ospf/lsupd.c b/proto/ospf/lsupd.c index 7318b751..fafe4872 100644 --- a/proto/ospf/lsupd.c +++ b/proto/ospf/lsupd.c @@ -185,6 +185,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 */ @@ -591,8 +598,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); @@ -629,6 +637,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); 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; } diff --git a/proto/ospf/ospf.c b/proto/ospf/ospf.c index f3eabb47..968d7aa0 100644 --- a/proto/ospf/ospf.c +++ b/proto/ospf/ospf.c @@ -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 @@ -207,7 +209,6 @@ ospf_area_remove(struct ospf_area *oa) mb_free(oa); } - struct ospf_area * ospf_find_area(struct ospf_proto *p, u32 aid) { @@ -228,6 +229,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) { @@ -246,6 +278,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); @@ -267,6 +301,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); @@ -398,6 +436,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); @@ -475,9 +516,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) @@ -664,6 +714,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); diff --git a/proto/ospf/ospf.h b/proto/ospf/ospf.h index 7fac47c8..aac13512 100644 --- a/proto/ospf/ospf.h +++ b/proto/ospf/ospf.h @@ -75,6 +75,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 @@ -82,6 +83,9 @@ #define OSPF_VLINK_ID_OFFSET 0x80000000 +#define OSPF_GR_ABLE 1 +#define OSPF_GR_AWARE 2 + struct ospf_config { struct proto_config c; @@ -97,7 +101,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) */ }; @@ -216,6 +222,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 */ @@ -228,6 +237,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 */ u64 csn64; /* Last used cryptographic sequence number */ struct ospf_area *backbone; /* If exists */ event *flood_event; /* Event for flooding LS updates */ @@ -346,6 +357,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 */ @@ -388,6 +401,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 */ @@ -555,6 +569,7 @@ struct ospf_auth3 #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 @@ -569,6 +584,7 @@ struct ospf_auth3 /* 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 @@ -613,6 +629,12 @@ struct ospf_auth3 #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 @@ -959,6 +981,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); @@ -990,12 +1014,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); diff --git a/proto/ospf/rt.c b/proto/ospf/rt.c index 6ddd6c9f..126ef201 100644 --- a/proto/ospf/rt.c +++ b/proto/ospf/rt.c @@ -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) @@ -503,7 +537,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); } } @@ -526,7 +560,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); } } @@ -1708,7 +1742,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; @@ -1735,7 +1769,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; @@ -1748,7 +1782,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; @@ -1838,7 +1872,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; @@ -1871,7 +1905,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,133 @@ 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. + */ + + #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; +} diff --git a/proto/ospf/rt.h b/proto/ospf/rt.h index 589d2bc5..094e125b 100644 --- a/proto/ospf/rt.h +++ b/proto/ospf/rt.h @@ -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_ */ diff --git a/proto/ospf/topology.c b/proto/ospf/topology.c index 1579f496..efd03b54 100644 --- a/proto/ospf/topology.c +++ b/proto/ospf/topology.c @@ -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) @@ -1675,6 +1702,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 @@ -1718,6 +1798,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) diff --git a/proto/ospf/topology.h b/proto/ospf/topology.h index fd70239d..ffae436a 100644 --- a/proto/ospf/topology.h +++ b/proto/ospf/topology.h @@ -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); void ospf_update_topology(struct ospf_proto *p);