diff options
| -rw-r--r-- | bgpd/bgp_bmp.c | 5 | ||||
| -rw-r--r-- | bgpd/bgp_evpn_vty.c | 6 | ||||
| -rw-r--r-- | bgpd/bgp_main.c | 15 | ||||
| -rw-r--r-- | doc/user/bgp.rst | 66 | ||||
| -rw-r--r-- | lib/nexthop.c | 127 | ||||
| -rw-r--r-- | lib/nexthop.h | 25 | ||||
| -rw-r--r-- | lib/nexthop_group.c | 81 | ||||
| -rw-r--r-- | lib/nexthop_group.h | 6 | ||||
| -rwxr-xr-x | lib/route_types.pl | 2 | ||||
| -rw-r--r-- | lib/zclient.c | 37 | ||||
| -rw-r--r-- | lib/zclient.h | 5 | ||||
| -rw-r--r-- | sharpd/sharp_vty.c | 3 | ||||
| -rw-r--r-- | sharpd/sharp_zebra.c | 4 | ||||
| -rw-r--r-- | zebra/rib.h | 20 | ||||
| -rw-r--r-- | zebra/zapi_msg.c | 96 | ||||
| -rw-r--r-- | zebra/zebra_dplane.c | 70 | ||||
| -rw-r--r-- | zebra/zebra_mpls.c | 69 | ||||
| -rw-r--r-- | zebra/zebra_nhg.c | 10 | ||||
| -rw-r--r-- | zebra/zebra_pw.c | 104 | ||||
| -rw-r--r-- | zebra/zebra_rib.c | 35 | ||||
| -rw-r--r-- | zebra/zebra_rnh.c | 19 | ||||
| -rw-r--r-- | zebra/zebra_vty.c | 224 |
22 files changed, 724 insertions, 305 deletions
diff --git a/bgpd/bgp_bmp.c b/bgpd/bgp_bmp.c index db330f998c..af88547ca9 100644 --- a/bgpd/bgp_bmp.c +++ b/bgpd/bgp_bmp.c @@ -951,8 +951,11 @@ afibreak: /* initialize syncrdpos to the first * mid-layer table entry */ - if (!bmp->syncrdpos) + if (!bmp->syncrdpos) { bmp->syncrdpos = bgp_table_top(table); + if (!bmp->syncrdpos) + goto eor; + } /* look for a valid mid-layer table */ do { diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index 2cb13fb85f..2584939378 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -3899,6 +3899,12 @@ DEFPY (bgp_evpn_advertise_pip_ip_mac, struct listnode *node = NULL; struct bgpevpn *vpn = NULL; + /* + * At this point if bgp_evpn is NULL and evpn is enabled + * something stupid has gone wrong + */ + assert(bgp_evpn); + update_advertise_vrf_routes(bgp_vrf); /* Update (svi) type-2 routes */ diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c index 33eaf9ae74..b082aa9c6a 100644 --- a/bgpd/bgp_main.c +++ b/bgpd/bgp_main.c @@ -133,19 +133,20 @@ void sighup(void) /* * This is turned off for the moment. There is all * sorts of config turned off by bgp_terminate - * that is not setup properly again in bgp_rest. + * that is not setup properly again in bgp_reset. * I see no easy way to do this nor do I see that * this is a desirable way to reload config * given the yang work. */ /* Terminate all thread. */ - bgp_terminate(); - bgp_reset(); - zlog_info("bgpd restarting!"); - - /* Reload config file. */ - vty_read_config(NULL, bgpd_di.config_file, config_default); + /* + * bgp_terminate(); + * bgp_reset(); + * zlog_info("bgpd restarting!"); + * Reload config file. + * vty_read_config(NULL, bgpd_di.config_file, config_default); + */ /* Try to return to normal operation. */ } diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index 8f27218fe9..d776b32a88 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -2432,72 +2432,6 @@ This makes possible to separate not only layer 3 networks like VRF-lite networks Also, VRF netns based make possible to separate layer 2 networks on separate VRF instances. -.. _bgp-cisco-compatibility: - -Cisco Compatibility -------------------- - -FRR has commands that change some configuration syntax and default behavior to -behave more closely to Cisco conventions. These are deprecated and will be -removed in a future version of FRR. - -.. deprecated:: 5.0 - Please transition to using the FRR specific syntax for your configuration. - -.. index:: bgp config-type cisco -.. clicmd:: bgp config-type cisco - - Cisco compatible BGP configuration output. - - When this configuration line is specified: - - - ``no synchronization`` is displayed. This command does nothing and is for - display purposes only. - - ``no auto-summary`` is displayed. - - The ``network`` and ``aggregate-address`` arguments are displayed as: - - :: - - A.B.C.D M.M.M.M - - FRR: network 10.0.0.0/8 - Cisco: network 10.0.0.0 - - FRR: aggregate-address 192.168.0.0/24 - Cisco: aggregate-address 192.168.0.0 255.255.255.0 - - Community attribute handling is also different. If no configuration is - specified community attribute and extended community attribute are sent to - the neighbor. If a user manually disables the feature, the community - attribute is not sent to the neighbor. When ``bgp config-type cisco`` is - specified, the community attribute is not sent to the neighbor by default. - To send the community attribute user has to specify - :clicmd:`neighbor A.B.C.D send-community` like so: - - .. code-block:: frr - - ! - router bgp 1 - neighbor 10.0.0.1 remote-as 1 - address-family ipv4 unicast - no neighbor 10.0.0.1 send-community - exit-address-family - ! - router bgp 1 - neighbor 10.0.0.1 remote-as 1 - address-family ipv4 unicast - neighbor 10.0.0.1 send-community - exit-address-family - ! - -.. deprecated:: 5.0 - Please transition to using the FRR specific syntax for your configuration. - -.. index:: bgp config-type zebra -.. clicmd:: bgp config-type zebra - - FRR style BGP configuration. This is the default. - .. _bgp-debugging: Debugging diff --git a/lib/nexthop.c b/lib/nexthop.c index 3496081d47..28d96a539c 100644 --- a/lib/nexthop.c +++ b/lib/nexthop.c @@ -157,6 +157,10 @@ static int _nexthop_cmp_no_labels(const struct nexthop *next1, goto done; if (!CHECK_FLAG(next1->flags, NEXTHOP_FLAG_HAS_BACKUP) && + !CHECK_FLAG(next2->flags, NEXTHOP_FLAG_HAS_BACKUP)) + return 0; + + if (!CHECK_FLAG(next1->flags, NEXTHOP_FLAG_HAS_BACKUP) && CHECK_FLAG(next2->flags, NEXTHOP_FLAG_HAS_BACKUP)) return -1; @@ -164,12 +168,18 @@ static int _nexthop_cmp_no_labels(const struct nexthop *next1, !CHECK_FLAG(next2->flags, NEXTHOP_FLAG_HAS_BACKUP)) return 1; - if (next1->backup_idx < next2->backup_idx) + if (next1->backup_num == 0 && next2->backup_num == 0) + goto done; + + if (next1->backup_num < next2->backup_num) return -1; - if (next1->backup_idx > next2->backup_idx) + if (next1->backup_num > next2->backup_num) return 1; + ret = memcmp(next1->backup_idx, + next2->backup_idx, next1->backup_num); + done: return ret; } @@ -515,11 +525,12 @@ struct nexthop *nexthop_next_active_resolved(const struct nexthop *nexthop) return next; } -unsigned int nexthop_level(struct nexthop *nexthop) +unsigned int nexthop_level(const struct nexthop *nexthop) { unsigned int rv = 0; - for (struct nexthop *par = nexthop->rparent; par; par = par->rparent) + for (const struct nexthop *par = nexthop->rparent; + par; par = par->rparent) rv++; return rv; @@ -529,14 +540,15 @@ unsigned int nexthop_level(struct nexthop *nexthop) uint32_t nexthop_hash_quick(const struct nexthop *nexthop) { uint32_t key = 0x45afe398; - uint32_t val; + int i; key = jhash_3words(nexthop->type, nexthop->vrf_id, nexthop->nh_label_type, key); if (nexthop->nh_label) { int labels = nexthop->nh_label->num_labels; - int i = 0; + + i = 0; while (labels >= 3) { key = jhash_3words(nexthop->nh_label->label[i], @@ -559,14 +571,35 @@ uint32_t nexthop_hash_quick(const struct nexthop *nexthop) key = jhash_1word(nexthop->nh_label->label[i], key); } - val = 0; - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) - val = (uint32_t)nexthop->backup_idx; - - key = jhash_3words(nexthop->ifindex, - CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK), val, + key = jhash_2words(nexthop->ifindex, + CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK), key); + /* Include backup nexthops, if present */ + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) { + int backups = nexthop->backup_num; + + i = 0; + + while (backups >= 3) { + key = jhash_3words(nexthop->backup_idx[i], + nexthop->backup_idx[i + 1], + nexthop->backup_idx[i + 2], key); + backups -= 3; + i += 3; + } + + while (backups >= 2) { + key = jhash_2words(nexthop->backup_idx[i], + nexthop->backup_idx[i + 1], key); + backups -= 2; + i += 2; + } + + if (backups >= 1) + key = jhash_1word(nexthop->backup_idx[i], key); + } + return key; } @@ -604,7 +637,12 @@ void nexthop_copy_no_recurse(struct nexthop *copy, copy->type = nexthop->type; copy->flags = nexthop->flags; copy->weight = nexthop->weight; - copy->backup_idx = nexthop->backup_idx; + + assert(nexthop->backup_num < NEXTHOP_MAX_BACKUPS); + copy->backup_num = nexthop->backup_num; + if (copy->backup_num > 0) + memcpy(copy->backup_idx, nexthop->backup_idx, copy->backup_num); + memcpy(©->gate, &nexthop->gate, sizeof(nexthop->gate)); memcpy(©->src, &nexthop->src, sizeof(nexthop->src)); memcpy(©->rmap_src, &nexthop->rmap_src, sizeof(nexthop->rmap_src)); @@ -621,7 +659,7 @@ void nexthop_copy(struct nexthop *copy, const struct nexthop *nexthop, nexthop_copy_no_recurse(copy, nexthop, rparent); /* Bit of a special case here, we need to handle the case - * of a nexthop resolving to agroup. Hence, we need to + * of a nexthop resolving to a group. Hence, we need to * use a nexthop_group API. */ if (CHECK_FLAG(copy->flags, NEXTHOP_FLAG_RECURSIVE)) @@ -647,6 +685,67 @@ struct nexthop *nexthop_dup(const struct nexthop *nexthop, } /* + * Parse one or more backup index values, as comma-separated numbers, + * into caller's array of uint8_ts. The array must be NEXTHOP_MAX_BACKUPS + * in size. Mails back the number of values converted, and returns 0 on + * success, <0 if an error in parsing. + */ +int nexthop_str2backups(const char *str, int *num_backups, + uint8_t *backups) +{ + char *ostr; /* copy of string (start) */ + char *lstr; /* working copy of string */ + char *nump; /* pointer to next segment */ + char *endp; /* end pointer */ + int i, ret; + uint8_t tmp[NEXTHOP_MAX_BACKUPS]; + uint32_t lval; + + /* Copy incoming string; the parse is destructive */ + lstr = ostr = XSTRDUP(MTYPE_TMP, str); + *num_backups = 0; + ret = 0; + + for (i = 0; i < NEXTHOP_MAX_BACKUPS && lstr; i++) { + nump = strsep(&lstr, ","); + lval = strtoul(nump, &endp, 10); + + /* Format check */ + if (*endp != '\0') { + ret = -1; + break; + } + + /* Empty value */ + if (endp == nump) { + ret = -1; + break; + } + + /* Limit to one octet */ + if (lval > 255) { + ret = -1; + break; + } + + tmp[i] = lval; + } + + /* Excess values */ + if (ret == 0 && i == NEXTHOP_MAX_BACKUPS && lstr) + ret = -1; + + if (ret == 0) { + *num_backups = i; + memcpy(backups, tmp, i); + } + + XFREE(MTYPE_TMP, ostr); + + return ret; +} + +/* * nexthop printing variants: * %pNHvv * via 1.2.3.4 diff --git a/lib/nexthop.h b/lib/nexthop.h index eda88efc08..ed40cc7eed 100644 --- a/lib/nexthop.h +++ b/lib/nexthop.h @@ -65,6 +65,12 @@ enum nh_encap_type { NET_VXLAN = 100, /* value copied from FPM_NH_ENCAP_VXLAN. */ }; +/* Fixed limit on the number of backup nexthops per primary nexthop */ +#define NEXTHOP_MAX_BACKUPS 8 + +/* Backup index value is limited */ +#define NEXTHOP_BACKUP_IDX_MAX 255 + /* Nexthop structure. */ struct nexthop { struct nexthop *next; @@ -124,10 +130,11 @@ struct nexthop { /* Weight of the nexthop ( for unequal cost ECMP ) */ uint8_t weight; - /* Index of a corresponding backup nexthop in a backup list; + /* Count and index of corresponding backup nexthop(s) in a backup list; * only meaningful if the HAS_BACKUP flag is set. */ - uint8_t backup_idx; + uint8_t backup_num; + uint8_t backup_idx[NEXTHOP_MAX_BACKUPS]; /* Encapsulation information. */ enum nh_encap_type nh_encap_type; @@ -136,9 +143,6 @@ struct nexthop { } nh_encap; }; -/* Backup index value is limited */ -#define NEXTHOP_BACKUP_IDX_MAX 255 - /* Utility to append one nexthop to another. */ #define NEXTHOP_APPEND(to, new) \ do { \ @@ -216,7 +220,7 @@ extern const char *nexthop2str(const struct nexthop *nexthop, extern struct nexthop *nexthop_next(const struct nexthop *nexthop); extern struct nexthop * nexthop_next_active_resolved(const struct nexthop *nexthop); -extern unsigned int nexthop_level(struct nexthop *nexthop); +extern unsigned int nexthop_level(const struct nexthop *nexthop); /* Copies to an already allocated nexthop struct */ extern void nexthop_copy(struct nexthop *copy, const struct nexthop *nexthop, struct nexthop *rparent); @@ -231,6 +235,15 @@ extern struct nexthop *nexthop_dup(const struct nexthop *nexthop, extern struct nexthop *nexthop_dup_no_recurse(const struct nexthop *nexthop, struct nexthop *rparent); +/* + * Parse one or more backup index values, as comma-separated numbers, + * into caller's array of uint8_ts. The array must be NEXTHOP_MAX_BACKUPS + * in size. Mails back the number of values converted, and returns 0 on + * success, <0 if an error in parsing. + */ +int nexthop_str2backups(const char *str, int *num_backups, + uint8_t *backups); + #ifdef _FRR_ATTRIBUTE_PRINTFRR #pragma FRR printfrr_ext "%pNH" (struct nexthop *) #endif diff --git a/lib/nexthop_group.c b/lib/nexthop_group.c index 4f0c72af27..97815ceeb9 100644 --- a/lib/nexthop_group.c +++ b/lib/nexthop_group.c @@ -43,12 +43,9 @@ struct nexthop_hold { char *intf; char *labels; uint32_t weight; - int backup_idx; /* Index of backup nexthop, if >= 0 */ + char *backup_str; }; -/* Invalid/unset value for nexthop_hold's backup_idx */ -#define NHH_BACKUP_IDX_INVALID -1 - struct nexthop_group_hooks { void (*new)(const char *name); void (*add_nexthop)(const struct nexthop_group_cmd *nhg, @@ -677,7 +674,8 @@ static void nexthop_group_save_nhop(struct nexthop_group_cmd *nhgc, const char *nhvrf_name, const union sockunion *addr, const char *intf, const char *labels, - const uint32_t weight, int backup_idx) + const uint32_t weight, + const char *backup_str) { struct nexthop_hold *nh; @@ -694,7 +692,8 @@ static void nexthop_group_save_nhop(struct nexthop_group_cmd *nhgc, nh->weight = weight; - nh->backup_idx = backup_idx; + if (backup_str) + nh->backup_str = XSTRDUP(MTYPE_TMP, backup_str); listnode_add_sort(nhgc->nhg_list, nh); } @@ -741,10 +740,11 @@ static bool nexthop_group_parse_nexthop(struct nexthop *nhop, const union sockunion *addr, const char *intf, const char *name, const char *labels, int *lbl_ret, - uint32_t weight, int backup_idx) + uint32_t weight, const char *backup_str) { int ret = 0; struct vrf *vrf; + int num; memset(nhop, 0, sizeof(*nhop)); @@ -800,13 +800,15 @@ static bool nexthop_group_parse_nexthop(struct nexthop *nhop, nhop->weight = weight; - if (backup_idx != NHH_BACKUP_IDX_INVALID) { - /* Validate index value */ - if (backup_idx > NEXTHOP_BACKUP_IDX_MAX) + if (backup_str) { + /* Parse backup indexes */ + ret = nexthop_str2backups(backup_str, + &num, nhop->backup_idx); + if (ret == 0) { + SET_FLAG(nhop->flags, NEXTHOP_FLAG_HAS_BACKUP); + nhop->backup_num = num; + } else return false; - - SET_FLAG(nhop->flags, NEXTHOP_FLAG_HAS_BACKUP); - nhop->backup_idx = backup_idx; } return true; @@ -820,7 +822,7 @@ static bool nexthop_group_parse_nhh(struct nexthop *nhop, { return (nexthop_group_parse_nexthop(nhop, nhh->addr, nhh->intf, nhh->nhvrf_name, nhh->labels, NULL, - nhh->weight, nhh->backup_idx)); + nhh->weight, nhh->backup_str)); } DEFPY(ecmp_nexthops, ecmp_nexthops_cmd, @@ -833,7 +835,7 @@ DEFPY(ecmp_nexthops, ecmp_nexthops_cmd, nexthop-vrf NAME$vrf_name \ |label WORD \ |weight (1-255) \ - |backup-idx$bi_str (0-254)$idx \ + |backup-idx WORD \ }]", NO_STR "Specify one of the nexthops in this ECMP group\n" @@ -847,19 +849,26 @@ DEFPY(ecmp_nexthops, ecmp_nexthops_cmd, "One or more labels in the range (16-1048575) separated by '/'\n" "Weight to be used by the nexthop for purposes of ECMP\n" "Weight value to be used\n" - "Backup nexthop index in another group\n" - "Nexthop index value\n") + "Specify backup nexthop indexes in another group\n" + "One or more indexes in the range (0-254) separated by ','\n") { VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc); struct nexthop nhop; struct nexthop *nh; int lbl_ret = 0; bool legal; - int backup_idx = idx; + int num; + uint8_t backups[NEXTHOP_MAX_BACKUPS]; bool yes = !no; - if (bi_str == NULL) - backup_idx = NHH_BACKUP_IDX_INVALID; + /* Pre-parse backup string to validate */ + if (backup_idx) { + lbl_ret = nexthop_str2backups(backup_idx, &num, backups); + if (lbl_ret < 0) { + vty_out(vty, "%% Invalid backups\n"); + return CMD_WARNING_CONFIG_FAILED; + } + } legal = nexthop_group_parse_nexthop(&nhop, addr, intf, vrf_name, label, &lbl_ret, weight, backup_idx); @@ -943,10 +952,11 @@ static struct cmd_node nexthop_group_node = { .config_write = nexthop_group_write, }; -void nexthop_group_write_nexthop(struct vty *vty, struct nexthop *nh) +void nexthop_group_write_nexthop(struct vty *vty, const struct nexthop *nh) { char buf[100]; struct vrf *vrf; + int i; vty_out(vty, "nexthop "); @@ -991,16 +1001,22 @@ void nexthop_group_write_nexthop(struct vty *vty, struct nexthop *nh) if (nh->weight) vty_out(vty, " weight %u", nh->weight); - if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_HAS_BACKUP)) - vty_out(vty, " backup-idx %d", nh->backup_idx); + if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_HAS_BACKUP)) { + vty_out(vty, " backup-idx %d", nh->backup_idx[0]); + + for (i = 1; i < nh->backup_num; i++) + vty_out(vty, ",%d", nh->backup_idx[i]); + } vty_out(vty, "\n"); } -void nexthop_group_json_nexthop(json_object *j, struct nexthop *nh) +void nexthop_group_json_nexthop(json_object *j, const struct nexthop *nh) { char buf[100]; struct vrf *vrf; + json_object *json_backups = NULL; + int i; switch (nh->type) { case NEXTHOP_TYPE_IFINDEX: @@ -1047,12 +1063,19 @@ void nexthop_group_json_nexthop(json_object *j, struct nexthop *nh) if (nh->weight) json_object_int_add(j, "weight", nh->weight); - if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_HAS_BACKUP)) - json_object_int_add(j, "backupIdx", nh->backup_idx); + if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_HAS_BACKUP)) { + json_backups = json_object_new_array(); + for (i = 0; i < nh->backup_num; i++) + json_object_array_add( + json_backups, + json_object_new_int(nh->backup_idx[i])); + + json_object_object_add(j, "backupIdx", json_backups); + } } static void nexthop_group_write_nexthop_internal(struct vty *vty, - struct nexthop_hold *nh) + const struct nexthop_hold *nh) { char buf[100]; @@ -1073,8 +1096,8 @@ static void nexthop_group_write_nexthop_internal(struct vty *vty, if (nh->weight) vty_out(vty, " weight %u", nh->weight); - if (nh->backup_idx != NHH_BACKUP_IDX_INVALID) - vty_out(vty, " backup-idx %d", nh->backup_idx); + if (nh->backup_str) + vty_out(vty, " backup-idx %s", nh->backup_str); vty_out(vty, "\n"); } diff --git a/lib/nexthop_group.h b/lib/nexthop_group.h index 9888dad982..0b5ac91bb2 100644 --- a/lib/nexthop_group.h +++ b/lib/nexthop_group.h @@ -135,9 +135,11 @@ extern bool nexthop_group_equal(const struct nexthop_group *nhg1, extern struct nexthop_group_cmd *nhgc_find(const char *name); -extern void nexthop_group_write_nexthop(struct vty *vty, struct nexthop *nh); +extern void nexthop_group_write_nexthop(struct vty *vty, + const struct nexthop *nh); -extern void nexthop_group_json_nexthop(json_object *j, struct nexthop *nh); +extern void nexthop_group_json_nexthop(json_object *j, + const struct nexthop *nh); /* Return the number of nexthops in this nhg */ extern uint8_t nexthop_group_nexthop_num(const struct nexthop_group *nhg); diff --git a/lib/route_types.pl b/lib/route_types.pl index f297096633..e007de4d69 100755 --- a/lib/route_types.pl +++ b/lib/route_types.pl @@ -121,7 +121,7 @@ sub codelist { } $str =~ s/ $//; push @lines, $str . "\\n\" \\\n"; - push @lines, " \" > - selected route, * - FIB route, q - queued route, r - rejected route\\n\\n\""; + push @lines, " \" > - selected route, * - FIB route, q - queued, r - rejected, b - backup\\n\\n\""; return join("", @lines); } diff --git a/lib/zclient.c b/lib/zclient.c index d8f311fcec..92ff2537d5 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -888,7 +888,7 @@ static void zapi_nexthop_group_sort(struct zapi_nexthop *nh_grp, int zapi_nexthop_encode(struct stream *s, const struct zapi_nexthop *api_nh, uint32_t api_flags) { - int ret = 0; + int i, ret = 0; int nh_flags = api_nh->flags; stream_putl(s, api_nh->vrf_id); @@ -951,8 +951,17 @@ int zapi_nexthop_encode(struct stream *s, const struct zapi_nexthop *api_nh, sizeof(struct ethaddr)); /* Index of backup nexthop */ - if (CHECK_FLAG(nh_flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP)) - stream_putc(s, api_nh->backup_idx); + if (CHECK_FLAG(nh_flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP)) { + /* Validate backup count */ + if (api_nh->backup_num > NEXTHOP_MAX_BACKUPS) { + ret = -1; + goto done; + } + + stream_putc(s, api_nh->backup_num); + for (i = 0; i < api_nh->backup_num; i++) + stream_putc(s, api_nh->backup_idx[i]); + } done: return ret; @@ -1111,7 +1120,7 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api) static int zapi_nexthop_decode(struct stream *s, struct zapi_nexthop *api_nh, uint32_t api_flags) { - int ret = -1; + int i, ret = -1; STREAM_GETL(s, api_nh->vrf_id); STREAM_GETC(s, api_nh->type); @@ -1163,8 +1172,15 @@ static int zapi_nexthop_decode(struct stream *s, struct zapi_nexthop *api_nh, sizeof(struct ethaddr)); /* Backup nexthop index */ - if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP)) - STREAM_GETC(s, api_nh->backup_idx); + if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP)) { + STREAM_GETC(s, api_nh->backup_num); + + if (api_nh->backup_num > NEXTHOP_MAX_BACKUPS) + return -1; + + for (i = 0; i < api_nh->backup_num; i++) + STREAM_GETC(s, api_nh->backup_idx[i]); + } /* Success */ ret = 0; @@ -1483,7 +1499,8 @@ struct nexthop *nexthop_from_zapi_nexthop(const struct zapi_nexthop *znh) if (CHECK_FLAG(znh->flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP)) { SET_FLAG(n->flags, NEXTHOP_FLAG_HAS_BACKUP); - n->backup_idx = znh->backup_idx; + n->backup_num = znh->backup_num; + memcpy(n->backup_idx, znh->backup_idx, n->backup_num); } return n; @@ -1519,8 +1536,12 @@ int zapi_nexthop_from_nexthop(struct zapi_nexthop *znh, } if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_HAS_BACKUP)) { + if (nh->backup_num > NEXTHOP_MAX_BACKUPS) + return -1; + SET_FLAG(znh->flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP); - znh->backup_idx = nh->backup_idx; + znh->backup_num = nh->backup_num; + memcpy(znh->backup_idx, nh->backup_idx, znh->backup_num); } return 0; diff --git a/lib/zclient.h b/lib/zclient.h index 3ded2f55d7..250824e612 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -394,8 +394,9 @@ struct zapi_nexthop { uint32_t weight; - /* Index of backup nexthop */ - uint8_t backup_idx; + /* Backup nexthops, for IP-FRR, TI-LFA, etc */ + uint8_t backup_num; + uint8_t backup_idx[NEXTHOP_MAX_BACKUPS]; }; /* diff --git a/sharpd/sharp_vty.c b/sharpd/sharp_vty.c index 48220d1c9b..1d2b87b9ba 100644 --- a/sharpd/sharp_vty.c +++ b/sharpd/sharp_vty.c @@ -278,7 +278,8 @@ DEFPY (install_routes, if (backup) { /* Set flag and index in primary nexthop */ SET_FLAG(sg.r.nhop.flags, NEXTHOP_FLAG_HAS_BACKUP); - sg.r.nhop.backup_idx = 0; + sg.r.nhop.backup_num = 1; + sg.r.nhop.backup_idx[0] = 0; if (backup_nexthop4.s_addr != INADDR_ANY) { sg.r.backup_nhop.gate.ipv4 = backup_nexthop4; diff --git a/sharpd/sharp_zebra.c b/sharpd/sharp_zebra.c index 7ab2d6ec22..74e44014a9 100644 --- a/sharpd/sharp_zebra.c +++ b/sharpd/sharp_zebra.c @@ -155,6 +155,8 @@ int sharp_install_lsps_helper(bool install_p, const struct prefix *p, return -1; i++; + if (i >= MULTIPATH_NUM) + break; } } @@ -188,6 +190,8 @@ int sharp_install_lsps_helper(bool install_p, const struct prefix *p, return -1; i++; + if (i >= MULTIPATH_NUM) + break; } if (i > 0) diff --git a/zebra/rib.h b/zebra/rib.h index ec992974fa..6ec9241b73 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -144,6 +144,10 @@ struct route_entry { #define ROUTE_ENTRY_INSTALLED 0x10 /* Route has Failed installation into the Data Plane in some manner */ #define ROUTE_ENTRY_FAILED 0x20 +/* Route has a 'fib' set of nexthops, probably because the installed set + * differs from the rib/normal set of nexthops. + */ +#define ROUTE_ENTRY_USE_FIB_NHG 0x40 /* Sequence value incremented for each dataplane operation */ uint32_t dplane_sequence; @@ -526,26 +530,28 @@ DECLARE_HOOK(rib_update, (struct route_node * rn, const char *reason), (rn, reason)) /* - * Access active nexthop-group, either RIB or FIB version + * Access installed/fib nexthops, which may be a subset of the + * rib nexthops. */ static inline struct nexthop_group *rib_get_fib_nhg(struct route_entry *re) { - if (re->fib_ng.nexthop) + /* If the fib set is a subset of the active rib set, + * use the dedicated fib list. + */ + if (CHECK_FLAG(re->status, ROUTE_ENTRY_USE_FIB_NHG)) return &(re->fib_ng); else return &(re->nhe->nhg); } /* - * Access active nexthop-group, either RIB or FIB version + * Access backup nexthop-group that represents the installed backup nexthops; + * any installed backup will be on the fib list. */ static inline struct nexthop_group *rib_get_fib_backup_nhg( struct route_entry *re) { - if (re->fib_backup_ng.nexthop) - return &(re->fib_backup_ng); - else - return zebra_nhg_get_backup_nhg(re->nhe); + return &(re->fib_backup_ng); } extern void zebra_vty_init(void); diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index 062941a1e4..33972681f8 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -1416,6 +1416,7 @@ static struct nexthop *nexthop_from_zapi(struct route_entry *re, struct nexthop *nexthop = NULL; struct ipaddr vtep_ip; struct interface *ifp; + int i; char nhbuf[INET6_ADDRSTRLEN] = ""; switch (api_nh->type) { @@ -1521,17 +1522,36 @@ static struct nexthop *nexthop_from_zapi(struct route_entry *re, nexthop->weight = api_nh->weight; if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP)) { - if (api_nh->backup_idx < api->backup_nexthop_num) { - /* Capture backup info */ - SET_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP); - nexthop->backup_idx = api_nh->backup_idx; - } else { - /* Warn about invalid backup index */ + /* Validate count */ + if (api_nh->backup_num > NEXTHOP_MAX_BACKUPS) { if (IS_ZEBRA_DEBUG_RECV || IS_ZEBRA_DEBUG_EVENT) - zlog_debug("%s: invalid backup nh idx %d", - __func__, api_nh->backup_idx); + zlog_debug("%s: invalid backup nh count %d", + __func__, api_nh->backup_num); + nexthop_free(nexthop); + nexthop = NULL; + goto done; + } + + /* Copy backup info */ + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP); + nexthop->backup_num = api_nh->backup_num; + + for (i = 0; i < api_nh->backup_num; i++) { + /* Validate backup index */ + if (api_nh->backup_idx[i] < api->backup_nexthop_num) { + nexthop->backup_idx[i] = api_nh->backup_idx[i]; + } else { + if (IS_ZEBRA_DEBUG_RECV || IS_ZEBRA_DEBUG_EVENT) + zlog_debug("%s: invalid backup nh idx %d", + __func__, + api_nh->backup_idx[i]); + nexthop_free(nexthop); + nexthop = NULL; + goto done; + } } } + done: return nexthop; } @@ -1703,7 +1723,7 @@ static void zread_route_add(ZAPI_HANDLER_ARGS) __func__, nhbuf); } UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP); - nexthop->backup_idx = 0; + nexthop->backup_num = 0; } /* MPLS labels for BGP-LU or Segment Routing */ @@ -1981,6 +2001,56 @@ static void zread_vrf_unregister(ZAPI_HANDLER_ARGS) } /* + * Validate incoming zapi mpls lsp / labels message + */ +static int zapi_labels_validate(const struct zapi_labels *zl) +{ + int ret = -1; + int i, j, idx; + uint32_t bits[8]; + uint32_t ival; + const struct zapi_nexthop *znh; + + /* Validate backup info: no duplicates for a single primary */ + if (zl->backup_nexthop_num == 0) { + ret = 0; + goto done; + } + + for (j = 0; j < zl->nexthop_num; j++) { + znh = &zl->nexthops[j]; + + memset(bits, 0, sizeof(bits)); + + for (i = 0; i < znh->backup_num; i++) { + idx = znh->backup_idx[i] / 32; + + ival = 1 << znh->backup_idx[i] % 32; + + /* Check whether value is already used */ + if (ival & bits[idx]) { + /* Fail */ + + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: invalid zapi mpls message: duplicate backup nexthop index %d", + __func__, + znh->backup_idx[i]); + goto done; + } + + /* Mark index value */ + bits[idx] |= ival; + } + } + + ret = 0; + +done: + + return ret; +} + +/* * Handle request to create an MPLS LSP. * * A single message can fully specify an LSP with multiple nexthops. @@ -2006,6 +2076,10 @@ static void zread_mpls_labels_add(ZAPI_HANDLER_ARGS) if (!mpls_enabled) return; + /* Validate; will debug on failure */ + if (zapi_labels_validate(&zl) < 0) + return; + ret = mpls_zapi_labels_process(true, zvrf, &zl); if (ret < 0) { if (IS_ZEBRA_DEBUG_RECV) @@ -2087,6 +2161,10 @@ static void zread_mpls_labels_replace(ZAPI_HANDLER_ARGS) if (!mpls_enabled) return; + /* Validate; will debug on failure */ + if (zapi_labels_validate(&zl) < 0) + return; + /* This removes everything, then re-adds from the client's * zapi message. Since the LSP will be processed later, on this * this same pthread, all of the changes will 'appear' at once. diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index 2f20b3cd75..0cc7139d6b 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -2016,10 +2016,19 @@ int dplane_ctx_lsp_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op, break; } - /* Need to copy flags too */ + /* Need to copy flags and backup info too */ new_nhlfe->flags = nhlfe->flags; new_nhlfe->nexthop->flags = nhlfe->nexthop->flags; + if (CHECK_FLAG(new_nhlfe->nexthop->flags, + NEXTHOP_FLAG_HAS_BACKUP)) { + new_nhlfe->nexthop->backup_num = + nhlfe->nexthop->backup_num; + memcpy(new_nhlfe->nexthop->backup_idx, + nhlfe->nexthop->backup_idx, + new_nhlfe->nexthop->backup_num); + } + if (nhlfe == lsp->best_nhlfe) ctx->u.lsp.best_nhlfe = new_nhlfe; } @@ -2119,8 +2128,15 @@ static int dplane_ctx_pw_init(struct zebra_dplane_ctx *ctx, if (re) { nhg = rib_get_fib_nhg(re); - copy_nexthops(&(ctx->u.pw.nhg.nexthop), - nhg->nexthop, NULL); + if (nhg && nhg->nexthop) + copy_nexthops(&(ctx->u.pw.nhg.nexthop), + nhg->nexthop, NULL); + + /* Include any installed backup nexthops */ + nhg = rib_get_fib_backup_nhg(re); + if (nhg && nhg->nexthop) + copy_nexthops(&(ctx->u.pw.nhg.nexthop), + nhg->nexthop, NULL); } route_unlock_node(rn); } @@ -2477,6 +2493,7 @@ dplane_route_notif_update(struct route_node *rn, enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE; struct zebra_dplane_ctx *new_ctx = NULL; struct nexthop *nexthop; + struct nexthop_group *nhg; if (rn == NULL || re == NULL) goto done; @@ -2498,8 +2515,17 @@ dplane_route_notif_update(struct route_node *rn, nexthops_free(new_ctx->u.rinfo.zd_ng.nexthop); new_ctx->u.rinfo.zd_ng.nexthop = NULL; - copy_nexthops(&(new_ctx->u.rinfo.zd_ng.nexthop), - (rib_get_fib_nhg(re))->nexthop, NULL); + nhg = rib_get_fib_nhg(re); + if (nhg && nhg->nexthop) + copy_nexthops(&(new_ctx->u.rinfo.zd_ng.nexthop), + nhg->nexthop, NULL); + + /* Check for installed backup nexthops also */ + nhg = rib_get_fib_backup_nhg(re); + if (nhg && nhg->nexthop) { + copy_nexthops(&(new_ctx->u.rinfo.zd_ng.nexthop), + nhg->nexthop, NULL); + } for (ALL_NEXTHOPS(new_ctx->u.rinfo.zd_ng, nexthop)) UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); @@ -2599,6 +2625,8 @@ dplane_lsp_notif_update(zebra_lsp_t *lsp, enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE; int ret = EINVAL; struct zebra_dplane_ctx *ctx = NULL; + struct nhlfe_list_head *head; + zebra_nhlfe_t *nhlfe, *new_nhlfe; /* Obtain context block */ ctx = dplane_ctx_alloc(); @@ -2607,10 +2635,27 @@ dplane_lsp_notif_update(zebra_lsp_t *lsp, goto done; } + /* Copy info from zebra LSP */ ret = dplane_ctx_lsp_init(ctx, op, lsp); if (ret != AOK) goto done; + /* Add any installed backup nhlfes */ + head = &(ctx->u.lsp.backup_nhlfe_list); + frr_each(nhlfe_list, head, nhlfe) { + + if (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED) && + CHECK_FLAG(nhlfe->nexthop->flags, NEXTHOP_FLAG_FIB)) { + new_nhlfe = zebra_mpls_lsp_add_nh(&(ctx->u.lsp), + nhlfe->type, + nhlfe->nexthop); + + /* Need to copy flags too */ + new_nhlfe->flags = nhlfe->flags; + new_nhlfe->nexthop->flags = nhlfe->nexthop->flags; + } + } + /* Capture info about the source of the notification */ dplane_ctx_set_notif_provider( ctx, @@ -4018,19 +4063,19 @@ bool dplane_is_in_shutdown(void) */ void zebra_dplane_pre_finish(void) { - struct zebra_dplane_provider *dp; + struct zebra_dplane_provider *prov; if (IS_ZEBRA_DEBUG_DPLANE) - zlog_debug("Zebra dataplane pre-fini called"); + zlog_debug("Zebra dataplane pre-finish called"); zdplane_info.dg_is_shutdown = true; /* Notify provider(s) of pending shutdown. */ - TAILQ_FOREACH(dp, &zdplane_info.dg_providers_q, dp_prov_link) { - if (dp->dp_fini == NULL) + TAILQ_FOREACH(prov, &zdplane_info.dg_providers_q, dp_prov_link) { + if (prov->dp_fini == NULL) continue; - dp->dp_fini(dp, true); + prov->dp_fini(prov, true /* early */); } } @@ -4352,7 +4397,10 @@ void zebra_dplane_shutdown(void) zdplane_info.dg_pthread = NULL; zdplane_info.dg_master = NULL; - /* Notify provider(s) of final shutdown. */ + /* Notify provider(s) of final shutdown. + * Note that this call is in the main pthread, so providers must + * be prepared for that. + */ TAILQ_FOREACH(dp, &zdplane_info.dg_providers_q, dp_prov_link) { if (dp->dp_fini == NULL) continue; diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c index 27f1f9396c..f905036323 100644 --- a/zebra/zebra_mpls.c +++ b/zebra/zebra_mpls.c @@ -120,7 +120,8 @@ static int mpls_lsp_uninstall_all(struct hash *lsp_table, zebra_lsp_t *lsp, enum lsp_types_t type); static int mpls_static_lsp_uninstall_all(struct zebra_vrf *zvrf, mpls_label_t in_label); -static void nhlfe_print(zebra_nhlfe_t *nhlfe, struct vty *vty); +static void nhlfe_print(zebra_nhlfe_t *nhlfe, struct vty *vty, + const char *indent); static void lsp_print(struct vty *vty, zebra_lsp_t *lsp); static void *slsp_alloc(void *p); static int snhlfe_match(zebra_snhlfe_t *snhlfe, enum nexthop_types_t gtype, @@ -1503,7 +1504,9 @@ static json_object *nhlfe_json(zebra_nhlfe_t *nhlfe) { char buf[BUFSIZ]; json_object *json_nhlfe = NULL; + json_object *json_backups = NULL; struct nexthop *nexthop = nhlfe->nexthop; + int i; json_nhlfe = json_object_new_object(); json_object_string_add(json_nhlfe, "type", nhlfe_type2str(nhlfe->type)); @@ -1534,13 +1537,27 @@ static json_object *nhlfe_json(zebra_nhlfe_t *nhlfe) default: break; } + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) { + json_backups = json_object_new_array(); + for (i = 0; i < nexthop->backup_num; i++) { + json_object_array_add( + json_backups, + json_object_new_int(nexthop->backup_idx[i])); + } + + json_object_object_add(json_nhlfe, "backupIndex", + json_backups); + } + return json_nhlfe; } /* * Print the NHLFE for a LSP forwarding entry. */ -static void nhlfe_print(zebra_nhlfe_t *nhlfe, struct vty *vty) +static void nhlfe_print(zebra_nhlfe_t *nhlfe, struct vty *vty, + const char *indent) { struct nexthop *nexthop; char buf[MPLS_LABEL_STRLEN]; @@ -1555,6 +1572,10 @@ static void nhlfe_print(zebra_nhlfe_t *nhlfe, struct vty *vty) nexthop->nh_label->label, buf, sizeof(buf), 0), nhlfe->distance); + + if (indent) + vty_out(vty, "%s", indent); + switch (nexthop->type) { case NEXTHOP_TYPE_IPV4: case NEXTHOP_TYPE_IPV4_IFINDEX: @@ -1592,31 +1613,34 @@ static void nhlfe_print(zebra_nhlfe_t *nhlfe, struct vty *vty) static void lsp_print(struct vty *vty, zebra_lsp_t *lsp) { zebra_nhlfe_t *nhlfe, *backup; - int i; + int i, j; vty_out(vty, "Local label: %u%s\n", lsp->ile.in_label, CHECK_FLAG(lsp->flags, LSP_FLAG_INSTALLED) ? " (installed)" : ""); frr_each(nhlfe_list, &lsp->nhlfe_list, nhlfe) { - nhlfe_print(nhlfe, vty); + nhlfe_print(nhlfe, vty, NULL); + + if (nhlfe->nexthop == NULL || + !CHECK_FLAG(nhlfe->nexthop->flags, + NEXTHOP_FLAG_HAS_BACKUP)) + continue; - if (nhlfe->nexthop && - CHECK_FLAG(nhlfe->nexthop->flags, - NEXTHOP_FLAG_HAS_BACKUP)) { - /* Find backup in backup list */ + /* Backup nhlfes: find backups in backup list */ + for (j = 0; j < nhlfe->nexthop->backup_num; j++) { i = 0; backup = NULL; frr_each(nhlfe_list, &lsp->backup_nhlfe_list, backup) { - if (i == nhlfe->nexthop->backup_idx) + if (i == nhlfe->nexthop->backup_idx[j]) break; i++; } if (backup) { vty_out(vty, " [backup %d]", i); - nhlfe_print(backup, vty); + nhlfe_print(backup, vty, " "); } } } @@ -1640,6 +1664,19 @@ static json_object *lsp_json(zebra_lsp_t *lsp) json_object_array_add(json_nhlfe_list, nhlfe_json(nhlfe)); json_object_object_add(json, "nexthops", json_nhlfe_list); + json_nhlfe_list = NULL; + + + frr_each(nhlfe_list, &lsp->backup_nhlfe_list, nhlfe) { + if (json_nhlfe_list == NULL) + json_nhlfe_list = json_object_new_array(); + + json_object_array_add(json_nhlfe_list, nhlfe_json(nhlfe)); + } + + if (json_nhlfe_list) + json_object_object_add(json, "backupNexthops", json_nhlfe_list); + return json; } @@ -2109,6 +2146,7 @@ static int update_nhlfes_from_ctx(struct nhlfe_list_head *nhlfe_head, __func__, buf); SET_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED); + SET_FLAG(nhlfe->flags, NHLFE_FLAG_SELECTED); } else { if (is_debug) @@ -2116,6 +2154,7 @@ static int update_nhlfes_from_ctx(struct nhlfe_list_head *nhlfe_head, __func__, buf); UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED); + UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_SELECTED); } if (CHECK_FLAG(ctx_nhlfe->nexthop->flags, @@ -2137,6 +2176,7 @@ static int update_nhlfes_from_ctx(struct nhlfe_list_head *nhlfe_head, zlog_debug("%s: no match for lsp nhlfe %s", __func__, buf); UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED); + UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_SELECTED); UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); } @@ -3332,7 +3372,14 @@ static int lsp_znh_install(zebra_lsp_t *lsp, enum lsp_types_t type, /* Update backup info if present */ if (CHECK_FLAG(znh->flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP)) { - nhlfe->nexthop->backup_idx = znh->backup_idx; + if (znh->backup_num > NEXTHOP_MAX_BACKUPS) { + nhlfe_del(nhlfe); + return -1; + } + + nhlfe->nexthop->backup_num = znh->backup_num; + memcpy(nhlfe->nexthop->backup_idx, znh->backup_idx, + znh->backup_num); SET_FLAG(nhlfe->nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP); } diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c index 9bfd7aacb7..c058090844 100644 --- a/zebra/zebra_nhg.c +++ b/zebra/zebra_nhg.c @@ -1951,8 +1951,11 @@ static int nexthop_active(afi_t afi, struct route_entry *re, goto done_with_match; } - /* Examine installed nexthops */ - nhg = &match->nhe->nhg; + /* Examine installed nexthops; note that there + * may not be any installed primary nexthops if + * only backups are installed. + */ + nhg = rib_get_fib_nhg(match); for (ALL_NEXTHOPS_PTR(nhg, newhop)) { if (!nexthop_valid_resolve(nexthop, newhop)) continue; @@ -1973,8 +1976,7 @@ static int nexthop_active(afi_t afi, struct route_entry *re, * dedicated fib list. */ nhg = rib_get_fib_backup_nhg(match); - if (nhg == NULL || - nhg == zebra_nhg_get_backup_nhg(match->nhe)) + if (nhg == NULL || nhg->nexthop == NULL) goto done_with_match; for (ALL_NEXTHOPS_PTR(nhg, newhop)) { diff --git a/zebra/zebra_pw.c b/zebra/zebra_pw.c index c00cd88fe3..cdcca1e930 100644 --- a/zebra/zebra_pw.c +++ b/zebra/zebra_pw.c @@ -517,6 +517,7 @@ static void vty_show_mpls_pseudowire_detail(struct vty *vty) struct zebra_pw *pw; struct route_entry *re; struct nexthop *nexthop; + struct nexthop_group *nhg; zvrf = vrf_info_lookup(VRF_DEFAULT); if (!zvrf) @@ -544,22 +545,41 @@ static void vty_show_mpls_pseudowire_detail(struct vty *vty) vty_out(vty, " VC-ID: %u\n", pw->data.ldp.pwid); vty_out(vty, " Status: %s \n", (zebra_pw_enabled(pw) && pw->status == PW_FORWARDING) - ? "Up" - : "Down"); + ? "Up" + : "Down"); re = rib_match(family2afi(pw->af), SAFI_UNICAST, pw->vrf_id, &pw->nexthop, NULL); - if (re) { - for (ALL_NEXTHOPS_PTR(rib_get_fib_nhg(re), nexthop)) { - snprintfrr(buf_nh, sizeof(buf_nh), "%pNHv", - nexthop); - vty_out(vty, " Next Hop: %s\n", buf_nh); - if (nexthop->nh_label) - vty_out(vty, " Next Hop label: %u\n", - nexthop->nh_label->label[0]); - else - vty_out(vty, " Next Hop label: %s\n", - "-"); - } + if (re == NULL) + continue; + + nhg = rib_get_fib_nhg(re); + for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { + snprintfrr(buf_nh, sizeof(buf_nh), "%pNHv", + nexthop); + vty_out(vty, " Next Hop: %s\n", buf_nh); + if (nexthop->nh_label) + vty_out(vty, " Next Hop label: %u\n", + nexthop->nh_label->label[0]); + else + vty_out(vty, " Next Hop label: %s\n", + "-"); + } + + /* Include any installed backups */ + nhg = rib_get_fib_backup_nhg(re); + if (nhg == NULL) + continue; + + for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { + snprintfrr(buf_nh, sizeof(buf_nh), "%pNHv", + nexthop); + vty_out(vty, " Next Hop: %s\n", buf_nh); + if (nexthop->nh_label) + vty_out(vty, " Next Hop label: %u\n", + nexthop->nh_label->label[0]); + else + vty_out(vty, " Next Hop label: %s\n", + "-"); } } } @@ -568,6 +588,7 @@ static void vty_show_mpls_pseudowire(struct zebra_pw *pw, json_object *json_pws) { struct route_entry *re; struct nexthop *nexthop; + struct nexthop_group *nhg; char buf_nbr[INET6_ADDRSTRLEN]; char buf_nh[100]; json_object *json_pw = NULL; @@ -602,23 +623,48 @@ static void vty_show_mpls_pseudowire(struct zebra_pw *pw, json_object *json_pws) : "Down"); re = rib_match(family2afi(pw->af), SAFI_UNICAST, pw->vrf_id, &pw->nexthop, NULL); - if (re) { - for (ALL_NEXTHOPS_PTR(rib_get_fib_nhg(re), nexthop)) { - json_nexthop = json_object_new_object(); - snprintfrr(buf_nh, sizeof(buf_nh), "%pNHv", nexthop); - json_object_string_add(json_nexthop, "nexthop", buf_nh); - if (nexthop->nh_label) - json_object_int_add( - json_nexthop, "nhLabel", - nexthop->nh_label->label[0]); - else - json_object_string_add(json_nexthop, "nhLabel", - "-"); + if (re == NULL) + goto done; + + nhg = rib_get_fib_nhg(re); + for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { + json_nexthop = json_object_new_object(); + snprintfrr(buf_nh, sizeof(buf_nh), "%pNHv", nexthop); + json_object_string_add(json_nexthop, "nexthop", buf_nh); + if (nexthop->nh_label) + json_object_int_add( + json_nexthop, "nhLabel", + nexthop->nh_label->label[0]); + else + json_object_string_add(json_nexthop, "nhLabel", + "-"); - json_object_array_add(json_nexthops, json_nexthop); - } - json_object_object_add(json_pw, "nexthops", json_nexthops); + json_object_array_add(json_nexthops, json_nexthop); + } + + /* Include installed backup nexthops also */ + nhg = rib_get_fib_backup_nhg(re); + if (nhg == NULL) + goto done; + + for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { + json_nexthop = json_object_new_object(); + snprintfrr(buf_nh, sizeof(buf_nh), "%pNHv", nexthop); + json_object_string_add(json_nexthop, "nexthop", buf_nh); + if (nexthop->nh_label) + json_object_int_add( + json_nexthop, "nhLabel", + nexthop->nh_label->label[0]); + else + json_object_string_add(json_nexthop, "nhLabel", + "-"); + + json_object_array_add(json_nexthops, json_nexthop); } + +done: + + json_object_object_add(json_pw, "nexthops", json_nexthops); json_object_array_add(json_pws, json_pw); } diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 15c491ae72..ee9d5d76e0 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -472,6 +472,7 @@ void rib_install_kernel(struct route_node *rn, struct route_entry *re, SET_FLAG(old->status, ROUTE_ENTRY_QUEUED); /* Free old FIB nexthop group */ + UNSET_FLAG(old->status, ROUTE_ENTRY_USE_FIB_NHG); if (old->fib_ng.nexthop) { nexthops_free(old->fib_ng.nexthop); old->fib_ng.nexthop = NULL; @@ -574,6 +575,7 @@ static void rib_uninstall(struct route_node *rn, struct route_entry *re) nexthops_free(re->fib_ng.nexthop); re->fib_ng.nexthop = NULL; } + UNSET_FLAG(re->status, ROUTE_ENTRY_USE_FIB_NHG); for (ALL_NEXTHOPS(re->nhe->nhg, nexthop)) UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); @@ -1375,7 +1377,7 @@ static bool rib_update_nhg_from_ctx(struct nexthop_group *re_nhg, continue; /* Check for a FIB nexthop corresponding to the RIB nexthop */ - if (nexthop_same(ctx_nexthop, nexthop) == false) { + if (!nexthop_same(ctx_nexthop, nexthop)) { /* If the FIB doesn't know about the nexthop, * it's not installed */ @@ -1490,7 +1492,7 @@ static bool rib_update_re_from_ctx(struct route_entry *re, VRF_LOGNAME(vrf), re->vrf_id, dest_str); goto check_backups; - } else if (re->fib_ng.nexthop) { + } else if (CHECK_FLAG(re->status, ROUTE_ENTRY_USE_FIB_NHG)) { /* * Free stale fib list and move on to check the rib nhg. */ @@ -1501,6 +1503,8 @@ static bool rib_update_re_from_ctx(struct route_entry *re, nexthops_free(re->fib_ng.nexthop); re->fib_ng.nexthop = NULL; + UNSET_FLAG(re->status, ROUTE_ENTRY_USE_FIB_NHG); + /* Note that the installed nexthops have changed */ changed_p = true; } else { @@ -1546,20 +1550,15 @@ no_nexthops: */ if (IS_ZEBRA_DEBUG_RIB) zlog_debug( - "%s(%u):%s update_from_ctx(): changed %s, adding new fib nhg", + "%s(%u):%s update_from_ctx(): changed %s, adding new fib nhg%s", VRF_LOGNAME(vrf), re->vrf_id, dest_str, - (changed_p ? "true" : "false")); + (changed_p ? "true" : "false"), + ctxnhg->nexthop != NULL ? "" : " (empty)"); + /* Set the flag about the dedicated fib list */ + SET_FLAG(re->status, ROUTE_ENTRY_USE_FIB_NHG); if (ctxnhg->nexthop) copy_nexthops(&(re->fib_ng.nexthop), ctxnhg->nexthop, NULL); - else { - /* Bit of a special case when the fib has _no_ installed - * nexthops. - */ - nexthop = nexthop_new(); - nexthop->type = NEXTHOP_TYPE_IPV4; - _nexthop_add(&(re->fib_ng.nexthop), nexthop); - } check_backups: @@ -1611,7 +1610,7 @@ check_backups: } /* - * If a FIB backup nexthop set exists: attach a copy + * If a FIB backup nexthop set exists, attach a copy * to the route if any backup is installed */ if (ctxnhg && ctxnhg->nexthop) { @@ -2632,6 +2631,8 @@ static void _route_entry_dump_nh(const struct route_entry *re, char nhname[PREFIX_STRLEN]; char backup_str[50]; char wgt_str[50]; + char temp_str[10]; + int i; struct interface *ifp; struct vrf *vrf = vrf_lookup_by_id(nexthop->vrf_id); @@ -2657,8 +2658,12 @@ static void _route_entry_dump_nh(const struct route_entry *re, backup_str[0] = '\0'; if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) { - snprintf(backup_str, sizeof(backup_str), "backup %d,", - (int)nexthop->backup_idx); + snprintf(backup_str, sizeof(backup_str), "backup "); + for (i = 0; i < nexthop->backup_num; i++) { + snprintf(temp_str, sizeof(temp_str), "%d, ", + nexthop->backup_idx[i]); + strlcat(backup_str, temp_str, sizeof(backup_str)); + } } wgt_str[0] = '\0'; diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c index b8b245cebd..b151e90a92 100644 --- a/zebra/zebra_rnh.c +++ b/zebra/zebra_rnh.c @@ -1040,22 +1040,10 @@ static bool compare_valid_nexthops(struct route_entry *r1, * backups will be in the 'fib' list. */ nhg1 = rib_get_fib_backup_nhg(r1); - if (nhg1 == zebra_nhg_get_backup_nhg(r1->nhe)) - nhg1 = NULL; - nhg2 = rib_get_fib_backup_nhg(r2); - if (nhg2 == zebra_nhg_get_backup_nhg(r2->nhe)) - nhg2 = NULL; - - if (nhg1) - nh1 = nhg1->nexthop; - else - nh1 = NULL; - if (nhg2) - nh2 = nhg2->nexthop; - else - nh2 = NULL; + nh1 = nhg1->nexthop; + nh2 = nhg2->nexthop; while (1) { /* Find each backup list's next valid nexthop */ @@ -1180,9 +1168,6 @@ static int send_client(struct rnh *rnh, struct zserv *client, } nhg = rib_get_fib_backup_nhg(re); - if (nhg == zebra_nhg_get_backup_nhg(re->nhe)) - nhg = NULL; - if (nhg) { for (ALL_NEXTHOPS_PTR(nhg, nh)) if (rnh_nexthop_valid(re, nh)) { diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index b18871dd2a..bc9dada255 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -71,6 +71,12 @@ static void vty_show_ip_route_summary(struct vty *vty, static void vty_show_ip_route_summary_prefix(struct vty *vty, struct route_table *table, bool use_json); +/* Helper api to format a nexthop in the 'detailed' output path. */ +static void show_nexthop_detail_helper(struct vty *vty, + const struct route_entry *re, + const struct nexthop *nexthop, + bool is_backup); + DEFUN (ip_multicast_mode, ip_multicast_mode_cmd, @@ -166,11 +172,24 @@ DEFUN (show_ip_rpf_addr, } static char re_status_output_char(const struct route_entry *re, - const struct nexthop *nhop) + const struct nexthop *nhop, + bool is_fib) { if (CHECK_FLAG(re->status, ROUTE_ENTRY_INSTALLED)) { - if (!CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_DUPLICATE) && - !CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_RECURSIVE)) + bool star_p = false; + + if (nhop && + !CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_DUPLICATE) && + !CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_RECURSIVE)) { + /* More-specific test for 'fib' output */ + if (is_fib) { + star_p = !!CHECK_FLAG(nhop->flags, + NEXTHOP_FLAG_FIB); + } else + star_p = true; + } + + if (star_p) return '*'; else return ' '; @@ -190,19 +209,51 @@ static char re_status_output_char(const struct route_entry *re, } /* - * TODO -- Show backup nexthop info + * Show backup nexthop info, in the 'detailed' output path */ static void show_nh_backup_helper(struct vty *vty, - const struct nhg_hash_entry *nhe, + const struct route_entry *re, const struct nexthop *nexthop) { + const struct nexthop *start, *backup, *temp; + int i, idx; + /* Double-check that there _is_ a backup */ - if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) + if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP) || + re->nhe->backup_info == NULL || re->nhe->backup_info->nhe == NULL || + re->nhe->backup_info->nhe->nhg.nexthop == NULL) return; - /* Locate the backup nexthop */ + /* Locate the backup nexthop(s) */ + start = re->nhe->backup_info->nhe->nhg.nexthop; + for (i = 0; i < nexthop->backup_num; i++) { + /* Format the backup(s) (indented) */ + backup = start; + for (idx = 0; idx < nexthop->backup_idx[i]; idx++) { + backup = backup->next; + if (backup == NULL) + break; + } - /* Format the backup (indented) */ + /* It's possible for backups to be recursive too, + * so walk the recursive resolution list if present. + */ + temp = backup; + while (backup) { + vty_out(vty, " "); + show_nexthop_detail_helper(vty, re, backup, + true /*backup*/); + vty_out(vty, "\n"); + + if (backup->resolved && temp == backup) + backup = backup->resolved; + else + backup = nexthop_next(backup); + + if (backup == temp->next) + break; + } + } } @@ -212,14 +263,20 @@ static void show_nh_backup_helper(struct vty *vty, */ static void show_nexthop_detail_helper(struct vty *vty, const struct route_entry *re, - const struct nexthop *nexthop) + const struct nexthop *nexthop, + bool is_backup) { char addrstr[32]; char buf[MPLS_LABEL_STRLEN]; + int i; - vty_out(vty, " %c%s", - re_status_output_char(re, nexthop), - nexthop->rparent ? " " : ""); + if (is_backup) + vty_out(vty, " b%s", + nexthop->rparent ? " " : ""); + else + vty_out(vty, " %c%s", + re_status_output_char(re, nexthop, false), + nexthop->rparent ? " " : ""); switch (nexthop->type) { case NEXTHOP_TYPE_IPV4: @@ -333,6 +390,13 @@ static void show_nexthop_detail_helper(struct vty *vty, if (nexthop->weight) vty_out(vty, ", weight %u", nexthop->weight); + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) { + vty_out(vty, ", backup %d", nexthop->backup_idx[0]); + + for (i = 1; i < nexthop->backup_num; i++) + vty_out(vty, ",%d", nexthop->backup_idx[i]); + } } /* New RIB. Detailed information for IPv4 route. */ @@ -403,12 +467,13 @@ static void vty_show_ip_route_detail(struct vty *vty, struct route_node *rn, for (ALL_NEXTHOPS(re->nhe->nhg, nexthop)) { /* Use helper to format each nexthop */ - show_nexthop_detail_helper(vty, re, nexthop); + show_nexthop_detail_helper(vty, re, nexthop, + false /*not backup*/); vty_out(vty, "\n"); - /* Include backup info, if present */ + /* Include backup(s), if present */ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) - show_nh_backup_helper(vty, re->nhe, nexthop); + show_nh_backup_helper(vty, re, nexthop); } vty_out(vty, "\n"); } @@ -422,6 +487,7 @@ static void show_route_nexthop_helper(struct vty *vty, const struct nexthop *nexthop) { char buf[MPLS_LABEL_STRLEN]; + int i; switch (nexthop->type) { case NEXTHOP_TYPE_IPV4: @@ -519,8 +585,12 @@ static void show_route_nexthop_helper(struct vty *vty, if (nexthop->weight) vty_out(vty, ", weight %u", nexthop->weight); - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) - vty_out(vty, ", backup %d", nexthop->backup_idx); + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) { + vty_out(vty, ", backup %d", nexthop->backup_idx[0]); + + for (i = 1; i < nexthop->backup_num; i++) + vty_out(vty, ",%d", nexthop->backup_idx[i]); + } } /* @@ -534,6 +604,8 @@ static void show_nexthop_json_helper(json_object *json_nexthop, char buf[SRCDEST2STR_BUFFER]; struct vrf *vrf = NULL; json_object *json_labels = NULL; + json_object *json_backups = NULL; + int i; json_object_int_add(json_nexthop, "flags", nexthop->flags); @@ -645,9 +717,17 @@ static void show_nexthop_json_helper(json_object *json_nexthop, json_object_boolean_true_add(json_nexthop, "recursive"); - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) - json_object_int_add(json_nexthop, "backupIndex", - nexthop->backup_idx); + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) { + json_backups = json_object_new_array(); + for (i = 0; i < nexthop->backup_num; i++) { + json_object_array_add( + json_backups, + json_object_new_int(nexthop->backup_idx[i])); + } + + json_object_object_add(json_nexthop, "backupIndex", + json_backups); + } switch (nexthop->type) { case NEXTHOP_TYPE_IPV4: @@ -705,18 +785,19 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn, struct route_entry *re, json_object *json, bool is_fib) { - struct nexthop *nexthop; + const struct nexthop *nexthop; int len = 0; char buf[SRCDEST2STR_BUFFER]; json_object *json_nexthops = NULL; json_object *json_nexthop = NULL; json_object *json_route = NULL; time_t uptime; - struct vrf *vrf = NULL; - rib_dest_t *dest = rib_dest_from_rnode(rn); - struct nexthop_group *nhg; + const struct vrf *vrf = NULL; + const rib_dest_t *dest = rib_dest_from_rnode(rn); + const struct nexthop_group *nhg; char up_str[MONOTIME_STRLEN]; - bool first_p; + bool first_p = true; + bool nhg_from_backup = false; uptime = monotime(NULL); uptime -= re->uptime; @@ -791,9 +872,11 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn, for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { json_nexthop = json_object_new_object(); + show_nexthop_json_helper(json_nexthop, + nexthop, re); - show_nexthop_json_helper(json_nexthop, nexthop, re); - json_object_array_add(json_nexthops, json_nexthop); + json_object_array_add(json_nexthops, + json_nexthop); } json_object_object_add(json_route, "nexthops", json_nexthops); @@ -804,7 +887,7 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn, else nhg = zebra_nhg_get_backup_nhg(re->nhe); - if (nhg) { + if (nhg && nhg->nexthop) { json_nexthops = json_object_new_array(); for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { @@ -824,42 +907,62 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn, return; } + /* Prefix information, and first nexthop. If we're showing 'fib', + * and there are no installed primary nexthops, see if there are any + * backup nexthops and start with those. + */ + if (is_fib && nhg->nexthop == NULL) { + nhg = rib_get_fib_backup_nhg(re); + nhg_from_backup = true; + } + + len = vty_out(vty, "%c", zebra_route_char(re->type)); + if (re->instance) + len += vty_out(vty, "[%d]", re->instance); + if (nhg_from_backup && nhg->nexthop) { + len += vty_out( + vty, "%cb%c %s", + CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED) ? '>' : ' ', + re_status_output_char(re, nhg->nexthop, is_fib), + srcdest_rnode2str(rn, buf, sizeof(buf))); + } else { + len += vty_out( + vty, "%c%c %s", + CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED) ? '>' : ' ', + re_status_output_char(re, nhg->nexthop, is_fib), + srcdest_rnode2str(rn, buf, sizeof(buf))); + } + + /* Distance and metric display. */ + if (((re->type == ZEBRA_ROUTE_CONNECT) && + (re->distance || re->metric)) || + (re->type != ZEBRA_ROUTE_CONNECT)) + len += vty_out(vty, " [%u/%u]", re->distance, + re->metric); + /* Nexthop information. */ - first_p = true; for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { if (first_p) { first_p = false; - - /* Prefix information. */ - len = vty_out(vty, "%c", zebra_route_char(re->type)); - if (re->instance) - len += vty_out(vty, "[%d]", re->instance); - len += vty_out( - vty, "%c%c %s", - CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED) - ? '>' - : ' ', - re_status_output_char(re, nexthop), - srcdest_rnode2str(rn, buf, sizeof(buf))); - - /* Distance and metric display. */ - if (((re->type == ZEBRA_ROUTE_CONNECT) && - (re->distance || re->metric)) || - (re->type != ZEBRA_ROUTE_CONNECT)) - len += vty_out(vty, " [%u/%u]", re->distance, - re->metric); + } else if (nhg_from_backup) { + vty_out(vty, " b%c%*c", + re_status_output_char(re, nexthop, is_fib), + len - 3 + (2 * nexthop_level(nexthop)), ' '); } else { vty_out(vty, " %c%*c", - re_status_output_char(re, nexthop), + re_status_output_char(re, nexthop, is_fib), len - 3 + (2 * nexthop_level(nexthop)), ' '); } show_route_nexthop_helper(vty, re, nexthop); - vty_out(vty, ", %s\n", up_str); } - /* Check for backup info if present */ + /* If we only had backup nexthops, we're done */ + if (nhg_from_backup) + return; + + /* Check for backup nexthop info if present */ if (is_fib) nhg = rib_get_fib_backup_nhg(re); else @@ -1206,7 +1309,7 @@ static void show_nexthop_group_out(struct vty *vty, struct nhg_hash_entry *nhe) if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) vty_out(vty, " [backup %d]", - nexthop->backup_idx); + nexthop->backup_idx[0]); vty_out(vty, "\n"); continue; @@ -1214,22 +1317,13 @@ static void show_nexthop_group_out(struct vty *vty, struct nhg_hash_entry *nhe) /* TODO -- print more useful backup info */ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) { - struct nexthop *backup; int i; - i = 0; - for (ALL_NEXTHOPS(nhe->backup_info->nhe->nhg, backup)) { - if (i == nexthop->backup_idx) - break; - i++; - } + vty_out(vty, "[backup"); + for (i = 0; i < nexthop->backup_num; i++) + vty_out(vty, " %d", nexthop->backup_idx[i]); - /* TODO */ - if (backup) - vty_out(vty, " [backup %d]", - nexthop->backup_idx); - else - vty_out(vty, " [backup INVALID]"); + vty_out(vty, "]"); } vty_out(vty, "\n"); |
