From a8f748585471feafd1100e22782e3d3f57cb565b Mon Sep 17 00:00:00 2001 From: Mark Stapp Date: Wed, 10 Mar 2021 10:36:46 -0500 Subject: [PATCH] zebra: revise pw reachability logic Modify the pseudowire reachability logic so that it returns success if there is at least one installed labelled nexthop for the route resolving the pw destination. We also check for valid backup nexthops if necessary, in case there's been a switchover event. Only OpenBSD requires that _all_ nexthops be labelled, so we have a more strict version of the logic also. Signed-off-by: Mark Stapp (cherry picked from commit 0d145d47c89263caeb2ae21fe09ac6d5e99de789) --- zebra/zebra_pw.c | 124 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 111 insertions(+), 13 deletions(-) diff --git a/zebra/zebra_pw.c b/zebra/zebra_pw.c index 5afb3e5926..6b4a815151 100644 --- a/zebra/zebra_pw.c +++ b/zebra/zebra_pw.c @@ -48,7 +48,7 @@ static int zebra_pw_enabled(struct zebra_pw *); static void zebra_pw_install(struct zebra_pw *); static void zebra_pw_uninstall(struct zebra_pw *); static int zebra_pw_install_retry(struct thread *); -static int zebra_pw_check_reachability(struct zebra_pw *); +static int zebra_pw_check_reachability(const struct zebra_pw *); static void zebra_pw_update_status(struct zebra_pw *, int); static inline int zebra_pw_compare(const struct zebra_pw *a, @@ -243,14 +243,79 @@ static void zebra_pw_update_status(struct zebra_pw *pw, int status) zsend_pw_update(pw->client, pw); } -static int zebra_pw_check_reachability(struct zebra_pw *pw) +static int zebra_pw_check_reachability_strict(const struct zebra_pw *pw, + struct route_entry *re) +{ + const struct nexthop *nexthop; + const struct nexthop_group *nhg; + bool found_p = false; + bool fail_p = false; + + /* TODO: consider GRE/L2TPv3 tunnels in addition to MPLS LSPs */ + + /* All active nexthops must be labelled; look at + * primary and backup fib lists, in case there's been + * a backup nexthop activation. + */ + nhg = rib_get_fib_nhg(re); + if (nhg && nhg->nexthop) { + for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + continue; + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) { + if (nexthop->nh_label != NULL) + found_p = true; + else { + fail_p = true; + break; + } + } + } + } + + if (fail_p) + goto done; + + nhg = rib_get_fib_backup_nhg(re); + if (nhg && nhg->nexthop) { + for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + continue; + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) { + if (nexthop->nh_label != NULL) + found_p = true; + else { + fail_p = true; + break; + } + } + } + } + +done: + + if (fail_p || !found_p) { + if (IS_ZEBRA_DEBUG_PW) + zlog_debug("%s: unlabeled route for %s", + __func__, pw->ifname); + return -1; + } + + return 0; +} + +static int zebra_pw_check_reachability(const struct zebra_pw *pw) { struct route_entry *re; - struct nexthop *nexthop; + const struct nexthop *nexthop; + const struct nexthop_group *nhg; + bool found_p = false; /* TODO: consider GRE/L2TPv3 tunnels in addition to MPLS LSPs */ - /* find route to the remote end of the pseudowire */ + /* Find route to the remote end of the pseudowire */ re = rib_match(family2afi(pw->af), SAFI_UNICAST, pw->vrf_id, &pw->nexthop, NULL); if (!re) { @@ -260,19 +325,52 @@ static int zebra_pw_check_reachability(struct zebra_pw *pw) return -1; } - /* - * Need to ensure that there's a label binding for all nexthops. - * Otherwise, ECMP for this route could render the pseudowire unusable. + /* Stricter checking for some OSes (OBSD, e.g.) */ + if (mpls_pw_reach_strict) + return zebra_pw_check_reachability_strict(pw, re); + + /* There must be at least one installed labelled nexthop; + * look at primary and backup fib lists, in case there's been + * a backup nexthop activation. */ - for (ALL_NEXTHOPS(re->nhe->nhg, nexthop)) { - if (!nexthop->nh_label) { - if (IS_ZEBRA_DEBUG_PW) - zlog_debug("%s: unlabeled route for %s", - __func__, pw->ifname); - return -1; + nhg = rib_get_fib_nhg(re); + if (nhg && nhg->nexthop) { + for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + continue; + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE) && + nexthop->nh_label != NULL) { + found_p = true; + break; + } + } + } + + if (found_p) + return 0; + + nhg = rib_get_fib_backup_nhg(re); + if (nhg && nhg->nexthop) { + for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + continue; + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE) && + nexthop->nh_label != NULL) { + found_p = true; + break; + } } } + if (!found_p) { + if (IS_ZEBRA_DEBUG_PW) + zlog_debug("%s: unlabeled route for %s", + __func__, pw->ifname); + return -1; + } + return 0; } -- 2.39.5