From e23e56831958c3ae373a56a13ab8e0fc46fe6413 Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Wed, 1 Mar 2023 17:31:56 -0300 Subject: [PATCH 1/6] ospf6d: update DR election to handle GR corner case Consider the case where a DR router is performing a graceful restart, and all neighbors attached to the DR network have their priorities set to zero. According to RFC 3623, the router should reclaim its DR status while coming back up once it receives a Hello packet from a neighbor listing the router as the DR, and the associated interface is in Waiting state. The problem arises when the DR election starts. Since the router is already elected the DR, and no BDR will be elected (since all neighbors have their priorities set to zero), the AdjOk event won't be triggered at the end of the DR election as it would normally happen. That causes all neighbors reachable over the broadcast interface to get stuck in the 2-Way state. Fix this corner case by always triggering the AdjOk event at the end of the DR election process when a GR is in progress. Triggering the AdjOk event when not necessary should never be a problem as the neighbor FSM is already prepared to deal with that. Signed-off-by: Renato Westphal --- ospf6d/ospf6_interface.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c index 7a22fdf064..7afb47c752 100644 --- a/ospf6d/ospf6_interface.c +++ b/ospf6d/ospf6_interface.c @@ -592,6 +592,7 @@ static struct ospf6_neighbor *better_drouter(struct ospf6_neighbor *a, uint8_t dr_election(struct ospf6_interface *oi) { + struct ospf6 *ospf6 = oi->area->ospf6; struct listnode *node, *nnode; struct ospf6_neighbor *on, *drouter, *bdrouter, myself; struct ospf6_neighbor *best_drouter, *best_bdrouter; @@ -602,13 +603,12 @@ uint8_t dr_election(struct ospf6_interface *oi) /* pseudo neighbor myself, including noting current DR/BDR (1) */ memset(&myself, 0, sizeof(myself)); - inet_ntop(AF_INET, &oi->area->ospf6->router_id, myself.name, - sizeof(myself.name)); + inet_ntop(AF_INET, &ospf6->router_id, myself.name, sizeof(myself.name)); myself.state = OSPF6_NEIGHBOR_TWOWAY; myself.drouter = oi->drouter; myself.bdrouter = oi->bdrouter; myself.priority = oi->priority; - myself.router_id = oi->area->ospf6->router_id; + myself.router_id = ospf6->router_id; /* Electing BDR (2) */ for (ALL_LIST_ELEMENTS(oi->neighbor_list, node, nnode, on)) @@ -657,8 +657,10 @@ uint8_t dr_election(struct ospf6_interface *oi) /* If DR or BDR change, invoke AdjOK? for each neighbor (7) */ /* RFC 2328 section 12.4. Originating LSAs (3) will be handled accordingly after AdjOK */ - if (oi->drouter != (drouter ? drouter->router_id : htonl(0)) - || oi->bdrouter != (bdrouter ? bdrouter->router_id : htonl(0))) { + + if (oi->drouter != (drouter ? drouter->router_id : htonl(0)) || + oi->bdrouter != (bdrouter ? bdrouter->router_id : htonl(0)) || + ospf6->gr_info.restart_in_progress) { if (IS_OSPF6_DEBUG_INTERFACE) zlog_debug("DR Election on %s: DR: %s BDR: %s", oi->interface->name, From 5cbcc459aec36faa38d84a62cd57789bdf8f2882 Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Wed, 1 Mar 2023 17:31:56 -0300 Subject: [PATCH 2/6] ospf6d: add exception for virtual links when exiting from the GR mode RFC 5340 says that Link-LSAs should not be originated for virtual links. Add a check to prevent that from happening while exiting from the GR mode. Signed-off-by: Renato Westphal --- ospf6d/ospf6_gr.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ospf6d/ospf6_gr.c b/ospf6d/ospf6_gr.c index c182e48f2e..768b5e0333 100644 --- a/ospf6d/ospf6_gr.c +++ b/ospf6d/ospf6_gr.c @@ -149,7 +149,9 @@ static void ospf6_gr_restart_exit(struct ospf6 *ospf6, const char *reason) OSPF6_ROUTER_LSA_EXECUTE(area); for (ALL_LIST_ELEMENTS_RO(area->if_list, anode, oi)) { - OSPF6_LINK_LSA_EXECUTE(oi); + /* Reoriginate Link-LSA. */ + if (oi->type != OSPF_IFTYPE_VIRTUALLINK) + OSPF6_LINK_LSA_EXECUTE(oi); /* * 2) The router should reoriginate network-LSAs on all From 3a94ed56964097e35c932bafce0c164b9323c12d Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Wed, 1 Mar 2023 17:31:56 -0300 Subject: [PATCH 3/6] ospf6d: fix duplicate inter-area-prefix-LSAs after exiting from GR mode An ABR that is originating inter-area-prefix-LSAs should take into account the fact that there might be self-originated LSAs for the same prefixes that were originated prior to a graceful restart. When that happens, the previous LSA-IDs should be reused to avoid having duplicate LSAs. Signed-off-by: Renato Westphal --- ospf6d/ospf6_abr.c | 16 +++++++++++++--- ospf6d/ospf6_lsdb.c | 27 +++++++++++++++++++++++++++ ospf6d/ospf6_lsdb.h | 3 +++ 3 files changed, 43 insertions(+), 3 deletions(-) diff --git a/ospf6d/ospf6_abr.c b/ospf6d/ospf6_abr.c index 4def3c7386..5b2b204dd8 100644 --- a/ospf6d/ospf6_abr.c +++ b/ospf6d/ospf6_abr.c @@ -515,11 +515,21 @@ int ospf6_abr_originate_summary_to_area(struct ospf6_route *route, summary->path.origin.id = ADV_ROUTER_IN_PREFIX(&route->prefix); } else { + struct ospf6_lsa *old; + summary->path.origin.type = htons(OSPF6_LSTYPE_INTER_PREFIX); - summary->path.origin.id = ospf6_new_ls_id( - summary->path.origin.type, - summary->path.origin.adv_router, area->lsdb); + + /* Try to reuse LS-ID from previous running instance. */ + old = ospf6_find_inter_prefix_lsa(area->ospf6, area, + &route->prefix); + if (old) + summary->path.origin.id = old->header->id; + else + summary->path.origin.id = ospf6_new_ls_id( + summary->path.origin.type, + summary->path.origin.adv_router, + area->lsdb); } summary = ospf6_route_add(summary, summary_table); } else { diff --git a/ospf6d/ospf6_lsdb.c b/ospf6d/ospf6_lsdb.c index 0221102b59..586183731c 100644 --- a/ospf6d/ospf6_lsdb.c +++ b/ospf6d/ospf6_lsdb.c @@ -13,8 +13,10 @@ #include "vty.h" #include "ospf6_proto.h" +#include "ospf6_area.h" #include "ospf6_lsa.h" #include "ospf6_lsdb.h" +#include "ospf6_abr.h" #include "ospf6_asbr.h" #include "ospf6_route.h" #include "ospf6d.h" @@ -216,6 +218,31 @@ struct ospf6_lsa *ospf6_find_external_lsa(struct ospf6 *ospf6, struct prefix *p) return lsa; } +struct ospf6_lsa *ospf6_find_inter_prefix_lsa(struct ospf6 *ospf6, + struct ospf6_area *area, + struct prefix *p) +{ + struct ospf6_lsa *lsa; + uint16_t type = htons(OSPF6_LSTYPE_INTER_PREFIX); + + for (ALL_LSDB_TYPED_ADVRTR(area->lsdb, type, ospf6->router_id, lsa)) { + struct ospf6_inter_prefix_lsa *prefix_lsa; + struct prefix prefix; + + prefix_lsa = + (struct ospf6_inter_prefix_lsa *)OSPF6_LSA_HEADER_END( + lsa->header); + prefix.family = AF_INET6; + prefix.prefixlen = prefix_lsa->prefix.prefix_length; + ospf6_prefix_in6_addr(&prefix.u.prefix6, prefix_lsa, + &prefix_lsa->prefix); + if (prefix_same(p, &prefix)) + return lsa; + } + + return NULL; +} + struct ospf6_lsa *ospf6_lsdb_lookup_next(uint16_t type, uint32_t id, uint32_t adv_router, struct ospf6_lsdb *lsdb) diff --git a/ospf6d/ospf6_lsdb.h b/ospf6d/ospf6_lsdb.h index 7e20b05447..a2444f1c14 100644 --- a/ospf6d/ospf6_lsdb.h +++ b/ospf6d/ospf6_lsdb.h @@ -29,6 +29,9 @@ extern struct ospf6_lsa *ospf6_lsdb_lookup(uint16_t type, uint32_t id, extern struct ospf6_lsa *ospf6_lsdb_lookup_next(uint16_t type, uint32_t id, uint32_t adv_router, struct ospf6_lsdb *lsdb); +extern struct ospf6_lsa *ospf6_find_inter_prefix_lsa(struct ospf6 *ospf6, + struct ospf6_area *area, + struct prefix *p); extern void ospf6_lsdb_add(struct ospf6_lsa *lsa, struct ospf6_lsdb *lsdb); extern void ospf6_lsdb_remove(struct ospf6_lsa *lsa, struct ospf6_lsdb *lsdb); From 5eb2c602aace233d7b0a7151f4739cc20cc6123c Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Wed, 1 Mar 2023 17:31:56 -0300 Subject: [PATCH 4/6] ospf6d: fix refreshing of NSSA/AS-external LSAs after a graceful restart This commit fixes a bug where self-originated NSSA/AS-External LSAs would age out about one hour after exiting from the GR mode. The reason is because received self-originated LSAs aren't registered for periodic refreshing, so that needs to be done manually. Fix this by explicitly reoriginating all NSSA/AS-External LSAs while exiting from the GR mode. Signed-off-by: Renato Westphal --- ospf6d/ospf6_gr.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/ospf6d/ospf6_gr.c b/ospf6d/ospf6_gr.c index 768b5e0333..2e8bb025fb 100644 --- a/ospf6d/ospf6_gr.c +++ b/ospf6d/ospf6_gr.c @@ -127,6 +127,7 @@ static void ospf6_gr_restart_exit(struct ospf6 *ospf6, const char *reason) { struct ospf6_area *area; struct listnode *onode, *anode; + struct ospf6_route *route; if (IS_DEBUG_OSPF6_GR) zlog_debug("GR: exiting graceful restart: %s", reason); @@ -162,6 +163,16 @@ static void ospf6_gr_restart_exit(struct ospf6 *ospf6, const char *reason) } } + /* + * While all self-originated NSSA and AS-external LSAs were already + * learned from the helping neighbors, we need to reoriginate them in + * order to ensure they will be refreshed periodically. + */ + for (route = ospf6_route_head(ospf6->external_table); route; + route = ospf6_route_next(route)) + ospf6_handle_external_lsa_origination(ospf6, route, + &route->prefix); + /* * 3) The router reruns its OSPF routing calculations, this time * installing the results into the system forwarding table, and From 3d2533ed58d299b64505e055a824ab0396af9be4 Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Wed, 1 Mar 2023 17:31:56 -0300 Subject: [PATCH 5/6] ospf6d: fix missing intra-area-prefix-LSA after a graceful restart Upon exiting the GR mode, force reorigination of intra-area-prefix-LSAs on all attached areas. This is to ensure all configured areas will have an associated intra-area-prefix-LSA at the end of the GR procedures, even if the area doesn't have any full adjacency. Signed-off-by: Renato Westphal --- ospf6d/ospf6_gr.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ospf6d/ospf6_gr.c b/ospf6d/ospf6_gr.c index 2e8bb025fb..847531361e 100644 --- a/ospf6d/ospf6_gr.c +++ b/ospf6d/ospf6_gr.c @@ -149,6 +149,12 @@ static void ospf6_gr_restart_exit(struct ospf6 *ospf6, const char *reason) */ OSPF6_ROUTER_LSA_EXECUTE(area); + /* + * Force reorigination of intra-area-prefix-LSAs to handle + * areas without any full adjacency. + */ + OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB(area); + for (ALL_LIST_ELEMENTS_RO(area->if_list, anode, oi)) { /* Reoriginate Link-LSA. */ if (oi->type != OSPF_IFTYPE_VIRTUALLINK) From cb1f47c2eb46c3ca7bd485c15fed024f31c50be6 Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Wed, 1 Mar 2023 17:31:56 -0300 Subject: [PATCH 6/6] ospfd, ospf6d: perform GR consistency check only when necessary The GR code should check for topology changes only upon the receipt of Router-LSAs and Network-LSAs. Other LSAs types don't affect the topology as far as a restarting router is concerned. This optimization reduces unnecessary computations when the restarting router receives thousands of inter-area LSAs or external LSAs while coming back up. Signed-off-by: Renato Westphal --- ospf6d/ospf6_flood.c | 5 ++++- ospfd/ospf_packet.c | 11 ++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/ospf6d/ospf6_flood.c b/ospf6d/ospf6_flood.c index 2d2069566c..db1520ff20 100644 --- a/ospf6d/ospf6_flood.c +++ b/ospf6d/ospf6_flood.c @@ -1105,9 +1105,12 @@ void ospf6_receive_lsa(struct ospf6_neighbor *from, &new->refresh); } + /* GR: check for network topology change. */ struct ospf6 *ospf6 = from->ospf6_if->area->ospf6; struct ospf6_area *area = from->ospf6_if->area; - if (ospf6->gr_info.restart_in_progress) + if (ospf6->gr_info.restart_in_progress && + (new->header->type == ntohs(OSPF6_LSTYPE_ROUTER) || + new->header->type == ntohs(OSPF6_LSTYPE_NETWORK))) ospf6_gr_check_lsdb_consistency(ospf6, area); return; diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c index 5268c9896b..2937c4ec0c 100644 --- a/ospfd/ospf_packet.c +++ b/ospfd/ospf_packet.c @@ -2102,6 +2102,14 @@ static void ospf_ls_upd(struct ospf *ospf, struct ip *iph, if (ospf_flood(oi->ospf, nbr, current, lsa) < 0) /* Trap NSSA later. */ DISCARD_LSA(lsa, 5); + + /* GR: check for network topology change. */ + if (ospf->gr_info.restart_in_progress && + ((lsa->data->type == OSPF_ROUTER_LSA || + lsa->data->type == OSPF_NETWORK_LSA))) + ospf_gr_check_lsdb_consistency(oi->ospf, + oi->area); + continue; } @@ -2214,9 +2222,6 @@ static void ospf_ls_upd(struct ospf *ospf, struct ip *iph, assert(listcount(lsas) == 0); list_delete(&lsas); - - if (ospf->gr_info.restart_in_progress) - ospf_gr_check_lsdb_consistency(oi->ospf, oi->area); } /* OSPF Link State Acknowledgment message read -- RFC2328 Section 13.7. */