diff options
69 files changed, 2469 insertions, 763 deletions
diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index d2257b0126..1a0e5c0cd3 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -4075,7 +4075,7 @@ DEFUN(show_bgp_l2vpn_evpn_es, */ DEFUN(show_bgp_l2vpn_evpn_summary, show_bgp_l2vpn_evpn_summary_cmd, - "show bgp [vrf VRFNAME] l2vpn evpn summary [failed] [json]", + "show bgp [vrf VRFNAME] l2vpn evpn summary [established|failed] [json]", SHOW_STR BGP_STR "bgp vrf\n" @@ -4083,6 +4083,7 @@ DEFUN(show_bgp_l2vpn_evpn_summary, L2VPN_HELP_STR EVPN_HELP_STR "Summary of BGP neighbor status\n" + "Show only sessions in Established state\n" "Show only sessions not in Established state\n" JSON_STR) { @@ -4090,13 +4091,17 @@ DEFUN(show_bgp_l2vpn_evpn_summary, bool uj = use_json(argc, argv); char *vrf = NULL; bool show_failed = false; + bool show_established = false; if (argv_find(argv, argc, "vrf", &idx_vrf)) vrf = argv[++idx_vrf]->arg; if (argv_find(argv, argc, "failed", &idx_vrf)) show_failed = true; - return bgp_show_summary_vty(vty, vrf, AFI_L2VPN, SAFI_EVPN, - show_failed, uj); + if (argv_find(argv, argc, "established", &idx_vrf)) + show_established = true; + + return bgp_show_summary_vty(vty, vrf, AFI_L2VPN, SAFI_EVPN, show_failed, + show_established, uj); } /* diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 449dab12b0..c38db2eb67 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -8861,7 +8861,8 @@ static void bgp_show_failed_summary(struct vty *vty, struct bgp *bgp, /* Show BGP peer's summary information. */ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, - bool show_failed, bool use_json) + bool show_failed, bool show_established, + bool use_json) { struct peer *peer; struct listnode *node, *nnode; @@ -9194,6 +9195,10 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, bgp_show_failed_summary(vty, bgp, peer, json_peer, 0, use_json); } else if (!show_failed) { + if (show_established + && bgp_has_peer_failed(peer, afi, safi)) + continue; + json_peer = json_object_new_object(); if (peer_dynamic_neighbor(peer)) { json_object_boolean_true_add(json_peer, @@ -9283,6 +9288,10 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, max_neighbor_width, use_json); } else if (!show_failed) { + if (show_established + && bgp_has_peer_failed(peer, afi, safi)) + continue; + memset(dn_flag, '\0', sizeof(dn_flag)); if (peer_dynamic_neighbor(peer)) { dn_flag[0] = '*'; @@ -9405,7 +9414,8 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, } static void bgp_show_summary_afi_safi(struct vty *vty, struct bgp *bgp, int afi, - int safi, bool show_failed, bool use_json) + int safi, bool show_failed, + bool show_established, bool use_json) { int is_first = 1; int afi_wildcard = (afi == AFI_MAX); @@ -9448,7 +9458,8 @@ static void bgp_show_summary_afi_safi(struct vty *vty, struct bgp *bgp, int afi, false)); } } - bgp_show_summary(vty, bgp, afi, safi, show_failed, + bgp_show_summary(vty, bgp, afi, safi, + show_failed, show_established, use_json); } safi++; @@ -9472,6 +9483,7 @@ static void bgp_show_summary_afi_safi(struct vty *vty, struct bgp *bgp, int afi, static void bgp_show_all_instances_summary_vty(struct vty *vty, afi_t afi, safi_t safi, bool show_failed, + bool show_established, bool use_json) { struct listnode *node, *nnode; @@ -9501,7 +9513,7 @@ static void bgp_show_all_instances_summary_vty(struct vty *vty, afi_t afi, : bgp->name); } bgp_show_summary_afi_safi(vty, bgp, afi, safi, show_failed, - use_json); + show_established, use_json); } if (use_json) @@ -9511,15 +9523,16 @@ static void bgp_show_all_instances_summary_vty(struct vty *vty, afi_t afi, } int bgp_show_summary_vty(struct vty *vty, const char *name, afi_t afi, - safi_t safi, bool show_failed, bool use_json) + safi_t safi, bool show_failed, bool show_established, + bool use_json) { struct bgp *bgp; if (name) { if (strmatch(name, "all")) { - bgp_show_all_instances_summary_vty(vty, afi, safi, - show_failed, - use_json); + bgp_show_all_instances_summary_vty( + vty, afi, safi, show_failed, show_established, + use_json); return CMD_SUCCESS; } else { bgp = bgp_lookup_by_name(name); @@ -9534,7 +9547,8 @@ int bgp_show_summary_vty(struct vty *vty, const char *name, afi_t afi, } bgp_show_summary_afi_safi(vty, bgp, afi, safi, - show_failed, use_json); + show_failed, show_established, + use_json); return CMD_SUCCESS; } } @@ -9543,7 +9557,7 @@ int bgp_show_summary_vty(struct vty *vty, const char *name, afi_t afi, if (bgp) bgp_show_summary_afi_safi(vty, bgp, afi, safi, show_failed, - use_json); + show_established, use_json); else { if (use_json) vty_out(vty, "{}\n"); @@ -9558,7 +9572,7 @@ int bgp_show_summary_vty(struct vty *vty, const char *name, afi_t afi, /* `show [ip] bgp summary' commands. */ DEFUN (show_ip_bgp_summary, show_ip_bgp_summary_cmd, - "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] summary [failed] [json]", + "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] summary [established|failed] [json]", SHOW_STR IP_STR BGP_STR @@ -9566,6 +9580,7 @@ DEFUN (show_ip_bgp_summary, BGP_AFI_HELP_STR BGP_SAFI_WITH_LABEL_HELP_STR "Summary of BGP neighbor status\n" + "Show only sessions in Established state\n" "Show only sessions not in Established state\n" JSON_STR) { @@ -9573,6 +9588,7 @@ DEFUN (show_ip_bgp_summary, afi_t afi = AFI_MAX; safi_t safi = SAFI_MAX; bool show_failed = false; + bool show_established = false; int idx = 0; @@ -9594,10 +9610,13 @@ DEFUN (show_ip_bgp_summary, if (argv_find(argv, argc, "failed", &idx)) show_failed = true; + if (argv_find(argv, argc, "established", &idx)) + show_established = true; bool uj = use_json(argc, argv); - return bgp_show_summary_vty(vty, vrf, afi, safi, show_failed, uj); + return bgp_show_summary_vty(vty, vrf, afi, safi, show_failed, + show_established, uj); } const char *get_afi_safi_str(afi_t afi, safi_t safi, bool for_json) diff --git a/bgpd/bgp_vty.h b/bgpd/bgp_vty.h index d6ca198d09..95eefbc36f 100644 --- a/bgpd/bgp_vty.h +++ b/bgpd/bgp_vty.h @@ -178,6 +178,7 @@ extern int bgp_vty_find_and_parse_afi_safi_bgp(struct vty *vty, int bgp_vty_find_and_parse_bgp(struct vty *vty, struct cmd_token **argv, int argc, struct bgp **bgp, bool use_json); extern int bgp_show_summary_vty(struct vty *vty, const char *name, afi_t afi, - safi_t safi, bool show_failed, bool use_json); + safi_t safi, bool show_failed, + bool show_established, bool use_json); #endif /* _QUAGGA_BGP_VTY_H */ diff --git a/doc/user/bfd.rst b/doc/user/bfd.rst index 2586b9b988..618d90a85e 100644 --- a/doc/user/bfd.rst +++ b/doc/user/bfd.rst @@ -143,7 +143,7 @@ BFD peers and profiles share the same BFD session configuration commands. .. clicmd:: transmit-interval (10-60000) The minimum transmission interval (less jitter) that this system - wants to use to send BFD control packets. + wants to use to send BFD control packets. Defaults to 300ms. .. index:: echo-interval (10-60000) .. clicmd:: echo-interval (10-60000) @@ -159,7 +159,7 @@ BFD peers and profiles share the same BFD session configuration commands. It is recommended that the transmission interval of control packets to be increased after enabling echo-mode to reduce bandwidth usage. - For example: `transmission-interval 2000`. + For example: `transmit-interval 2000`. Echo mode is not supported on multi-hop setups (see :rfc:`5883` section 3). @@ -246,6 +246,30 @@ The following commands are available inside the BGP configuration node. Removes the BFD profile configuration from peer session(s). +.. _bfd-isis-peer-config: + +IS-IS BFD Configuration +----------------------- + +The following commands are available inside the interface configuration node. + +.. index:: isis bfd +.. clicmd:: ip isis bfd + + Listen for BFD events on peers created on the interface. Every time + a new neighbor is found a BFD peer is created to monitor the link + status for fast convergence. + +.. index:: no isis bfd +.. clicmd:: no isis bfd + + Removes any notification registration for this interface peers. + + Note that there will be just one BFD session per interface. In case both + IPv4 and IPv6 support are configured then just a IPv6 based session is + created. + + .. _bfd-ospf-peer-config: OSPF BFD Configuration diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index 388d479bab..8b56834d80 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -2731,6 +2731,12 @@ structure is extended with :clicmd:`show bgp [afi] [safi]`. Show a bgp peer summary for peers that are not succesfully exchanging routes for the specified address family, and subsequent address-family. +.. index:: show bgp [afi] [safi] summary established [json] +.. clicmd:: show bgp [afi] [safi] summary established [json] + + Show a bgp peer summary for peers that are succesfully exchanging routes + for the specified address family, and subsequent address-family. + .. index:: show bgp [afi] [safi] neighbor [PEER] .. clicmd:: show bgp [afi] [safi] neighbor [PEER] diff --git a/doc/user/pim.rst b/doc/user/pim.rst index 8834d28abb..919dc51f69 100644 --- a/doc/user/pim.rst +++ b/doc/user/pim.rst @@ -166,10 +166,22 @@ Certain signals have special meanings to *pimd*. urib-only Lookup in the Unicast Rib only. -.. index:: no ip msdp mesh-group [WORD] -.. clicmd:: no ip msdp mesh-group [WORD] +.. index:: [no] ip msdp mesh-group [WORD] +.. clicmd:: [no] ip msdp mesh-group [WORD] - Delete multicast source discovery protocol mesh-group + Create or Delete a multicast source discovery protocol mesh-group using + [WORD] as the group name. + +.. index:: [no] ip msdp mesh-group WORD member A.B.C.D +.. clicmd:: [no] ip msdp mesh-group WORD member A.B.C.D + + Attach or Delete A.B.C.D to the MSDP mesh group WORD specified. + +.. index:: [no] ip msdp mesh-group WORD source A.B.C.D +.. clicmd:: [no] ip msdp mesh-group WORD source A.B.C.D + + For the address specified A.B.C.D use that as the source address for + mesh group packets being sent. .. index:: ip igmp generate-query-once [version (2-3)] .. clicmd:: ip igmp generate-query-once [version (2-3)] diff --git a/isisd/isis_adjacency.c b/isisd/isis_adjacency.c index 66de11e6f7..425627485a 100644 --- a/isisd/isis_adjacency.c +++ b/isisd/isis_adjacency.c @@ -171,14 +171,14 @@ void isis_delete_adj(void *arg) return; THREAD_TIMER_OFF(adj->t_expire); - if (adj->adj_state != ISIS_ADJ_DOWN) { + if (adj->adj_state != ISIS_ADJ_DOWN) adj->adj_state = ISIS_ADJ_DOWN; - hook_call(isis_adj_state_change_hook, adj); - } /* remove from SPF trees */ spftree_area_adj_del(adj->circuit->area, adj); + hook_call(isis_adj_state_change_hook, adj); + XFREE(MTYPE_ISIS_ADJACENCY_INFO, adj->area_addresses); XFREE(MTYPE_ISIS_ADJACENCY_INFO, adj->ipv4_addresses); XFREE(MTYPE_ISIS_ADJACENCY_INFO, adj->ipv6_addresses); diff --git a/isisd/isis_bfd.c b/isisd/isis_bfd.c index 9f8424fcd6..69c971ee2c 100644 --- a/isisd/isis_bfd.c +++ b/isisd/isis_bfd.c @@ -138,6 +138,8 @@ static void bfd_adj_event(struct isis_adjacency *adj, struct prefix *dst, return; } + adj->circuit->area->bfd_signalled_down = true; + isis_adj_state_change(&adj, ISIS_ADJ_DOWN, "bfd session went down"); } diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c index 03d7b3d07b..e0e82e4725 100644 --- a/isisd/isis_circuit.c +++ b/isisd/isis_circuit.c @@ -721,7 +721,7 @@ void isis_circuit_down(struct isis_circuit *circuit) #endif /* ifndef FABRICD */ /* log adjacency changes if configured to do so */ - if (circuit->area && circuit->area->log_adj_changes) { + if (circuit->area->log_adj_changes) { struct isis_adjacency *adj = NULL; if (circuit->circ_type == CIRCUIT_T_P2P) { adj = circuit->u.p2p.neighbor; diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c index 5cf652b291..63303e2308 100644 --- a/isisd/isis_lsp.c +++ b/isisd/isis_lsp.c @@ -1376,7 +1376,13 @@ static int lsp_refresh(struct thread *thread) if ((area->is_type & level) == 0) return ISIS_ERROR; - if (monotime_since(&area->last_lsp_refresh_event[level - 1], NULL) < 100000L) { + /* + * Throttle regeneration of LSPs (but not when BFD signalled a 'down' + * message) + */ + if (monotime_since(&area->last_lsp_refresh_event[level - 1], NULL) + < 100000L + && !(area->bfd_force_spf_refresh)) { sched_debug("ISIS (%s): Still unstable, postpone LSP L%d refresh", area->area_tag, level); _lsp_regenerate_schedule(area, level, 0, false, @@ -1429,7 +1435,12 @@ int _lsp_regenerate_schedule(struct isis_area *area, int level, "ISIS (%s): Checking whether L%d needs to be scheduled", area->area_tag, lvl); - if (area->lsp_regenerate_pending[lvl - 1]) { + if (area->lsp_regenerate_pending[lvl - 1] + && !(area->bfd_signalled_down)) { + /* + * Note: in case of a BFD 'down' message the refresh is + * scheduled once again just to be sure + */ struct timeval remain = thread_timer_remain( area->t_lsp_refresh[lvl - 1]); sched_debug( @@ -1457,7 +1468,8 @@ int _lsp_regenerate_schedule(struct isis_area *area, int level, (long long)now); THREAD_TIMER_OFF(area->t_lsp_refresh[lvl - 1]); diff = now - lsp->last_generated; - if (diff < area->lsp_gen_interval[lvl - 1]) { + if (diff < area->lsp_gen_interval[lvl - 1] + && !(area->bfd_signalled_down)) { timeout = 1000 * (area->lsp_gen_interval[lvl - 1] - diff); sched_debug( @@ -1465,17 +1477,21 @@ int _lsp_regenerate_schedule(struct isis_area *area, int level, area->area_tag, timeout); } else { /* - * lsps are not regenerated if lsp_regenerate function - * is called - * directly. However if the lsp_regenerate call is - * queued for - * later execution it works. + * Schedule LSP refresh ASAP */ - timeout = 100; - sched_debug( - "ISIS (%s): Last generation was more than lsp_gen_interval ago." - " Scheduling for execution in %ld ms.", - area->area_tag, timeout); + timeout = 0; + + if (area->bfd_signalled_down) { + sched_debug( + "ISIS (%s): Scheduling immediately due to BDF 'down' message.", + area->area_tag); + area->bfd_signalled_down = false; + area->bfd_force_spf_refresh = true; + } else { + sched_debug( + "ISIS (%s): Last generation was more than lsp_gen_interval ago. Scheduling for execution now.", + area->area_tag); + } } area->lsp_regenerate_pending[lvl - 1] = 1; diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c index dfcea9a921..fb1aad8c49 100644 --- a/isisd/isis_spf.c +++ b/isisd/isis_spf.c @@ -1288,9 +1288,20 @@ int _isis_spf_schedule(struct isis_area *area, int level, /* wait configured min_spf_interval before doing the SPF */ long timer; - if (diff >= area->min_spf_interval[level - 1]) { - /* Last run is more than min interval ago, schedule immediate run */ + if (diff >= area->min_spf_interval[level - 1] + || area->bfd_force_spf_refresh) { + /* + * Last run is more than min interval ago or BFD signalled a + * 'down' message, schedule immediate run + */ timer = 0; + + if (area->bfd_force_spf_refresh) { + zlog_debug( + "ISIS-Spf (%s) L%d SPF scheduled immediately due to BFD 'down' message", + area->area_tag, level); + area->bfd_force_spf_refresh = false; + } } else { timer = area->min_spf_interval[level - 1] - diff; } diff --git a/isisd/isisd.c b/isisd/isisd.c index 286542c8d3..53e48bd1cf 100644 --- a/isisd/isisd.c +++ b/isisd/isisd.c @@ -203,6 +203,9 @@ struct isis_area *isis_area_create(const char *area_tag) area->lsp_refresh_arg[1].area = area; area->lsp_refresh_arg[1].level = IS_LEVEL_2; + area->bfd_signalled_down = false; + area->bfd_force_spf_refresh = false; + QOBJ_REG(area, isis_area); diff --git a/isisd/isisd.h b/isisd/isisd.h index def2027aa3..57d9691cc7 100644 --- a/isisd/isisd.h +++ b/isisd/isisd.h @@ -127,6 +127,9 @@ struct isis_area { */ int lsp_regenerate_pending[ISIS_LEVELS]; + bool bfd_signalled_down; + bool bfd_force_spf_refresh; + struct fabricd *fabricd; /* diff --git a/lib/nexthop.c b/lib/nexthop.c index 0d239e091b..3496081d47 100644 --- a/lib/nexthop.c +++ b/lib/nexthop.c @@ -187,35 +187,41 @@ int nexthop_cmp(const struct nexthop *next1, const struct nexthop *next2) return ret; } -int nexthop_same_firsthop(struct nexthop *next1, struct nexthop *next2) +bool nexthop_same_firsthop(const struct nexthop *next1, + const struct nexthop *next2) { + /* Map the TYPE_IPx types to TYPE_IPx_IFINDEX */ int type1 = NEXTHOP_FIRSTHOPTYPE(next1->type); int type2 = NEXTHOP_FIRSTHOPTYPE(next2->type); if (type1 != type2) - return 0; + return false; + + if (next1->vrf_id != next2->vrf_id) + return false; + switch (type1) { case NEXTHOP_TYPE_IPV4_IFINDEX: if (!IPV4_ADDR_SAME(&next1->gate.ipv4, &next2->gate.ipv4)) - return 0; + return false; if (next1->ifindex != next2->ifindex) - return 0; + return false; break; case NEXTHOP_TYPE_IFINDEX: if (next1->ifindex != next2->ifindex) - return 0; + return false; break; case NEXTHOP_TYPE_IPV6_IFINDEX: if (!IPV6_ADDR_SAME(&next1->gate.ipv6, &next2->gate.ipv6)) - return 0; + return false; if (next1->ifindex != next2->ifindex) - return 0; + return false; break; default: /* do nothing */ break; } - return 1; + return true; } /* diff --git a/lib/nexthop.h b/lib/nexthop.h index 9b71262589..eda88efc08 100644 --- a/lib/nexthop.h +++ b/lib/nexthop.h @@ -208,7 +208,8 @@ extern int nexthop_g_addr_cmp(enum nexthop_types_t type, extern const char *nexthop_type_to_str(enum nexthop_types_t nh_type); extern bool nexthop_labels_match(const struct nexthop *nh1, const struct nexthop *nh2); -extern int nexthop_same_firsthop(struct nexthop *next1, struct nexthop *next2); +extern bool nexthop_same_firsthop(const struct nexthop *next1, + const struct nexthop *next2); extern const char *nexthop2str(const struct nexthop *nexthop, char *str, int size); diff --git a/lib/routemap.c b/lib/routemap.c index 3b45133450..7749ea4cc7 100644 --- a/lib/routemap.c +++ b/lib/routemap.c @@ -47,18 +47,12 @@ DEFINE_QOBJ_TYPE(route_map) #define IPv4_PREFIX_LIST "ip address prefix-list" #define IPv6_PREFIX_LIST "ipv6 address prefix-list" -#define IPv4_MATCH_RULE "ip " -#define IPv6_MATCH_RULE "ipv6 " #define IS_RULE_IPv4_PREFIX_LIST(S) \ (strncmp(S, IPv4_PREFIX_LIST, strlen(IPv4_PREFIX_LIST)) == 0) #define IS_RULE_IPv6_PREFIX_LIST(S) \ (strncmp(S, IPv6_PREFIX_LIST, strlen(IPv6_PREFIX_LIST)) == 0) -#define IS_IPv4_RULE(S) \ - (strncmp(S, IPv4_MATCH_RULE, strlen(IPv4_MATCH_RULE)) == 0) -#define IS_IPv6_RULE(S) \ - (strncmp(S, IPv6_MATCH_RULE, strlen(IPv6_MATCH_RULE)) == 0) struct route_map_pentry_dep { struct prefix_list_entry *pentry; const char *plist_name; @@ -86,8 +80,6 @@ static void route_map_del_plist_entries(afi_t afi, struct route_map_index *index, const char *plist_name, struct prefix_list_entry *entry); -static bool route_map_is_ip_rule_present(struct route_map_index *index); -static bool route_map_is_ipv6_rule_present(struct route_map_index *index); static struct hash *route_map_get_dep_hash(route_map_event_t event); @@ -1370,26 +1362,6 @@ enum rmap_compile_rets route_map_add_match(struct route_map_index *index, } else if (IS_RULE_IPv6_PREFIX_LIST(match_name)) { route_map_pfx_tbl_update(RMAP_EVENT_PLIST_ADDED, index, AFI_IP6, match_arg); - } else { - /* If IPv4 match criteria has been added to the route-map - * index, check for IPv6 prefix-list match rule presence and - * remove this index from the trie node created for each of the - * prefix-entry within the prefix-list. If no IPv6 prefix-list - * match rule is present, remove this index from the IPv6 - * default route's trie node. - */ - if (IS_IPv4_RULE(match_name)) - route_map_del_plist_entries(AFI_IP6, index, NULL, NULL); - - /* If IPv6 match criteria has been added to the route-map - * index, check for IPv4 prefix-list match rule presence and - * remove this index from the trie node created for each of the - * prefix-entry within the prefix-list. If no IPv4 prefix-list - * match rule is present, remove this index from the IPv4 - * default route's trie node. - */ - else if (IS_IPv6_RULE(match_name)) - route_map_del_plist_entries(AFI_IP, index, NULL, NULL); } /* Execute event hook. */ @@ -1441,7 +1413,7 @@ enum rmap_compile_rets route_map_delete_match(struct route_map_index *index, route_map_rule_delete(&index->match_list, rule); /* If IPv4 or IPv6 prefix-list match criteria - * has been delete to the route-map index, update + * has been delete from the route-map index, update * the route-map's prefix table. */ if (IS_RULE_IPv4_PREFIX_LIST(match_name)) { @@ -1452,30 +1424,6 @@ enum rmap_compile_rets route_map_delete_match(struct route_map_index *index, route_map_pfx_tbl_update( RMAP_EVENT_PLIST_DELETED, index, AFI_IP6, match_arg); - } else { - /* If no more IPv4 match rules are present in - * this index, check for IPv6 prefix-list match - * rule presence and add this index to trie node - * created for each of the prefix-entry within - * the prefix-list. If no IPv6 prefix-list match - * rule is present, add this index to the IPv6 - * default route's trie node. - */ - if (!route_map_is_ip_rule_present(index)) - route_map_add_plist_entries( - AFI_IP6, index, NULL, NULL); - - /* If no more IPv6 match rules are present in - * this index, check for IPv4 prefix-list match - * rule presence and add this index to trie node - * created for each of the prefix-entry within - * the prefix-list. If no IPv6 prefix-list match - * rule is present, add this index to the IPv4 - * default route's trie node. - */ - if (!route_map_is_ipv6_rule_present(index)) - route_map_add_plist_entries( - AFI_IP, index, NULL, NULL); } return RMAP_COMPILE_SUCCESS; @@ -1921,33 +1869,34 @@ static void route_map_pfx_table_del(struct route_table *table, route_unlock_node(rn); } -/* This function checks for the presence of an IPv4 match rule - * in the given route-map index. +/* This function checks for the presence of an IPv4 prefix-list + * match rule in the given route-map index. */ -static bool route_map_is_ip_rule_present(struct route_map_index *index) +static bool route_map_is_ip_pfx_list_rule_present(struct route_map_index *index) { struct route_map_rule_list *match_list = NULL; struct route_map_rule *rule = NULL; match_list = &index->match_list; for (rule = match_list->head; rule; rule = rule->next) - if (IS_IPv4_RULE(rule->cmd->str)) + if (IS_RULE_IPv4_PREFIX_LIST(rule->cmd->str)) return true; return false; } -/* This function checks for the presence of an IPv6 match rule - * in the given route-map index. +/* This function checks for the presence of an IPv6 prefix-list + * match rule in the given route-map index. */ -static bool route_map_is_ipv6_rule_present(struct route_map_index *index) +static bool +route_map_is_ipv6_pfx_list_rule_present(struct route_map_index *index) { struct route_map_rule_list *match_list = NULL; struct route_map_rule *rule = NULL; match_list = &index->match_list; for (rule = match_list->head; rule; rule = rule->next) - if (IS_IPv6_RULE(rule->cmd->str)) + if (IS_RULE_IPv6_PREFIX_LIST(rule->cmd->str)) return true; return false; @@ -2118,7 +2067,7 @@ static void route_map_trie_update(afi_t afi, route_map_event_t event, { if (event == RMAP_EVENT_PLIST_ADDED) { if (afi == AFI_IP) { - if (!route_map_is_ipv6_rule_present(index)) { + if (!route_map_is_ipv6_pfx_list_rule_present(index)) { route_map_pfx_table_del_default(AFI_IP6, index); route_map_add_plist_entries(afi, index, plist_name, NULL); @@ -2127,7 +2076,7 @@ static void route_map_trie_update(afi_t afi, route_map_event_t event, NULL, NULL); } } else { - if (!route_map_is_ip_rule_present(index)) { + if (!route_map_is_ip_pfx_list_rule_present(index)) { route_map_pfx_table_del_default(AFI_IP, index); route_map_add_plist_entries(afi, index, plist_name, NULL); @@ -2141,22 +2090,36 @@ static void route_map_trie_update(afi_t afi, route_map_event_t event, route_map_del_plist_entries(afi, index, plist_name, NULL); - if (!route_map_is_ipv6_rule_present(index)) + /* If IPv6 prefix-list match rule is not present, + * add this index to the IPv4 default route's trie + * node. + * Also, add this index to the trie nodes created + * for each of the prefix-entries within the IPv6 + * prefix-list, if the IPv6 prefix-list match rule + * is present. Else, add this index to the IPv6 + * default route's trie node. + */ + if (!route_map_is_ipv6_pfx_list_rule_present(index)) route_map_pfx_table_add_default(afi, index); - if (!route_map_is_ip_rule_present(index)) - route_map_add_plist_entries(AFI_IP6, index, - NULL, NULL); + route_map_add_plist_entries(AFI_IP6, index, NULL, NULL); } else { route_map_del_plist_entries(afi, index, plist_name, NULL); - if (!route_map_is_ip_rule_present(index)) + /* If IPv4 prefix-list match rule is not present, + * add this index to the IPv6 default route's trie + * node. + * Also, add this index to the trie nodes created + * for each of the prefix-entries within the IPv4 + * prefix-list, if the IPv4 prefix-list match rule + * is present. Else, add this index to the IPv4 + * default route's trie node. + */ + if (!route_map_is_ip_pfx_list_rule_present(index)) route_map_pfx_table_add_default(afi, index); - if (!route_map_is_ipv6_rule_present(index)) - route_map_add_plist_entries(AFI_IP, index, NULL, - NULL); + route_map_add_plist_entries(AFI_IP, index, NULL, NULL); } } } @@ -2229,30 +2192,27 @@ static void route_map_pentry_update(route_map_event_t event, } if (event == RMAP_EVENT_PLIST_ADDED) { - if (plist->count == 1) { - if (afi == AFI_IP) { - if (!route_map_is_ipv6_rule_present(index)) - route_map_add_plist_entries( - afi, index, plist_name, pentry); - } else { - if (!route_map_is_ip_rule_present(index)) - route_map_add_plist_entries( - afi, index, plist_name, pentry); - } + if (afi == AFI_IP) { + if (!route_map_is_ipv6_pfx_list_rule_present(index)) + route_map_add_plist_entries(afi, index, + plist_name, pentry); } else { - route_map_add_plist_entries(afi, index, plist_name, - pentry); + if (!route_map_is_ip_pfx_list_rule_present(index)) + route_map_add_plist_entries(afi, index, + plist_name, pentry); } } else if (event == RMAP_EVENT_PLIST_DELETED) { route_map_del_plist_entries(afi, index, plist_name, pentry); if (plist->count == 1) { if (afi == AFI_IP) { - if (!route_map_is_ipv6_rule_present(index)) + if (!route_map_is_ipv6_pfx_list_rule_present( + index)) route_map_pfx_table_add_default(afi, index); } else { - if (!route_map_is_ip_rule_present(index)) + if (!route_map_is_ip_pfx_list_rule_present( + index)) route_map_pfx_table_add_default(afi, index); } diff --git a/sharpd/sharp_zebra.c b/sharpd/sharp_zebra.c index c47f2105cb..7ab2d6ec22 100644 --- a/sharpd/sharp_zebra.c +++ b/sharpd/sharp_zebra.c @@ -435,6 +435,12 @@ static int sharp_debug_nexthops(struct zapi_route *api) int i; char buf[PREFIX_STRLEN]; + if (api->nexthop_num == 0) { + zlog_debug( + " Not installed"); + return 0; + } + for (i = 0; i < api->nexthop_num; i++) { struct zapi_nexthop *znh = &api->nexthops[i]; diff --git a/staticd/static_zebra.c b/staticd/static_zebra.c index 5cadf34365..c42f632ffb 100644 --- a/staticd/static_zebra.c +++ b/staticd/static_zebra.c @@ -363,6 +363,7 @@ extern void static_zebra_route_add(struct route_node *rn, memcpy(&api.src_prefix, src_pp, sizeof(api.src_prefix)); } SET_FLAG(api.flags, ZEBRA_FLAG_RR_USE_DISTANCE); + SET_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION); SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP); if (si_changed->distance) { SET_FLAG(api.message, ZAPI_MESSAGE_DISTANCE); diff --git a/tests/topotests/bfd-isis-topo1/__init__.py b/tests/topotests/bfd-isis-topo1/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/__init__.py diff --git a/tests/topotests/bfd-isis-topo1/rt1/bfdd.conf b/tests/topotests/bfd-isis-topo1/rt1/bfdd.conf new file mode 100644 index 0000000000..4793155939 --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt1/bfdd.conf @@ -0,0 +1,17 @@ +! +debug bfd network +debug bfd peer +debug bfd zebra +! +bfd + peer 10.0.1.2 interface eth-rt2 + detect-multiplier 3 + receive-interval 300 + transmit-interval 300 + ! + peer 10.0.2.2 interface eth-rt3 + detect-multiplier 3 + receive-interval 300 + transmit-interval 300 + ! +! diff --git a/tests/topotests/bfd-isis-topo1/rt1/isisd.conf b/tests/topotests/bfd-isis-topo1/rt1/isisd.conf new file mode 100644 index 0000000000..3219371d2e --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt1/isisd.conf @@ -0,0 +1,35 @@ +log file isisd.log +log timestamp precision 3 +! +hostname rt1 +! +password 1 +! +debug isis events +debug isis route-events +debug isis spf-events +debug isis adj-packets +debug isis lsp-sched +! +interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive +! +interface eth-rt2 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 + isis bfd +! +interface eth-rt3 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis bfd +! +router isis 1 + net 49.0000.0000.0000.0001.00 + is-type level-1 +! diff --git a/tests/topotests/bfd-isis-topo1/rt1/step1/show_ip_route.ref b/tests/topotests/bfd-isis-topo1/rt1/step1/show_ip_route.ref new file mode 100644 index 0000000000..af6e45cf33 --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt1/step1/show_ip_route.ref @@ -0,0 +1,74 @@ +{ + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bfd-isis-topo1/rt1/step1/show_ipv6_route.ref b/tests/topotests/bfd-isis-topo1/rt1/step1/show_ipv6_route.ref new file mode 100644 index 0000000000..68d3fe2c44 --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt1/step1/show_ipv6_route.ref @@ -0,0 +1,70 @@ +{ + "::ffff:202:202\/128":[ + { + "prefix":"::ffff:202:202\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "::ffff:303:303\/128":[ + { + "prefix":"::ffff:303:303\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "::ffff:404:404\/128":[ + { + "prefix":"::ffff:404:404\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "::ffff:505:505\/128":[ + { + "prefix":"::ffff:505:505\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bfd-isis-topo1/rt1/step2/show_bfd_peers.ref b/tests/topotests/bfd-isis-topo1/rt1/step2/show_bfd_peers.ref new file mode 100644 index 0000000000..cb4083d2fd --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt1/step2/show_bfd_peers.ref @@ -0,0 +1,16 @@ +[ + { + "peer": "10.0.2.2", + "interface": "eth-rt3", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + }, + { + "peer": "10.0.1.2", + "interface": "eth-rt2", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + } +] diff --git a/tests/topotests/bfd-isis-topo1/rt1/step3/show_bfd_peers_healthy.ref b/tests/topotests/bfd-isis-topo1/rt1/step3/show_bfd_peers_healthy.ref new file mode 100644 index 0000000000..cb4083d2fd --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt1/step3/show_bfd_peers_healthy.ref @@ -0,0 +1,16 @@ +[ + { + "peer": "10.0.2.2", + "interface": "eth-rt3", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + }, + { + "peer": "10.0.1.2", + "interface": "eth-rt2", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + } +] diff --git a/tests/topotests/bfd-isis-topo1/rt1/step3/show_bfd_peers_rt2_down.ref b/tests/topotests/bfd-isis-topo1/rt1/step3/show_bfd_peers_rt2_down.ref new file mode 100644 index 0000000000..f00b9f3d32 --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt1/step3/show_bfd_peers_rt2_down.ref @@ -0,0 +1,9 @@ +[ + { + "peer": "10.0.2.2", + "interface": "eth-rt3", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + } +] diff --git a/tests/topotests/bfd-isis-topo1/rt1/step3/show_bfd_peers_rt3_down.ref b/tests/topotests/bfd-isis-topo1/rt1/step3/show_bfd_peers_rt3_down.ref new file mode 100644 index 0000000000..f5bd276a4a --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt1/step3/show_bfd_peers_rt3_down.ref @@ -0,0 +1,9 @@ +[ + { + "peer": "10.0.1.2", + "interface": "eth-rt2", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + } +] diff --git a/tests/topotests/bfd-isis-topo1/rt1/step3/show_ip_route_healthy.ref b/tests/topotests/bfd-isis-topo1/rt1/step3/show_ip_route_healthy.ref new file mode 100644 index 0000000000..af6e45cf33 --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt1/step3/show_ip_route_healthy.ref @@ -0,0 +1,74 @@ +{ + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bfd-isis-topo1/rt1/step3/show_ip_route_rt2_down.ref b/tests/topotests/bfd-isis-topo1/rt1/step3/show_ip_route_rt2_down.ref new file mode 100644 index 0000000000..b8366bc0b7 --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt1/step3/show_ip_route_rt2_down.ref @@ -0,0 +1,74 @@ +{ + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bfd-isis-topo1/rt1/step3/show_ip_route_rt3_down.ref b/tests/topotests/bfd-isis-topo1/rt1/step3/show_ip_route_rt3_down.ref new file mode 100644 index 0000000000..42bd6abf82 --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt1/step3/show_ip_route_rt3_down.ref @@ -0,0 +1,74 @@ +{ + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bfd-isis-topo1/rt1/step3/show_ipv6_route_healthy.ref b/tests/topotests/bfd-isis-topo1/rt1/step3/show_ipv6_route_healthy.ref new file mode 100644 index 0000000000..68d3fe2c44 --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt1/step3/show_ipv6_route_healthy.ref @@ -0,0 +1,70 @@ +{ + "::ffff:202:202\/128":[ + { + "prefix":"::ffff:202:202\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "::ffff:303:303\/128":[ + { + "prefix":"::ffff:303:303\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "::ffff:404:404\/128":[ + { + "prefix":"::ffff:404:404\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "::ffff:505:505\/128":[ + { + "prefix":"::ffff:505:505\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bfd-isis-topo1/rt1/step3/show_ipv6_route_rt2_down.ref b/tests/topotests/bfd-isis-topo1/rt1/step3/show_ipv6_route_rt2_down.ref new file mode 100644 index 0000000000..200053c3e8 --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt1/step3/show_ipv6_route_rt2_down.ref @@ -0,0 +1,70 @@ +{ + "::ffff:202:202\/128":[ + { + "prefix":"::ffff:202:202\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "::ffff:303:303\/128":[ + { + "prefix":"::ffff:303:303\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "::ffff:404:404\/128":[ + { + "prefix":"::ffff:404:404\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "::ffff:505:505\/128":[ + { + "prefix":"::ffff:505:505\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bfd-isis-topo1/rt1/step3/show_ipv6_route_rt3_down.ref b/tests/topotests/bfd-isis-topo1/rt1/step3/show_ipv6_route_rt3_down.ref new file mode 100644 index 0000000000..4297f163b5 --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt1/step3/show_ipv6_route_rt3_down.ref @@ -0,0 +1,70 @@ +{ + "::ffff:202:202\/128":[ + { + "prefix":"::ffff:202:202\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "::ffff:303:303\/128":[ + { + "prefix":"::ffff:303:303\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "::ffff:404:404\/128":[ + { + "prefix":"::ffff:404:404\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "::ffff:505:505\/128":[ + { + "prefix":"::ffff:505:505\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bfd-isis-topo1/rt1/zebra.conf b/tests/topotests/bfd-isis-topo1/rt1/zebra.conf new file mode 100644 index 0000000000..6003125b6b --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt1/zebra.conf @@ -0,0 +1,25 @@ +log file zebra.log +log timestamp precision 3 +! +hostname rt1 +! +debug zebra kernel +debug zebra packet +debug zebra events +debug zebra rib +! +interface lo + ip address 1.1.1.1/32 + ipv6 address ::ffff:0101:0101/128 +! +interface eth-rt2 + ip address 10.0.1.1/24 +! +interface eth-rt3 + ip address 10.0.2.1/24 +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/bfd-isis-topo1/rt2/bfdd.conf b/tests/topotests/bfd-isis-topo1/rt2/bfdd.conf new file mode 100644 index 0000000000..a49cd4fa6b --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt2/bfdd.conf @@ -0,0 +1,12 @@ +! +debug bfd network +debug bfd peer +debug bfd zebra +! +bfd + peer 10.0.1.1 interface eth-rt1 + detect-multiplier 3 + receive-interval 300 + transmit-interval 300 + ! +! diff --git a/tests/topotests/bfd-isis-topo1/rt2/isisd.conf b/tests/topotests/bfd-isis-topo1/rt2/isisd.conf new file mode 100644 index 0000000000..63ccb640a4 --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt2/isisd.conf @@ -0,0 +1,30 @@ +log file isisd.log +! +hostname rt2 +! +password 1 +! +debug isis events +debug isis route-events +debug isis spf-events +! +interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive +! +interface eth-rt1 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 + isis bfd +! +interface eth-rt5 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 +! +router isis 1 + net 49.0000.0000.0000.0002.00 + is-type level-1 +! diff --git a/tests/topotests/bfd-isis-topo1/rt2/step2/show_bfd_peers.ref b/tests/topotests/bfd-isis-topo1/rt2/step2/show_bfd_peers.ref new file mode 100644 index 0000000000..8a90649efa --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt2/step2/show_bfd_peers.ref @@ -0,0 +1,9 @@ +[ + { + "peer": "10.0.1.1", + "interface": "eth-rt1", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + } +] diff --git a/tests/topotests/bfd-isis-topo1/rt2/zebra.conf b/tests/topotests/bfd-isis-topo1/rt2/zebra.conf new file mode 100644 index 0000000000..5fc7fc5b28 --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt2/zebra.conf @@ -0,0 +1,22 @@ +log file zebra.log +! +hostname rt2 +! +debug zebra kernel +debug zebra packet +! +interface lo + ip address 2.2.2.2/32 + ipv6 address ::ffff:0202:0202/128 +! +interface eth-rt1 + ip address 10.0.1.2/24 +! +interface eth-rt5 + ip address 10.0.3.1/24 +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/bfd-isis-topo1/rt3/bfdd.conf b/tests/topotests/bfd-isis-topo1/rt3/bfdd.conf new file mode 100644 index 0000000000..600054a0bc --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt3/bfdd.conf @@ -0,0 +1,12 @@ +! +debug bfd network +debug bfd peer +debug bfd zebra +! +bfd + peer 10.0.2.1 interface eth-rt1 + detect-multiplier 3 + receive-interval 300 + transmit-interval 300 + ! +! diff --git a/tests/topotests/bfd-isis-topo1/rt3/isisd.conf b/tests/topotests/bfd-isis-topo1/rt3/isisd.conf new file mode 100644 index 0000000000..928f1e1a2b --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt3/isisd.conf @@ -0,0 +1,32 @@ +log file isisd.log +! +hostname rt3 +! +password 1 +! +debug isis events +debug isis route-events +debug isis spf-events +! +interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive +! +interface eth-rt1 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis bfd +! +interface eth-rt4 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 +! +router isis 1 + net 49.0000.0000.0000.0003.00 + is-type level-1 +! + diff --git a/tests/topotests/bfd-isis-topo1/rt3/step2/show_bfd_peers.ref b/tests/topotests/bfd-isis-topo1/rt3/step2/show_bfd_peers.ref new file mode 100644 index 0000000000..13eb2a2c69 --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt3/step2/show_bfd_peers.ref @@ -0,0 +1,9 @@ +[ + { + "peer": "10.0.2.1", + "interface": "eth-rt1", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + } +] diff --git a/tests/topotests/bfd-isis-topo1/rt3/zebra.conf b/tests/topotests/bfd-isis-topo1/rt3/zebra.conf new file mode 100644 index 0000000000..d368de9bbe --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt3/zebra.conf @@ -0,0 +1,22 @@ +log file zebra.log +! +hostname rt3 +! +debug zebra kernel +debug zebra packet +! +interface lo + ip address 3.3.3.3/32 + ipv6 address ::ffff:0303:0303/128 +! +interface eth-rt1 + ip address 10.0.2.2/24 +! +interface eth-rt4 + ip address 10.0.4.1/24 +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/bfd-isis-topo1/rt4/bfdd.conf b/tests/topotests/bfd-isis-topo1/rt4/bfdd.conf new file mode 100644 index 0000000000..f35e772790 --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt4/bfdd.conf @@ -0,0 +1,5 @@ +! +debug bfd network +debug bfd peer +debug bfd zebra +! diff --git a/tests/topotests/bfd-isis-topo1/rt4/isisd.conf b/tests/topotests/bfd-isis-topo1/rt4/isisd.conf new file mode 100644 index 0000000000..fde97478a9 --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt4/isisd.conf @@ -0,0 +1,29 @@ +log file isisd.log +! +hostname rt4 +! +password 1 +! +debug isis events +debug isis route-events +debug isis spf-events +! +interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive +! +interface eth-rt3 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 +! +interface eth-rt5 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 +! +router isis 1 + net 49.0000.0000.0000.0004.00 + is-type level-1 +! diff --git a/tests/topotests/bfd-isis-topo1/rt4/zebra.conf b/tests/topotests/bfd-isis-topo1/rt4/zebra.conf new file mode 100644 index 0000000000..7b053bac35 --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt4/zebra.conf @@ -0,0 +1,22 @@ +log file zebra.log +! +hostname rt4 +! +debug zebra kernel +debug zebra packet +! +interface lo + ip address 4.4.4.4/32 + ipv6 address ::ffff:0404:0404/128 +! +interface eth-rt3 + ip address 10.0.4.2/24 +! +interface eth-rt5 + ip address 10.0.5.1/24 +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/bfd-isis-topo1/rt5/bfdd.conf b/tests/topotests/bfd-isis-topo1/rt5/bfdd.conf new file mode 100644 index 0000000000..f35e772790 --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt5/bfdd.conf @@ -0,0 +1,5 @@ +! +debug bfd network +debug bfd peer +debug bfd zebra +! diff --git a/tests/topotests/bfd-isis-topo1/rt5/isisd.conf b/tests/topotests/bfd-isis-topo1/rt5/isisd.conf new file mode 100644 index 0000000000..fd00cb1ddb --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt5/isisd.conf @@ -0,0 +1,29 @@ +log file isisd.log +! +hostname rt5 +! +password 1 +! +debug isis events +debug isis route-events +debug isis spf-events +! +interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive +! +interface eth-rt2 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 +! +interface eth-rt4 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 +! +router isis 1 + net 49.0000.0000.0000.0005.00 + is-type level-1 +! diff --git a/tests/topotests/bfd-isis-topo1/rt5/zebra.conf b/tests/topotests/bfd-isis-topo1/rt5/zebra.conf new file mode 100644 index 0000000000..0b7c9e02f3 --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt5/zebra.conf @@ -0,0 +1,22 @@ +log file zebra.log +! +hostname rt5 +! +debug zebra kernel +debug zebra packet +! +interface lo + ip address 5.5.5.5/32 + ipv6 address ::ffff:0505:0505/128 +! +interface eth-rt2 + ip address 10.0.3.2/24 +! +interface eth-rt4 + ip address 10.0.5.2/24 +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/bfd-isis-topo1/test_bfd_isis_topo1.py b/tests/topotests/bfd-isis-topo1/test_bfd_isis_topo1.py new file mode 100755 index 0000000000..a1ed0cc2af --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/test_bfd_isis_topo1.py @@ -0,0 +1,304 @@ +#!/usr/bin/env python + +# +# test_bfd_isis_topo1.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 by +# Network Device Education Foundation, Inc. ("NetDEF") +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_bfd_isis_topo1.py: + + +---------+ + | | + eth-rt2 (.1) | RT1 | eth-rt3 (.1) + +----------+ 1.1.1.1 +----------+ + | | | | + | +---------+ | + | | + | 10.0.2.0/24 | + | | + | eth-rt1 | (.2) + | 10.0.1.0/24 +----+----+ + | | | + | | RT3 | + | | 3.3.3.3 | + | | | + (.2) | eth-rt1 +----+----+ + +----+----+ eth-rt4 | (.1) + | | | + | RT2 | | + | 2.2.2.2 | 10.0.4.0/24 | + | | | + +----+----+ | + (.1) | eth-rt5 eth-rt3 | (.2) + | +----+----+ + | | | + | | RT4 | + | | 4.4.4.4 | + | | | + | +----+----+ + | 10.0.3.0/24 eth-rt5 | (.1) + | | + | | + | 10.0.5.0/24 | + | | + | +---------+ | + | | | | + +----------+ RT5 +----------+ + eth-rt2 (.2) | 5.5.5.5 | eth-rt4 (.2) + | | + +---------+ + +""" + +import os +import sys +import pytest +import json +import re +from time import sleep +from time import time +from functools import partial + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + + +class TemplateTopo(Topo): + "Test topology builder" + + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # + # Define FRR Routers + # + for router in ["rt1", "rt2", "rt3", "rt4", "rt5"]: + tgen.add_router(router) + + # + # Define connections + # + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["rt1"], nodeif="eth-rt2") + switch.add_link(tgen.gears["rt2"], nodeif="eth-rt1") + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["rt1"], nodeif="eth-rt3") + switch.add_link(tgen.gears["rt3"], nodeif="eth-rt1") + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["rt2"], nodeif="eth-rt5") + switch.add_link(tgen.gears["rt5"], nodeif="eth-rt2") + + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["rt3"], nodeif="eth-rt4") + switch.add_link(tgen.gears["rt4"], nodeif="eth-rt3") + + switch = tgen.add_switch("s5") + switch.add_link(tgen.gears["rt4"], nodeif="eth-rt5") + switch.add_link(tgen.gears["rt5"], nodeif="eth-rt4") + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(TemplateTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + # For all registered routers, load the zebra configuration file + for rname, router in router_list.iteritems(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BFD, os.path.join(CWD, "{}/bfdd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_ISIS, os.path.join(CWD, "{}/isisd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +def print_cmd_result(rname, command): + print(get_topogen().gears[rname].vtysh_cmd(command, isjson=False)) + + +def router_compare_json_output(rname, command, reference, count=120, wait=0.5): + "Compare router JSON output" + + logger.info('Comparing router "%s" "%s" output', rname, command) + + tgen = get_topogen() + filename = "{}/{}/{}".format(CWD, rname, reference) + expected = json.loads(open(filename).read()) + + # Run test function until we get an result. Wait at most 60 seconds. + test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected) + _, diff = topotest.run_and_expect(test_func, None, count=count, wait=wait) + assertmsg = '"{}" JSON output mismatches the expected result'.format(rname) + assert diff is None, assertmsg + + +## TEST STEPS + + +def test_rib_isis_step1(): + logger.info("Test (step 1): verify RIB (IPv4 and IPv6) for IS-IS") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router_compare_json_output( + "rt1", "show ip route isis json", "step1/show_ip_route.ref" + ) + router_compare_json_output( + "rt1", "show ipv6 route isis json", "step1/show_ipv6_route.ref" + ) + + +def test_bfd_isis_sessions_step2(): + logger.info("Test (step 2): verify BFD peers for IS-IS") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # BFD is just used on three routers + for rt in ["rt1", "rt2", "rt3"]: + router_compare_json_output( + rt, "show bfd peers json", "step2/show_bfd_peers.ref" + ) + + +def test_bfd_isis_interface_failure_rt2_step3(): + logger.info("Test (step 2): check failover handling when RT2 goes down") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Let's kill the interface on rt2 and see what happens with the RIB and BFD on rt1 + tgen.gears["rt2"].link_enable("eth-rt1", enabled=False) + + # By default BFD provides a recovery time of 900ms plus jitter, so let's wait + # initial 2 seconds to let the CI not suffer. + # TODO: add check for array size + sleep(2) + router_compare_json_output( + "rt1", "show ip route isis json", "step3/show_ip_route_rt2_down.ref", 1, 0 + ) + router_compare_json_output( + "rt1", "show ipv6 route isis json", "step3/show_ipv6_route_rt2_down.ref", 1, 0 + ) + router_compare_json_output( + "rt1", "show bfd peers json", "step3/show_bfd_peers_rt2_down.ref", 1, 0 + ) + + # Check recovery, this can take some time + tgen.gears["rt2"].link_enable("eth-rt1", enabled=True) + + router_compare_json_output( + "rt1", "show ip route isis json", "step3/show_ip_route_healthy.ref" + ) + router_compare_json_output( + "rt1", "show ipv6 route isis json", "step3/show_ipv6_route_healthy.ref" + ) + router_compare_json_output( + "rt1", "show bfd peers json", "step3/show_bfd_peers_healthy.ref" + ) + + +def test_bfd_isis_interface_failure_rt3_step3(): + logger.info("Test (step 2): check failover handling when RT2 goes down") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Let's kill the interface on rt3 and see what happens with the RIB and BFD on rt1 + tgen.gears["rt3"].link_enable("eth-rt1", enabled=False) + + # By default BFD provides a recovery time of 900ms plus jitter, so let's wait + # initial 2 seconds to let the CI not suffer. + # TODO: add check for array size + sleep(2) + router_compare_json_output( + "rt1", "show ip route isis json", "step3/show_ip_route_rt3_down.ref", 1, 0 + ) + router_compare_json_output( + "rt1", "show ipv6 route isis json", "step3/show_ipv6_route_rt3_down.ref", 1, 0 + ) + router_compare_json_output( + "rt1", "show bfd peers json", "step3/show_bfd_peers_rt3_down.ref", 1, 0 + ) + + # Check recovery, this can take some time + tgen.gears["rt3"].link_enable("eth-rt1", enabled=True) + + router_compare_json_output( + "rt1", "show ip route isis json", "step3/show_ip_route_healthy.ref" + ) + router_compare_json_output( + "rt1", "show ipv6 route isis json", "step3/show_ipv6_route_healthy.ref" + ) + router_compare_json_output( + "rt1", "show bfd peers json", "step3/show_bfd_peers_healthy.ref" + ) + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp-path-attributes-topo1/test_bgp_path_attributes.py b/tests/topotests/bgp-path-attributes-topo1/test_bgp_path_attributes.py index b0ff3ac437..607b036c6a 100755 --- a/tests/topotests/bgp-path-attributes-topo1/test_bgp_path_attributes.py +++ b/tests/topotests/bgp-path-attributes-topo1/test_bgp_path_attributes.py @@ -56,7 +56,6 @@ import pdb import json import time import inspect -import ipaddress from time import sleep import pytest diff --git a/tests/topotests/lib/bgp.py b/tests/topotests/lib/bgp.py index 38e4e1fce5..44b7335058 100644 --- a/tests/topotests/lib/bgp.py +++ b/tests/topotests/lib/bgp.py @@ -21,7 +21,7 @@ from copy import deepcopy from time import sleep import traceback -import ipaddr +import ipaddress import os import sys from lib import topotest @@ -381,10 +381,10 @@ def __create_bgp_unicast_neighbor( del_action = advertise_network_dict.setdefault("delete", False) # Generating IPs for verification - prefix = str(ipaddr.IPNetwork(unicode(network[0])).prefixlen) + prefix = str(ipaddress.ip_network(unicode(network[0])).prefixlen) network_list = generate_ips(network, no_of_network) for ip in network_list: - ip = str(ipaddr.IPNetwork(unicode(ip)).network) + ip = str(ipaddress.ip_network(unicode(ip)).network_address) cmd = "network {}/{}".format(ip, prefix) if del_action: @@ -859,7 +859,7 @@ def verify_router_id(tgen, topo, input_dict): logger.info("Checking router %s router-id", router) show_bgp_json = run_frr_cmd(rnode, "show bgp summary json", isjson=True) router_id_out = show_bgp_json["ipv4Unicast"]["routerId"] - router_id_out = ipaddr.IPv4Address(unicode(router_id_out)) + router_id_out = ipaddress.IPv4Address(unicode(router_id_out)) # Once router-id is deleted, highest interface ip should become # router-id @@ -867,7 +867,7 @@ def verify_router_id(tgen, topo, input_dict): router_id = find_interface_with_greater_ip(topo, router) else: router_id = input_dict[router]["bgp"]["router_id"] - router_id = ipaddr.IPv4Address(unicode(router_id)) + router_id = ipaddress.IPv4Address(unicode(router_id)) if router_id == router_id_out: logger.info("Found expected router-id %s for router %s", router_id, router) @@ -1467,9 +1467,10 @@ def clear_bgp_and_verify(tgen, topo, router): rnode = tgen.routers()[router] peer_uptime_before_clear_bgp = {} + sleeptime = 3 + # Verifying BGP convergence before bgp clear command for retry in range(44): - sleeptime = 3 # Waiting for BGP to converge logger.info( "Waiting for %s sec for BGP to converge on router" " %s...", @@ -1536,8 +1537,8 @@ def clear_bgp_and_verify(tgen, topo, router): ) else: errormsg = ( - "TIMEOUT!! BGP is not converged in 30 seconds for" - " router {}".format(router) + "TIMEOUT!! BGP is not converged in {} seconds for" + " router {}".format(retry * sleeptime, router) ) return errormsg @@ -1552,7 +1553,7 @@ def clear_bgp_and_verify(tgen, topo, router): peer_uptime_after_clear_bgp = {} # Verifying BGP convergence after bgp clear command for retry in range(44): - sleeptime = 3 + # Waiting for BGP to converge logger.info( "Waiting for %s sec for BGP to converge on router" " %s...", @@ -1615,8 +1616,8 @@ def clear_bgp_and_verify(tgen, topo, router): ) else: errormsg = ( - "TIMEOUT!! BGP is not converged in 30 seconds for" - " router {}".format(router) + "TIMEOUT!! BGP is not converged in {} seconds for" + " router {}".format(retry * sleeptime, router) ) return errormsg @@ -2102,7 +2103,7 @@ def verify_best_path_as_per_bgp_attribute( routes = generate_ips(_network, no_of_ip) for route in routes: - route = str(ipaddr.IPNetwork(unicode(route))) + route = str(ipaddress.ip_network(unicode(route))) if route in sh_ip_bgp_json["routes"]: route_attributes = sh_ip_bgp_json["routes"][route] @@ -2411,7 +2412,7 @@ def verify_bgp_rib(tgen, addr_type, dut, input_dict, next_hop=None, aspath=None) ip_list = generate_ips(network, no_of_ip) for st_rt in ip_list: - st_rt = str(ipaddr.IPNetwork(unicode(st_rt))) + st_rt = str(ipaddress.ip_network(unicode(st_rt))) _addr_type = validate_ip_address(st_rt) if _addr_type != addr_type: @@ -2547,7 +2548,7 @@ def verify_bgp_rib(tgen, addr_type, dut, input_dict, next_hop=None, aspath=None) ip_list = generate_ips(network, no_of_network) for st_rt in ip_list: - st_rt = str(ipaddr.IPNetwork(unicode(st_rt))) + st_rt = str(ipaddress.ip_network(unicode(st_rt))) _addr_type = validate_ip_address(st_rt) if _addr_type != addr_type: diff --git a/tests/topotests/lib/common_config.py b/tests/topotests/lib/common_config.py index 21ed47fc4b..fb82b50628 100644 --- a/tests/topotests/lib/common_config.py +++ b/tests/topotests/lib/common_config.py @@ -36,7 +36,7 @@ import sys import ConfigParser import traceback import socket -import ipaddr +import ipaddress from lib.topolog import logger, logger_config from lib.topogen import TopoRouter, get_topogen @@ -1066,10 +1066,10 @@ def generate_ips(network, no_of_ips): addr_type = validate_ip_address(start_ip) if addr_type == "ipv4": - start_ip = ipaddr.IPv4Address(unicode(start_ip)) + start_ip = ipaddress.IPv4Address(unicode(start_ip)) step = 2 ** (32 - mask) if addr_type == "ipv6": - start_ip = ipaddr.IPv6Address(unicode(start_ip)) + start_ip = ipaddress.IPv6Address(unicode(start_ip)) step = 2 ** (128 - mask) next_ip = start_ip @@ -1077,7 +1077,7 @@ def generate_ips(network, no_of_ips): while count < no_of_ips: ipaddress_list.append("{}/{}".format(next_ip, mask)) if addr_type == "ipv6": - next_ip = ipaddr.IPv6Address(int(next_ip) + step) + next_ip = ipaddress.IPv6Address(int(next_ip) + step) else: next_ip += step count += 1 @@ -2273,7 +2273,7 @@ def verify_rib( nh_found = False for st_rt in ip_list: - st_rt = str(ipaddr.IPNetwork(unicode(st_rt))) + st_rt = str(ipaddress.ip_network(unicode(st_rt))) _addr_type = validate_ip_address(st_rt) if _addr_type != addr_type: @@ -2469,7 +2469,7 @@ def verify_rib( nh_found = False for st_rt in ip_list: - st_rt = str(ipaddr.IPNetwork(unicode(st_rt))) + st_rt = str(ipaddress.ip_network(unicode(st_rt))) _addr_type = validate_ip_address(st_rt) if _addr_type != addr_type: diff --git a/tests/topotests/lib/topojson.py b/tests/topotests/lib/topojson.py index 24b61981d6..9c2baedde4 100644 --- a/tests/topotests/lib/topojson.py +++ b/tests/topotests/lib/topojson.py @@ -21,7 +21,7 @@ from collections import OrderedDict from json import dumps as json_dumps from re import search as re_search -import ipaddr +import ipaddress import pytest # Import topogen and topotest helpers @@ -65,12 +65,12 @@ def build_topo_from_json(tgen, topo): listRouters.append(routerN) if "ipv4base" in topo: - ipv4Next = ipaddr.IPv4Address(topo["link_ip_start"]["ipv4"]) + ipv4Next = ipaddress.IPv4Address(topo["link_ip_start"]["ipv4"]) ipv4Step = 2 ** (32 - topo["link_ip_start"]["v4mask"]) if topo["link_ip_start"]["v4mask"] < 32: ipv4Next += 1 if "ipv6base" in topo: - ipv6Next = ipaddr.IPv6Address(topo["link_ip_start"]["ipv6"]) + ipv6Next = ipaddress.IPv6Address(topo["link_ip_start"]["ipv6"]) ipv6Step = 2 ** (128 - topo["link_ip_start"]["v6mask"]) if topo["link_ip_start"]["v6mask"] < 127: ipv6Next += 1 @@ -181,7 +181,7 @@ def build_topo_from_json(tgen, topo): destRouter_link_json["ipv6"] = "{}/{}".format( ipv6Next + 1, topo["link_ip_start"]["v6mask"] ) - ipv6Next = ipaddr.IPv6Address(int(ipv6Next) + ipv6Step) + ipv6Next = ipaddress.IPv6Address(int(ipv6Next) + ipv6Step) logger.debug( "Generated link data for router: %s\n%s", diff --git a/tests/topotests/lib/topotest.py b/tests/topotests/lib/topotest.py index 22ed4b4d0f..bffb8208e7 100644 --- a/tests/topotests/lib/topotest.py +++ b/tests/topotests/lib/topotest.py @@ -1180,9 +1180,11 @@ class Router(Node): def startRouterDaemons(self, daemons=None): "Starts all FRR daemons for this router." - bundle_data = subprocess.check_output( - ["cat /etc/frr/support_bundle_commands.conf"], shell=True - ) + bundle_data = '' + + if os.path.exists('/etc/frr/support_bundle_commands.conf'): + bundle_data = subprocess.check_output( + ["cat /etc/frr/support_bundle_commands.conf"], shell=True) self.cmd( "echo '{}' > /etc/frr/support_bundle_commands.conf".format(bundle_data) ) diff --git a/tests/topotests/zebra_rib/r1/v4_route_1_static_override.json b/tests/topotests/zebra_rib/r1/v4_route_1_static_override.json index aa9522aff6..22e199f9aa 100644 --- a/tests/topotests/zebra_rib/r1/v4_route_1_static_override.json +++ b/tests/topotests/zebra_rib/r1/v4_route_1_static_override.json @@ -10,7 +10,7 @@ "installed":true, "table":254, "internalStatus":16, - "internalFlags":72, + "internalFlags":73, "internalNextHopNum":1, "internalNextHopActiveNum":1, "nexthops":[ diff --git a/tools/frr-reload.py b/tools/frr-reload.py index 200279b125..9e86cf2156 100755 --- a/tools/frr-reload.py +++ b/tools/frr-reload.py @@ -588,6 +588,7 @@ end line.startswith("vnc defaults") or line.startswith("vnc l2-group") or line.startswith("vnc nve-group") or + line.startswith("peer") or line.startswith("member pseudowire")): main_ctx_key = [] diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index 29e0842daf..9e5885c7b6 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -809,6 +809,9 @@ int vtysh_mark_file(const char *filename) } else if ((prev_node == KEYCHAIN_KEY_NODE) && (tried == 1)) { vty_out(vty, "exit\n"); + } else if ((prev_node == BFD_PEER_NODE) + && (tried == 1)) { + vty_out(vty, "exit\n"); } else if (tried) { vty_out(vty, "end\n"); } diff --git a/zebra/rib.h b/zebra/rib.h index a024b6dfaa..ec992974fa 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -94,9 +94,11 @@ struct route_entry { struct nhg_hash_entry *nhe; /* Nexthop group from FIB (optional), reflecting what is actually - * installed in the FIB if that differs. + * installed in the FIB if that differs. The 'backup' group is used + * when backup nexthops are present in the route's nhg. */ struct nexthop_group fib_ng; + struct nexthop_group fib_backup_ng; /* Nexthop group hash entry ID */ uint32_t nhe_id; @@ -526,7 +528,7 @@ DECLARE_HOOK(rib_update, (struct route_node * rn, const char *reason), /* * Access active nexthop-group, either RIB or FIB version */ -static inline struct nexthop_group *rib_active_nhg(struct route_entry *re) +static inline struct nexthop_group *rib_get_fib_nhg(struct route_entry *re) { if (re->fib_ng.nexthop) return &(re->fib_ng); @@ -534,6 +536,18 @@ static inline struct nexthop_group *rib_active_nhg(struct route_entry *re) return &(re->nhe->nhg); } +/* + * Access active nexthop-group, either RIB or FIB version + */ +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); +} + extern void zebra_vty_init(void); extern pid_t pid; diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 9883e73876..40a7eeba8e 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -1713,7 +1713,7 @@ ssize_t netlink_route_multipath_msg_encode(int cmd, nl_attr_nest_end(&req->n, nest); } - if (kernel_nexthops_supported() || force_nhg) { + if ((!fpm && kernel_nexthops_supported()) || (fpm && force_nhg)) { /* Kernel supports nexthop objects */ if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug("%s: %pFX nhg_id is %u", __func__, p, diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index a40aa8b643..dc7c595d26 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -2485,7 +2485,7 @@ static void zread_vrf_label(ZAPI_HANDLER_ARGS) if (really_remove) mpls_lsp_uninstall(def_zvrf, ltype, zvrf->label[afi], NEXTHOP_TYPE_IFINDEX, NULL, - ifp->ifindex); + ifp->ifindex, false /*backup*/); } if (nlabel != MPLS_LABEL_NONE) { diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index 64383fc81c..e34b6f23ff 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -1149,6 +1149,37 @@ void dplane_ctx_set_nexthops(struct zebra_dplane_ctx *ctx, struct nexthop *nh) nexthop_group_copy_nh_sorted(&(ctx->u.rinfo.zd_ng), nh); } +/* + * Set the list of backup nexthops; their ordering is preserved (they're not + * re-sorted.) + */ +void dplane_ctx_set_backup_nhg(struct zebra_dplane_ctx *ctx, + const struct nexthop_group *nhg) +{ + struct nexthop *nh, *last_nh, *nexthop; + + DPLANE_CTX_VALID(ctx); + + if (ctx->u.rinfo.backup_ng.nexthop) { + nexthops_free(ctx->u.rinfo.backup_ng.nexthop); + ctx->u.rinfo.backup_ng.nexthop = NULL; + } + + last_nh = NULL; + + /* Be careful to preserve the order of the backup list */ + for (nh = nhg->nexthop; nh; nh = nh->next) { + nexthop = nexthop_dup(nh, NULL); + + if (last_nh) + NEXTHOP_APPEND(last_nh, nexthop); + else + ctx->u.rinfo.backup_ng.nexthop = nexthop; + + last_nh = nexthop; + } +} + uint32_t dplane_ctx_get_nhg_id(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); @@ -1303,7 +1334,7 @@ const struct nhlfe_list_head *dplane_ctx_get_backup_nhlfe_list( zebra_nhlfe_t *dplane_ctx_add_nhlfe(struct zebra_dplane_ctx *ctx, enum lsp_types_t lsp_type, enum nexthop_types_t nh_type, - union g_addr *gate, + const union g_addr *gate, ifindex_t ifindex, uint8_t num_labels, mpls_label_t *out_labels) @@ -1322,7 +1353,7 @@ zebra_nhlfe_t *dplane_ctx_add_nhlfe(struct zebra_dplane_ctx *ctx, zebra_nhlfe_t *dplane_ctx_add_backup_nhlfe(struct zebra_dplane_ctx *ctx, enum lsp_types_t lsp_type, enum nexthop_types_t nh_type, - union g_addr *gate, + const union g_addr *gate, ifindex_t ifindex, uint8_t num_labels, mpls_label_t *out_labels) @@ -1921,18 +1952,12 @@ done: /* * Capture information for an LSP update in a dplane context. */ -static int dplane_ctx_lsp_init(struct zebra_dplane_ctx *ctx, - enum dplane_op_e op, - zebra_lsp_t *lsp) +int dplane_ctx_lsp_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op, + zebra_lsp_t *lsp) { int ret = AOK; zebra_nhlfe_t *nhlfe, *new_nhlfe; - if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) - zlog_debug("init dplane ctx %s: in-label %u ecmp# %d", - dplane_op2str(op), lsp->ile.in_label, - lsp->num_ecmp); - ctx->zd_op = op; ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS; @@ -1944,6 +1969,20 @@ static int dplane_ctx_lsp_init(struct zebra_dplane_ctx *ctx, nhlfe_list_init(&(ctx->u.lsp.nhlfe_list)); nhlfe_list_init(&(ctx->u.lsp.backup_nhlfe_list)); + + /* This may be called to create/init a dplane context, not necessarily + * to copy an lsp object. + */ + if (lsp == NULL) { + ret = AOK; + goto done; + } + + if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) + zlog_debug("init dplane ctx %s: in-label %u ecmp# %d", + dplane_op2str(op), lsp->ile.in_label, + lsp->num_ecmp); + ctx->u.lsp.ile = lsp->ile; ctx->u.lsp.addr_family = lsp->addr_family; ctx->u.lsp.num_ecmp = lsp->num_ecmp; @@ -2012,6 +2051,7 @@ static int dplane_ctx_pw_init(struct zebra_dplane_ctx *ctx, struct route_table *table; struct route_node *rn; struct route_entry *re; + const struct nexthop_group *nhg; if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) zlog_debug("init dplane ctx %s: pw '%s', loc %u, rem %u", @@ -2062,10 +2102,11 @@ static int dplane_ctx_pw_init(struct zebra_dplane_ctx *ctx, break; } - if (re) + if (re) { + nhg = rib_get_fib_nhg(re); copy_nexthops(&(ctx->u.pw.nhg.nexthop), - re->nhe->nhg.nexthop, NULL); - + nhg->nexthop, NULL); + } route_unlock_node(rn); } } @@ -2442,7 +2483,7 @@ dplane_route_notif_update(struct route_node *rn, new_ctx->u.rinfo.zd_ng.nexthop = NULL; copy_nexthops(&(new_ctx->u.rinfo.zd_ng.nexthop), - (rib_active_nhg(re))->nexthop, NULL); + (rib_get_fib_nhg(re))->nexthop, NULL); for (ALL_NEXTHOPS(new_ctx->u.rinfo.zd_ng, nexthop)) UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h index 9e07231fea..8e873886df 100644 --- a/zebra/zebra_dplane.h +++ b/zebra/zebra_dplane.h @@ -283,6 +283,8 @@ void dplane_ctx_set_distance(struct zebra_dplane_ctx *ctx, uint8_t distance); uint8_t dplane_ctx_get_old_distance(const struct zebra_dplane_ctx *ctx); void dplane_ctx_set_nexthops(struct zebra_dplane_ctx *ctx, struct nexthop *nh); +void dplane_ctx_set_backup_nhg(struct zebra_dplane_ctx *ctx, + const struct nexthop_group *nhg); uint32_t dplane_ctx_get_nhg_id(const struct zebra_dplane_ctx *ctx); const struct nexthop_group *dplane_ctx_get_ng( @@ -308,6 +310,14 @@ dplane_ctx_get_nhe_nh_grp(const struct zebra_dplane_ctx *ctx); uint8_t dplane_ctx_get_nhe_nh_grp_count(const struct zebra_dplane_ctx *ctx); /* Accessors for LSP information */ + +/* Init the internal LSP data struct - necessary before adding to it. + * If 'lsp' is non-NULL, info will be copied from it to the internal + * context data area. + */ +int dplane_ctx_lsp_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op, + zebra_lsp_t *lsp); + mpls_label_t dplane_ctx_get_in_label(const struct zebra_dplane_ctx *ctx); void dplane_ctx_set_in_label(struct zebra_dplane_ctx *ctx, mpls_label_t label); @@ -325,7 +335,7 @@ const struct nhlfe_list_head *dplane_ctx_get_backup_nhlfe_list( zebra_nhlfe_t *dplane_ctx_add_nhlfe(struct zebra_dplane_ctx *ctx, enum lsp_types_t lsp_type, enum nexthop_types_t nh_type, - union g_addr *gate, + const union g_addr *gate, ifindex_t ifindex, uint8_t num_labels, mpls_label_t *out_labels); @@ -333,7 +343,7 @@ zebra_nhlfe_t *dplane_ctx_add_nhlfe(struct zebra_dplane_ctx *ctx, zebra_nhlfe_t *dplane_ctx_add_backup_nhlfe(struct zebra_dplane_ctx *ctx, enum lsp_types_t lsp_type, enum nexthop_types_t nh_type, - union g_addr *gate, + const union g_addr *gate, ifindex_t ifindex, uint8_t num_labels, mpls_label_t *out_labels); diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c index 8ee8601689..e741268ebb 100644 --- a/zebra/zebra_mpls.c +++ b/zebra/zebra_mpls.c @@ -110,17 +110,14 @@ static zebra_nhlfe_t *nhlfe_find(struct nhlfe_list_head *list, static zebra_nhlfe_t *nhlfe_add(zebra_lsp_t *lsp, enum lsp_types_t lsp_type, enum nexthop_types_t gtype, const union g_addr *gate, ifindex_t ifindex, - uint8_t num_labels, const mpls_label_t *labels); + uint8_t num_labels, const mpls_label_t *labels, + bool is_backup); static int nhlfe_del(zebra_nhlfe_t *nhlfe); static void nhlfe_free(zebra_nhlfe_t *nhlfe); static void nhlfe_out_label_update(zebra_nhlfe_t *nhlfe, struct mpls_label_stack *nh_label); static int mpls_lsp_uninstall_all(struct hash *lsp_table, zebra_lsp_t *lsp, enum lsp_types_t type); -static int lsp_backup_uninstall(struct zebra_vrf *zvrf, enum lsp_types_t type, - mpls_label_t in_label, - enum nexthop_types_t gtype, - const union g_addr *gate, ifindex_t ifindex); 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); @@ -167,6 +164,15 @@ static void clear_nhlfe_installed(zebra_lsp_t *lsp) UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED); UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); } + + frr_each_safe(nhlfe_list, &lsp->backup_nhlfe_list, nhlfe) { + nexthop = nhlfe->nexthop; + if (!nexthop) + continue; + + UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED); + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); + } } /* @@ -240,7 +246,8 @@ static int lsp_install(struct zebra_vrf *zvrf, mpls_label_t label, nhlfe = nhlfe_add(lsp, lsp_type, nexthop->type, &nexthop->gate, nexthop->ifindex, nexthop->nh_label->num_labels, - nexthop->nh_label->label); + nexthop->nh_label->label, + false /*backup*/); if (!nhlfe) return -1; @@ -797,8 +804,7 @@ static void lsp_select_best_nhlfe(zebra_lsp_t *lsp) /* * First compute the best path, after checking nexthop status. We are - * only - * concerned with non-deleted NHLFEs. + * only concerned with non-deleted NHLFEs. */ frr_each_safe(nhlfe_list, &lsp->nhlfe_list, nhlfe) { /* Clear selection flags. */ @@ -816,6 +822,14 @@ static void lsp_select_best_nhlfe(zebra_lsp_t *lsp) if (!lsp->best_nhlfe) return; + /* + * Check the active status of backup nhlfes also + */ + frr_each_safe(nhlfe_list, &lsp->backup_nhlfe_list, nhlfe) { + if (!CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_DELETED)) + (void)nhlfe_nexthop_active(nhlfe); + } + /* Mark best NHLFE as selected. */ SET_FLAG(lsp->best_nhlfe->flags, NHLFE_FLAG_SELECTED); @@ -910,9 +924,9 @@ static wq_item_status lsp_process(struct work_queue *wq, void *data) if (IS_ZEBRA_DEBUG_MPLS) { if (oldbest) - nhlfe2str(oldbest, buf, BUFSIZ); + nhlfe2str(oldbest, buf, sizeof(buf)); if (newbest) - nhlfe2str(newbest, buf2, BUFSIZ); + nhlfe2str(newbest, buf2, sizeof(buf2)); zlog_debug( "Process LSP in-label %u oldbest %s newbest %s " "flags 0x%x ecmp# %d", @@ -1310,13 +1324,14 @@ static zebra_nhlfe_t *nhlfe_alloc(zebra_lsp_t *lsp, enum lsp_types_t lsp_type, } /* - * Add NHLFE. Base entry must have been created and duplicate - * check done. + * Add primary or backup NHLFE. Base entry must have been created and + * duplicate check done. */ static zebra_nhlfe_t *nhlfe_add(zebra_lsp_t *lsp, enum lsp_types_t lsp_type, enum nexthop_types_t gtype, const union g_addr *gate, ifindex_t ifindex, - uint8_t num_labels, const mpls_label_t *labels) + uint8_t num_labels, const mpls_label_t *labels, + bool is_backup) { zebra_nhlfe_t *nhlfe; @@ -1327,36 +1342,12 @@ static zebra_nhlfe_t *nhlfe_add(zebra_lsp_t *lsp, enum lsp_types_t lsp_type, nhlfe = nhlfe_alloc(lsp, lsp_type, gtype, gate, ifindex, num_labels, labels); - /* Enqueue to LSP, at head of list. */ - nhlfe_list_add_head(&lsp->nhlfe_list, nhlfe); - - return nhlfe; -} - -/* - * Add backup NHLFE. Base entry must have been created and duplicate - * check done. - */ -static zebra_nhlfe_t *nhlfe_backup_add(zebra_lsp_t *lsp, - enum lsp_types_t lsp_type, - enum nexthop_types_t gtype, - const union g_addr *gate, - ifindex_t ifindex, uint8_t num_labels, - const mpls_label_t *labels) -{ - zebra_nhlfe_t *nhlfe; - - if (!lsp) - return NULL; - - /* Allocate new object */ - nhlfe = nhlfe_alloc(lsp, lsp_type, gtype, gate, ifindex, num_labels, - labels); - - SET_FLAG(nhlfe->flags, NHLFE_FLAG_IS_BACKUP); - - /* Enqueue to LSP, at tail of list. */ - nhlfe_list_add_tail(&lsp->backup_nhlfe_list, nhlfe); + /* Enqueue to LSP: primaries at head of list, backups at tail */ + if (is_backup) { + SET_FLAG(nhlfe->flags, NHLFE_FLAG_IS_BACKUP); + nhlfe_list_add_tail(&lsp->backup_nhlfe_list, nhlfe); + } else + nhlfe_list_add_head(&lsp->nhlfe_list, nhlfe); return nhlfe; } @@ -1590,6 +1581,9 @@ static void nhlfe_print(zebra_nhlfe_t *nhlfe, struct vty *vty) break; } vty_out(vty, "%s", + CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_IS_BACKUP) ? " (backup)" + : ""); + vty_out(vty, "%s", CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED) ? " (installed)" : ""); vty_out(vty, "\n"); @@ -1616,6 +1610,7 @@ static void lsp_print(struct vty *vty, zebra_lsp_t *lsp) /* Find backup in backup list */ i = 0; + backup = NULL; frr_each(nhlfe_list, &lsp->backup_nhlfe_list, backup) { if (i == nhlfe->nexthop->backup_idx) break; @@ -1933,23 +1928,27 @@ void zebra_mpls_lsp_dplane_result(struct zebra_dplane_ctx *ctx) /* TODO -- Confirm that this result is still 'current' */ - if (status == ZEBRA_DPLANE_REQUEST_SUCCESS) { - /* Update zebra object */ - SET_FLAG(lsp->flags, LSP_FLAG_INSTALLED); - frr_each(nhlfe_list, &lsp->nhlfe_list, nhlfe) { - nexthop = nhlfe->nexthop; - if (!nexthop) - continue; - - SET_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED); - SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); - } - } else { + if (status != ZEBRA_DPLANE_REQUEST_SUCCESS) { UNSET_FLAG(lsp->flags, LSP_FLAG_INSTALLED); clear_nhlfe_installed(lsp); flog_warn(EC_ZEBRA_LSP_INSTALL_FAILURE, "LSP Install Failure: in-label %u", lsp->ile.in_label); + break; + } + + /* Update zebra object */ + SET_FLAG(lsp->flags, LSP_FLAG_INSTALLED); + frr_each(nhlfe_list, &lsp->nhlfe_list, nhlfe) { + nexthop = nhlfe->nexthop; + if (!nexthop) + continue; + + if (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_SELECTED) && + CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) { + SET_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED); + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); + } } break; @@ -1970,53 +1969,23 @@ void zebra_mpls_lsp_dplane_result(struct zebra_dplane_ctx *ctx) } /* - * Process async dplane notifications. + * Process LSP installation info from two sets of nhlfes: a set from + * a dplane notification, and a set from the zebra LSP object. Update + * counters of installed nexthops, and return whether the LSP has changed. */ -void zebra_mpls_process_dplane_notify(struct zebra_dplane_ctx *ctx) +static bool compare_notif_nhlfes(const struct nhlfe_list_head *ctx_head, + struct nhlfe_list_head *nhlfe_head, + int *start_counter, int *end_counter) { - struct zebra_vrf *zvrf; - zebra_ile_t tmp_ile; - struct hash *lsp_table; - zebra_lsp_t *lsp; zebra_nhlfe_t *nhlfe; - const struct nhlfe_list_head *head; const zebra_nhlfe_t *ctx_nhlfe; struct nexthop *nexthop; const struct nexthop *ctx_nexthop; - int start_count = 0, end_count = 0; /* Installed counts */ + int start_count = 0, end_count = 0; bool changed_p = false; bool is_debug = (IS_ZEBRA_DEBUG_DPLANE | IS_ZEBRA_DEBUG_MPLS); - if (is_debug) - zlog_debug("LSP dplane notif, in-label %u", - dplane_ctx_get_in_label(ctx)); - - /* Look for zebra LSP object */ - zvrf = vrf_info_lookup(VRF_DEFAULT); - if (zvrf == NULL) - goto done; - - lsp_table = zvrf->lsp_table; - - tmp_ile.in_label = dplane_ctx_get_in_label(ctx); - lsp = hash_lookup(lsp_table, &tmp_ile); - if (lsp == NULL) { - if (is_debug) - zlog_debug("dplane LSP notif: in-label %u not found", - dplane_ctx_get_in_label(ctx)); - goto done; - } - - /* - * The dataplane/forwarding plane is notifying zebra about the state - * of the nexthops associated with this LSP. First, we take a - * pre-scan pass to determine whether the LSP has transitioned - * from installed -> uninstalled. In that case, we need to have - * the existing state of the LSP objects available before making - * any changes. - */ - head = dplane_ctx_get_nhlfe_list(ctx); - frr_each_safe(nhlfe_list, &lsp->nhlfe_list, nhlfe) { + frr_each_safe(nhlfe_list, nhlfe_head, nhlfe) { char buf[NEXTHOP_STRLEN]; nexthop = nhlfe->nexthop; @@ -2026,8 +1995,9 @@ void zebra_mpls_process_dplane_notify(struct zebra_dplane_ctx *ctx) if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) start_count++; + ctx_nhlfe = NULL; ctx_nexthop = NULL; - frr_each(nhlfe_list_const, head, ctx_nhlfe) { + frr_each(nhlfe_list_const, ctx_head, ctx_nhlfe) { ctx_nexthop = ctx_nhlfe->nexthop; if (!ctx_nexthop) continue; @@ -2085,32 +2055,39 @@ void zebra_mpls_process_dplane_notify(struct zebra_dplane_ctx *ctx) } } - if (is_debug) - zlog_debug("LSP dplane notif: lfib start_count %d, end_count %d%s", - start_count, end_count, - changed_p ? ", changed" : ""); + if (start_counter) + *start_counter += start_count; + if (end_counter) + *end_counter += end_count; - /* - * Has the LSP become uninstalled? - */ - if (start_count > 0 && end_count == 0) { - /* Inform other lfibs */ - dplane_lsp_notif_update(lsp, DPLANE_OP_LSP_DELETE, ctx); - } + return changed_p; +} - /* - * Now we take a second pass and bring the zebra - * nexthop state into sync with the forwarding-plane state. - */ - frr_each_safe(nhlfe_list, &lsp->nhlfe_list, nhlfe) { +/* + * Update an lsp nhlfe list from a dplane context, typically an async + * notification context. Update the LSP list to match the installed + * status from the context's list. + */ +static int update_nhlfes_from_ctx(struct nhlfe_list_head *nhlfe_head, + const struct nhlfe_list_head *ctx_head) +{ + int ret = 0; + zebra_nhlfe_t *nhlfe; + const zebra_nhlfe_t *ctx_nhlfe; + struct nexthop *nexthop; + const struct nexthop *ctx_nexthop; + bool is_debug = (IS_ZEBRA_DEBUG_DPLANE | IS_ZEBRA_DEBUG_MPLS); + + frr_each_safe(nhlfe_list, nhlfe_head, nhlfe) { char buf[NEXTHOP_STRLEN]; nexthop = nhlfe->nexthop; if (!nexthop) continue; + ctx_nhlfe = NULL; ctx_nexthop = NULL; - frr_each(nhlfe_list_const, head, ctx_nhlfe) { + frr_each(nhlfe_list_const, ctx_head, ctx_nhlfe) { ctx_nexthop = ctx_nhlfe->nexthop; if (!ctx_nexthop) continue; @@ -2130,10 +2107,16 @@ void zebra_mpls_process_dplane_notify(struct zebra_dplane_ctx *ctx) /* Bring zebra nhlfe install state into sync */ if (CHECK_FLAG(ctx_nhlfe->flags, NHLFE_FLAG_INSTALLED)) { + if (is_debug) + zlog_debug("%s: matched lsp nhlfe %s (installed)", + __func__, buf); SET_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED); } else { + if (is_debug) + zlog_debug("%s: matched lsp nhlfe %s (not installed)", + __func__, buf); UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED); } @@ -2153,13 +2136,101 @@ void zebra_mpls_process_dplane_notify(struct zebra_dplane_ctx *ctx) } else { /* Not mentioned in lfib set -> uninstalled */ - + if (is_debug) + zlog_debug("%s: no match for lsp nhlfe %s", + __func__, buf); UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED); UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); } } + return ret; +} + +/* + * Process async dplane notifications. + */ +void zebra_mpls_process_dplane_notify(struct zebra_dplane_ctx *ctx) +{ + struct zebra_vrf *zvrf; + zebra_ile_t tmp_ile; + struct hash *lsp_table; + zebra_lsp_t *lsp; + const struct nhlfe_list_head *ctx_list; + int start_count = 0, end_count = 0; /* Installed counts */ + bool changed_p = false; + bool is_debug = (IS_ZEBRA_DEBUG_DPLANE | IS_ZEBRA_DEBUG_MPLS); + + if (is_debug) + zlog_debug("LSP dplane notif, in-label %u", + dplane_ctx_get_in_label(ctx)); + + /* Look for zebra LSP object */ + zvrf = vrf_info_lookup(VRF_DEFAULT); + if (zvrf == NULL) + goto done; + + lsp_table = zvrf->lsp_table; + + tmp_ile.in_label = dplane_ctx_get_in_label(ctx); + lsp = hash_lookup(lsp_table, &tmp_ile); + if (lsp == NULL) { + if (is_debug) + zlog_debug("dplane LSP notif: in-label %u not found", + dplane_ctx_get_in_label(ctx)); + goto done; + } + + /* + * The dataplane/forwarding plane is notifying zebra about the state + * of the nexthops associated with this LSP. First, we take a + * pre-scan pass to determine whether the LSP has transitioned + * from installed -> uninstalled. In that case, we need to have + * the existing state of the LSP objects available before making + * any changes. + */ + ctx_list = dplane_ctx_get_nhlfe_list(ctx); + + changed_p = compare_notif_nhlfes(ctx_list, &lsp->nhlfe_list, + &start_count, &end_count); + + if (is_debug) + zlog_debug("LSP dplane notif: lfib start_count %d, end_count %d%s", + start_count, end_count, + changed_p ? ", changed" : ""); + + ctx_list = dplane_ctx_get_backup_nhlfe_list(ctx); + + if (compare_notif_nhlfes(ctx_list, &lsp->backup_nhlfe_list, + &start_count, &end_count)) + /* Avoid accidentally setting back to 'false' */ + changed_p = true; + + if (is_debug) + zlog_debug("LSP dplane notif: lfib backups, start_count %d, end_count %d%s", + start_count, end_count, + changed_p ? ", changed" : ""); + + /* + * Has the LSP become uninstalled? We need the existing state of the + * nexthops/nhlfes at this point so we know what to delete. + */ + if (start_count > 0 && end_count == 0) { + /* Inform other lfibs */ + dplane_lsp_notif_update(lsp, DPLANE_OP_LSP_DELETE, ctx); + } + + /* + * Now we take a second pass and bring the zebra + * nexthop state into sync with the forwarding-plane state. + */ + ctx_list = dplane_ctx_get_nhlfe_list(ctx); + update_nhlfes_from_ctx(&lsp->nhlfe_list, ctx_list); + + ctx_list = dplane_ctx_get_backup_nhlfe_list(ctx); + update_nhlfes_from_ctx(&lsp->backup_nhlfe_list, ctx_list); + if (end_count > 0) { SET_FLAG(lsp->flags, LSP_FLAG_INSTALLED); @@ -2234,14 +2305,14 @@ int zebra_mpls_lsp_uninstall(struct zebra_vrf *zvrf, struct route_node *rn, zebra_nhlfe_t *zebra_mpls_lsp_add_nhlfe(zebra_lsp_t *lsp, enum lsp_types_t lsp_type, enum nexthop_types_t gtype, - union g_addr *gate, + const union g_addr *gate, ifindex_t ifindex, uint8_t num_labels, const mpls_label_t *out_labels) { /* Just a public pass-through to the internal implementation */ return nhlfe_add(lsp, lsp_type, gtype, gate, ifindex, num_labels, - out_labels); + out_labels, false /*backup*/); } /* @@ -2252,14 +2323,14 @@ zebra_nhlfe_t *zebra_mpls_lsp_add_nhlfe(zebra_lsp_t *lsp, zebra_nhlfe_t *zebra_mpls_lsp_add_backup_nhlfe(zebra_lsp_t *lsp, enum lsp_types_t lsp_type, enum nexthop_types_t gtype, - union g_addr *gate, + const union g_addr *gate, ifindex_t ifindex, uint8_t num_labels, const mpls_label_t *out_labels) { /* Just a public pass-through to the internal implementation */ - return nhlfe_backup_add(lsp, lsp_type, gtype, gate, ifindex, num_labels, - out_labels); + return nhlfe_add(lsp, lsp_type, gtype, gate, ifindex, num_labels, + out_labels, true); } /* @@ -2275,7 +2346,8 @@ zebra_nhlfe_t *zebra_mpls_lsp_add_nh(zebra_lsp_t *lsp, return NULL; nhlfe = nhlfe_add(lsp, lsp_type, nh->type, &nh->gate, nh->ifindex, - nh->nh_label->num_labels, nh->nh_label->label); + nh->nh_label->num_labels, nh->nh_label->label, + false /*backup*/); return nhlfe; } @@ -2293,9 +2365,9 @@ zebra_nhlfe_t *zebra_mpls_lsp_add_backup_nh(zebra_lsp_t *lsp, if (nh->nh_label == NULL || nh->nh_label->num_labels == 0) return NULL; - nhlfe = nhlfe_backup_add(lsp, lsp_type, nh->type, &nh->gate, + nhlfe = nhlfe_add(lsp, lsp_type, nh->type, &nh->gate, nh->ifindex, nh->nh_label->num_labels, - nh->nh_label->label); + nh->nh_label->label, true); return nhlfe; } @@ -2846,6 +2918,9 @@ int mpls_ftn_uninstall(struct zebra_vrf *zvrf, enum lsp_types_t type, SET_FLAG(re->status, ROUTE_ENTRY_CHANGED); SET_FLAG(re->status, ROUTE_ENTRY_LABELS_CHANGED); + /* This will create (or ref) a new nhe, so we will discard the local + * temporary nhe + */ mpls_zebra_nhe_update(re, afi, new_nhe); zebra_nhg_free(new_nhe); @@ -3013,7 +3088,8 @@ int mpls_zapi_labels_process(bool add_p, struct zebra_vrf *zvrf, else ret = mpls_lsp_uninstall(zvrf, zl->type, zl->local_label, znh->type, - &znh->gate, znh->ifindex); + &znh->gate, znh->ifindex, + false); if (ret < 0) { if (IS_ZEBRA_DEBUG_RECV || IS_ZEBRA_DEBUG_MPLS) { zapi_nexthop2str(znh, buf, sizeof(buf)); @@ -3055,10 +3131,10 @@ int mpls_zapi_labels_process(bool add_p, struct zebra_vrf *zvrf, if (add_p) ret = lsp_backup_znh_install(lsp, zl->type, znh); else - ret = lsp_backup_uninstall(zvrf, zl->type, - zl->local_label, - znh->type, &znh->gate, - znh->ifindex); + ret = mpls_lsp_uninstall(zvrf, zl->type, + zl->local_label, + znh->type, &znh->gate, + znh->ifindex, true); if (ret < 0) { if (IS_ZEBRA_DEBUG_RECV || @@ -3123,92 +3199,22 @@ static zebra_nhlfe_t * lsp_add_nhlfe(zebra_lsp_t *lsp, enum lsp_types_t type, uint8_t num_out_labels, const mpls_label_t *out_labels, enum nexthop_types_t gtype, const union g_addr *gate, - ifindex_t ifindex) + ifindex_t ifindex, bool is_backup) { zebra_nhlfe_t *nhlfe; char buf[MPLS_LABEL_STRLEN]; + const char *backup_str; - nhlfe = nhlfe_find(&lsp->nhlfe_list, type, gtype, gate, ifindex); - if (nhlfe) { - struct nexthop *nh = nhlfe->nexthop; - - assert(nh); - assert(nh->nh_label); - - /* Clear deleted flag (in case it was set) */ - UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_DELETED); - if (nh->nh_label->num_labels == num_out_labels - && !memcmp(nh->nh_label->label, out_labels, - sizeof(mpls_label_t) * num_out_labels)) - /* No change */ - return nhlfe; - - if (IS_ZEBRA_DEBUG_MPLS) { - char buf2[MPLS_LABEL_STRLEN]; - char buf3[MPLS_LABEL_STRLEN]; - - nhlfe2str(nhlfe, buf, sizeof(buf)); - mpls_label2str(num_out_labels, out_labels, buf2, - sizeof(buf2), 0); - mpls_label2str(nh->nh_label->num_labels, - nh->nh_label->label, buf3, sizeof(buf3), - 0); - - zlog_debug("LSP in-label %u type %d nexthop %s out-label(s) changed to %s (old %s)", - lsp->ile.in_label, type, buf, buf2, buf3); - } - - /* Update out label(s), trigger processing. */ - if (nh->nh_label->num_labels == num_out_labels) - memcpy(nh->nh_label->label, out_labels, - sizeof(mpls_label_t) * num_out_labels); - else { - nexthop_del_labels(nh); - nexthop_add_labels(nh, type, num_out_labels, - out_labels); - } + if (is_backup) { + nhlfe = nhlfe_find(&lsp->backup_nhlfe_list, type, gtype, + gate, ifindex); + backup_str = "backup "; } else { - /* Add LSP entry to this nexthop */ - nhlfe = nhlfe_add(lsp, type, gtype, gate, ifindex, - num_out_labels, out_labels); - if (!nhlfe) - return NULL; - - if (IS_ZEBRA_DEBUG_MPLS) { - char buf2[MPLS_LABEL_STRLEN]; - - nhlfe2str(nhlfe, buf, sizeof(buf)); - mpls_label2str(num_out_labels, out_labels, buf2, - sizeof(buf2), 0); - - zlog_debug("Add LSP in-label %u type %d nexthop %s out-label(s) %s", - lsp->ile.in_label, type, buf, buf2); - } - - lsp->addr_family = NHLFE_FAMILY(nhlfe); + nhlfe = nhlfe_find(&lsp->nhlfe_list, type, gtype, gate, + ifindex); + backup_str = ""; } - /* Mark NHLFE, queue LSP for processing. */ - SET_FLAG(nhlfe->flags, NHLFE_FLAG_CHANGED); - - return nhlfe; -} - -/* - * Install/update a NHLFE for an LSP in the forwarding table. This may be - * a new LSP entry or a new NHLFE for an existing in-label or an update of - * the out-label for an existing NHLFE (update case). - */ -static zebra_nhlfe_t * -lsp_add_backup_nhlfe(zebra_lsp_t *lsp, enum lsp_types_t type, - uint8_t num_out_labels, const mpls_label_t *out_labels, - enum nexthop_types_t gtype, const union g_addr *gate, - ifindex_t ifindex) -{ - zebra_nhlfe_t *nhlfe; - char buf[MPLS_LABEL_STRLEN]; - - nhlfe = nhlfe_find(&lsp->backup_nhlfe_list, type, gtype, gate, ifindex); if (nhlfe) { struct nexthop *nh = nhlfe->nexthop; @@ -3234,8 +3240,9 @@ lsp_add_backup_nhlfe(zebra_lsp_t *lsp, enum lsp_types_t type, nh->nh_label->label, buf3, sizeof(buf3), 0); - zlog_debug("LSP in-label %u type %d backup nexthop %s out-label(s) changed to %s (old %s)", - lsp->ile.in_label, type, buf, buf2, buf3); + zlog_debug("LSP in-label %u type %d %snexthop %s out-label(s) changed to %s (old %s)", + lsp->ile.in_label, type, backup_str, buf, + buf2, buf3); } /* Update out label(s), trigger processing. */ @@ -3249,8 +3256,8 @@ lsp_add_backup_nhlfe(zebra_lsp_t *lsp, enum lsp_types_t type, } } else { /* Add LSP entry to this nexthop */ - nhlfe = nhlfe_backup_add(lsp, type, gtype, gate, ifindex, - num_out_labels, out_labels); + nhlfe = nhlfe_add(lsp, type, gtype, gate, ifindex, + num_out_labels, out_labels, is_backup); if (!nhlfe) return NULL; @@ -3261,8 +3268,9 @@ lsp_add_backup_nhlfe(zebra_lsp_t *lsp, enum lsp_types_t type, mpls_label2str(num_out_labels, out_labels, buf2, sizeof(buf2), 0); - zlog_debug("Add LSP in-label %u type %d backup nexthop %s out-label(s) %s", - lsp->ile.in_label, type, buf, buf2); + zlog_debug("Add LSP in-label %u type %d %snexthop %s out-label(s) %s", + lsp->ile.in_label, type, backup_str, buf, + buf2); } lsp->addr_family = NHLFE_FAMILY(nhlfe); @@ -3300,7 +3308,7 @@ int mpls_lsp_install(struct zebra_vrf *zvrf, enum lsp_types_t type, return -1; nhlfe = lsp_add_nhlfe(lsp, type, num_out_labels, out_labels, gtype, - gate, ifindex); + gate, ifindex, false /*backup*/); if (nhlfe == NULL) return -1; @@ -3320,7 +3328,8 @@ static int lsp_znh_install(zebra_lsp_t *lsp, enum lsp_types_t type, zebra_nhlfe_t *nhlfe; nhlfe = lsp_add_nhlfe(lsp, type, znh->label_num, znh->labels, - znh->type, &znh->gate, znh->ifindex); + znh->type, &znh->gate, znh->ifindex, + false /*backup*/); if (nhlfe == NULL) return -1; @@ -3345,9 +3354,9 @@ static int lsp_backup_znh_install(zebra_lsp_t *lsp, enum lsp_types_t type, { zebra_nhlfe_t *nhlfe; - nhlfe = lsp_add_backup_nhlfe(lsp, type, znh->label_num, - znh->labels, znh->type, &znh->gate, - znh->ifindex); + nhlfe = lsp_add_nhlfe(lsp, type, znh->label_num, + znh->labels, znh->type, &znh->gate, + znh->ifindex, true /*backup*/); if (nhlfe == NULL) { if (IS_ZEBRA_DEBUG_MPLS) zlog_debug("%s: unable to add backup nhlfe, label: %u", @@ -3368,13 +3377,14 @@ static int lsp_backup_znh_install(zebra_lsp_t *lsp, enum lsp_types_t type, */ int mpls_lsp_uninstall(struct zebra_vrf *zvrf, enum lsp_types_t type, mpls_label_t in_label, enum nexthop_types_t gtype, - const union g_addr *gate, ifindex_t ifindex) + const union g_addr *gate, ifindex_t ifindex, + bool backup_p) { struct hash *lsp_table; zebra_ile_t tmp_ile; zebra_lsp_t *lsp; zebra_nhlfe_t *nhlfe; - char buf[BUFSIZ]; + char buf[NEXTHOP_STRLEN]; bool schedule_lsp = false; /* Lookup table. */ @@ -3387,7 +3397,13 @@ int mpls_lsp_uninstall(struct zebra_vrf *zvrf, enum lsp_types_t type, lsp = hash_lookup(lsp_table, &tmp_ile); if (!lsp) return 0; - nhlfe = nhlfe_find(&lsp->nhlfe_list, type, gtype, gate, ifindex); + + if (backup_p) + nhlfe = nhlfe_find(&lsp->backup_nhlfe_list, type, gtype, + gate, ifindex); + else + nhlfe = nhlfe_find(&lsp->nhlfe_list, type, gtype, gate, + ifindex); if (!nhlfe) return 0; @@ -3420,56 +3436,6 @@ int mpls_lsp_uninstall(struct zebra_vrf *zvrf, enum lsp_types_t type, return 0; } -/* - * Uninstall a particular NHLFE in the forwarding table. If this is - * the only NHLFE, the entire LSP forwarding entry has to be deleted. - */ -static int lsp_backup_uninstall(struct zebra_vrf *zvrf, enum lsp_types_t type, - mpls_label_t in_label, - enum nexthop_types_t gtype, - const union g_addr *gate, ifindex_t ifindex) -{ - struct hash *lsp_table; - zebra_ile_t tmp_ile; - zebra_lsp_t *lsp; - zebra_nhlfe_t *nhlfe; - char buf[BUFSIZ]; - - /* Lookup table. */ - lsp_table = zvrf->lsp_table; - if (!lsp_table) - return -1; - - /* If entry is not present, exit. */ - tmp_ile.in_label = in_label; - lsp = hash_lookup(lsp_table, &tmp_ile); - if (!lsp) - return 0; - nhlfe = nhlfe_find(&lsp->backup_nhlfe_list, type, gtype, gate, ifindex); - if (!nhlfe) - return 0; - - if (IS_ZEBRA_DEBUG_MPLS) { - nhlfe2str(nhlfe, buf, BUFSIZ); - zlog_debug("Del backup LSP in-label %u type %d nexthop %s flags 0x%x", - in_label, type, buf, nhlfe->flags); - } - - /* Mark NHLFE for delete or directly delete, as appropriate. */ - if (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED)) { - UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_CHANGED); - SET_FLAG(nhlfe->flags, NHLFE_FLAG_DELETED); - if (lsp_processq_add(lsp)) - return -1; - } else { - nhlfe_del(nhlfe); - - /* Free LSP entry if no other NHLFEs and not scheduled. */ - lsp_check_free(lsp_table, &lsp); - } - return 0; -} - int mpls_lsp_uninstall_all_vrf(struct zebra_vrf *zvrf, enum lsp_types_t type, mpls_label_t in_label) { @@ -3754,7 +3720,7 @@ int zebra_mpls_static_lsp_del(struct zebra_vrf *zvrf, mpls_label_t in_label, /* Uninstall LSP from the main table. */ mpls_lsp_uninstall(zvrf, ZEBRA_LSP_STATIC, in_label, gtype, - gate, ifindex); + gate, ifindex, false); /* Delete static LSP NHLFE */ snhlfe_del(snhlfe); diff --git a/zebra/zebra_mpls.h b/zebra/zebra_mpls.h index 9b5fb39573..07a8efeb8b 100644 --- a/zebra/zebra_mpls.h +++ b/zebra/zebra_mpls.h @@ -213,7 +213,7 @@ int zebra_mpls_lsp_uninstall(struct zebra_vrf *zvrf, struct route_node *rn, zebra_nhlfe_t *zebra_mpls_lsp_add_nhlfe(zebra_lsp_t *lsp, enum lsp_types_t lsp_type, enum nexthop_types_t gtype, - union g_addr *gate, + const union g_addr *gate, ifindex_t ifindex, uint8_t num_labels, const mpls_label_t *out_labels); @@ -222,7 +222,7 @@ zebra_nhlfe_t *zebra_mpls_lsp_add_nhlfe(zebra_lsp_t *lsp, zebra_nhlfe_t *zebra_mpls_lsp_add_backup_nhlfe(zebra_lsp_t *lsp, enum lsp_types_t lsp_type, enum nexthop_types_t gtype, - union g_addr *gate, + const union g_addr *gate, ifindex_t ifindex, uint8_t num_labels, const mpls_label_t *out_labels); @@ -331,7 +331,8 @@ int mpls_lsp_install(struct zebra_vrf *zvrf, enum lsp_types_t type, */ int mpls_lsp_uninstall(struct zebra_vrf *zvrf, enum lsp_types_t type, mpls_label_t in_label, enum nexthop_types_t gtype, - const union g_addr *gate, ifindex_t ifindex); + const union g_addr *gate, ifindex_t ifindex, + bool backup_p); /* * Uninstall all NHLFEs for a particular LSP forwarding entry. @@ -339,12 +340,6 @@ int mpls_lsp_uninstall(struct zebra_vrf *zvrf, enum lsp_types_t type, int mpls_lsp_uninstall_all_vrf(struct zebra_vrf *zvrf, enum lsp_types_t type, mpls_label_t in_label); -/* - * Uninstall all Segment Routing NHLFEs for a particular LSP forwarding entry. - * If no other NHLFEs exist, the entry would be deleted. - */ -void mpls_sr_lsp_uninstall_all(struct hash_bucket *bucket, void *ctxt); - #if defined(HAVE_CUMULUS) /* * Check that the label values used in LSP creation are consistent. The diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c index 02ba69bd4d..9bfd7aacb7 100644 --- a/zebra/zebra_nhg.c +++ b/zebra/zebra_nhg.c @@ -1734,6 +1734,10 @@ static bool nexthop_valid_resolve(const struct nexthop *nexthop, if (CHECK_FLAG(resolved->flags, NEXTHOP_FLAG_RECURSIVE)) return false; + /* Must be ACTIVE */ + if (!CHECK_FLAG(resolved->flags, NEXTHOP_FLAG_ACTIVE)) + return false; + switch (nexthop->type) { case NEXTHOP_TYPE_IPV4_IFINDEX: case NEXTHOP_TYPE_IPV6_IFINDEX: @@ -1917,6 +1921,13 @@ static int nexthop_active(afi_t afi, struct route_entry *re, if (nexthop->type == NEXTHOP_TYPE_IPV4 || nexthop->type == NEXTHOP_TYPE_IPV6) nexthop->ifindex = newhop->ifindex; + else if (nexthop->ifindex != newhop->ifindex) { + /* + * NEXTHOP_TYPE_*_IFINDEX but ifindex + * doesn't match what we found. + */ + return 0; + } } if (IS_ZEBRA_DEBUG_NHG_DETAIL) @@ -1926,11 +1937,23 @@ static int nexthop_active(afi_t afi, struct route_entry *re, return 1; } else if (CHECK_FLAG(re->flags, ZEBRA_FLAG_ALLOW_RECURSION)) { + struct nexthop_group *nhg; + resolved = 0; - for (ALL_NEXTHOPS(match->nhe->nhg, newhop)) { - if (!CHECK_FLAG(match->status, - ROUTE_ENTRY_INSTALLED)) - continue; + + /* Only useful if installed */ + if (!CHECK_FLAG(match->status, ROUTE_ENTRY_INSTALLED)) { + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: match %p (%u) not installed", + __func__, match, + match->nhe->id); + + goto done_with_match; + } + + /* Examine installed nexthops */ + nhg = &match->nhe->nhg; + for (ALL_NEXTHOPS_PTR(nhg, newhop)) { if (!nexthop_valid_resolve(nexthop, newhop)) continue; @@ -1945,25 +1968,21 @@ static int nexthop_active(afi_t afi, struct route_entry *re, resolved = 1; } - if (resolved) - re->nexthop_mtu = match->mtu; - else if (IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug( - " %s: Recursion failed to find", - __func__); + /* Examine installed backup nexthops, if any. There + * are only installed backups *if* there is a + * dedicated fib list. + */ + nhg = rib_get_fib_backup_nhg(match); + if (nhg == NULL || + nhg == zebra_nhg_get_backup_nhg(match->nhe)) + goto done_with_match; - return resolved; - } else if (re->type == ZEBRA_ROUTE_STATIC) { - resolved = 0; - for (ALL_NEXTHOPS(match->nhe->nhg, newhop)) { - if (!CHECK_FLAG(match->status, - ROUTE_ENTRY_INSTALLED)) - continue; + for (ALL_NEXTHOPS_PTR(nhg, newhop)) { if (!nexthop_valid_resolve(nexthop, newhop)) continue; - if (IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug("%s: STATIC match %p (%u), newhop %pNHv", + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: RECURSIVE match backup %p (%u), newhop %pNHv", __func__, match, match->nhe->id, newhop); @@ -1972,13 +1991,14 @@ static int nexthop_active(afi_t afi, struct route_entry *re, nexthop_set_resolved(afi, newhop, nexthop); resolved = 1; } +done_with_match: if (resolved) re->nexthop_mtu = match->mtu; - - if (!resolved && IS_ZEBRA_DEBUG_RIB_DETAILED) + else if (IS_ZEBRA_DEBUG_RIB_DETAILED) zlog_debug( - " %s: Static route unable to resolve", + " %s: Recursion failed to find", __func__); + return resolved; } else { if (IS_ZEBRA_DEBUG_RIB_DETAILED) { @@ -2170,17 +2190,20 @@ done: } /* - * Process a list of nexthops, given the head of the list, determining + * Process a list of nexthops, given an nhg, determining * whether each one is ACTIVE/installable at this time. */ static uint32_t nexthop_list_active_update(struct route_node *rn, struct route_entry *re, - struct nexthop *nexthop) + struct nexthop_group *nhg) { union g_addr prev_src; unsigned int prev_active, new_active; ifindex_t prev_index; uint32_t counter = 0; + struct nexthop *nexthop; + + nexthop = nhg->nexthop; /* Process nexthops one-by-one */ for ( ; nexthop; nexthop = nexthop->next) { @@ -2263,7 +2286,7 @@ int nexthop_active_update(struct route_node *rn, struct route_entry *re) curr_nhe->id = 0; /* Process nexthops */ - curr_active = nexthop_list_active_update(rn, re, curr_nhe->nhg.nexthop); + curr_active = nexthop_list_active_update(rn, re, &curr_nhe->nhg); if (IS_ZEBRA_DEBUG_NHG_DETAIL) zlog_debug("%s: re %p curr_active %u", __func__, re, @@ -2274,7 +2297,7 @@ int nexthop_active_update(struct route_node *rn, struct route_entry *re) goto backups_done; backup_active = nexthop_list_active_update( - rn, re, zebra_nhg_get_backup_nhg(curr_nhe)->nexthop); + rn, re, zebra_nhg_get_backup_nhg(curr_nhe)); if (IS_ZEBRA_DEBUG_NHG_DETAIL) zlog_debug("%s: re %p backup_active %u", __func__, re, diff --git a/zebra/zebra_pw.c b/zebra/zebra_pw.c index 8f0c964c18..2328ab650a 100644 --- a/zebra/zebra_pw.c +++ b/zebra/zebra_pw.c @@ -550,7 +550,7 @@ static void vty_show_mpls_pseudowire_detail(struct vty *vty) re = rib_match(family2afi(pw->af), SAFI_UNICAST, pw->vrf_id, &pw->nexthop, NULL); if (re) { - for (ALL_NEXTHOPS_PTR(rib_active_nhg(re), nexthop)) { + 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); @@ -604,7 +604,7 @@ static void vty_show_mpls_pseudowire(struct zebra_pw *pw, json_object *json_pws) re = rib_match(family2afi(pw->af), SAFI_UNICAST, pw->vrf_id, &pw->nexthop, NULL); if (re) { - for (ALL_NEXTHOPS_PTR(rib_active_nhg(re), nexthop)) { + 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); diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 31582dcb3d..67b3812ed3 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -1342,6 +1342,92 @@ static bool rib_compare_routes(const struct route_entry *re1, } /* + * Compare nexthop lists from a route and a dplane context; test whether + * the list installed in the FIB matches the route's list. + * Set 'changed_p' to 'true' if there were changes to the route's + * installed nexthops. + * + * Return 'false' if any ACTIVE route nexthops are not mentioned in the FIB + * list. + */ +static bool rib_update_nhg_from_ctx(struct nexthop_group *re_nhg, + const struct nexthop_group *ctx_nhg, + bool *changed_p) +{ + bool matched_p = true; + struct nexthop *nexthop, *ctx_nexthop; + + /* Get the first `installed` one to check against. + * If the dataplane doesn't set these to be what was actually installed, + * it will just be whatever was in re->nhe->nhg? + */ + ctx_nexthop = ctx_nhg->nexthop; + + if (CHECK_FLAG(ctx_nexthop->flags, NEXTHOP_FLAG_RECURSIVE) + || !CHECK_FLAG(ctx_nexthop->flags, NEXTHOP_FLAG_ACTIVE)) + ctx_nexthop = nexthop_next_active_resolved(ctx_nexthop); + + for (ALL_NEXTHOPS_PTR(re_nhg, nexthop)) { + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + continue; + + if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) + continue; + + /* Check for a FIB nexthop corresponding to the RIB nexthop */ + if (nexthop_same(ctx_nexthop, nexthop) == false) { + /* If the FIB doesn't know about the nexthop, + * it's not installed + */ + if (IS_ZEBRA_DEBUG_RIB_DETAILED || + IS_ZEBRA_DEBUG_NHG_DETAIL) { + zlog_debug("%s: no ctx match for rib nh %pNHv %s", + __func__, nexthop, + (CHECK_FLAG(nexthop->flags, + NEXTHOP_FLAG_FIB) ? + "(FIB)":"")); + } + matched_p = false; + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) + *changed_p = true; + + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); + + /* Keep checking nexthops */ + continue; + } + + if (CHECK_FLAG(ctx_nexthop->flags, NEXTHOP_FLAG_FIB)) { + if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) { + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: rib nh %pNHv -> installed", + __func__, nexthop); + + *changed_p = true; + } + + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); + } else { + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) { + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: rib nh %pNHv -> uninstalled", + __func__, nexthop); + + *changed_p = true; + } + + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); + } + + ctx_nexthop = nexthop_next_active_resolved(ctx_nexthop); + } + + return matched_p; +} + +/* * Update a route from a dplane context. This consolidates common code * that can be used in processing of results from FIB updates, and in * async notification processing. @@ -1352,10 +1438,10 @@ static bool rib_update_re_from_ctx(struct route_entry *re, struct zebra_dplane_ctx *ctx) { char dest_str[PREFIX_STRLEN] = ""; - char nh_str[NEXTHOP_STRLEN]; - struct nexthop *nexthop, *ctx_nexthop; + struct nexthop *nexthop; bool matched; const struct nexthop_group *ctxnhg; + struct nexthop_group *re_nhg; bool is_selected = false; /* Is 're' currently the selected re? */ bool changed_p = false; /* Change to nexthops? */ rib_dest_t *dest; @@ -1386,10 +1472,13 @@ static bool rib_update_re_from_ctx(struct route_entry *re, matched = false; ctxnhg = dplane_ctx_get_ng(ctx); - /* Check both fib group and notif group for equivalence. + /* Check route's fib group and incoming notif group for equivalence. * * Let's assume the nexthops are ordered here to save time. */ + /* TODO -- this isn't testing or comparing the FIB flags; we should + * do a more explicit loop, checking the incoming notification's flags. + */ if (re->fib_ng.nexthop && ctxnhg->nexthop && nexthop_group_equal(&re->fib_ng, ctxnhg)) matched = true; @@ -1400,7 +1489,7 @@ static bool rib_update_re_from_ctx(struct route_entry *re, zlog_debug( "%s(%u):%s update_from_ctx(): existing fib nhg, no change", VRF_LOGNAME(vrf), re->vrf_id, dest_str); - goto done; + goto check_backups; } else if (re->fib_ng.nexthop) { /* @@ -1430,70 +1519,16 @@ static bool rib_update_re_from_ctx(struct route_entry *re, * * Assume nexthops are ordered here as well. */ - matched = true; - ctx_nexthop = ctxnhg->nexthop; - - /* Nothing installed - we can skip some of the checking/comparison + /* If nothing is installed, we can skip some of the checking/comparison * of nexthops. */ - if (ctx_nexthop == NULL) { + if (ctxnhg->nexthop == NULL) { changed_p = true; goto no_nexthops; } - /* Get the first `installed` one to check against. - * If the dataplane doesn't set these to be what was actually installed, - * it will just be whatever was in re->nhe->nhg? - */ - if (CHECK_FLAG(ctx_nexthop->flags, NEXTHOP_FLAG_RECURSIVE) - || !CHECK_FLAG(ctx_nexthop->flags, NEXTHOP_FLAG_ACTIVE)) - ctx_nexthop = nexthop_next_active_resolved(ctx_nexthop); - - for (ALL_NEXTHOPS(re->nhe->nhg, nexthop)) { - - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) - continue; - - if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) - continue; - - /* Check for a FIB nexthop corresponding to the RIB nexthop */ - if (nexthop_same(ctx_nexthop, nexthop) == false) { - /* If the FIB doesn't know about the nexthop, - * it's not installed - */ - if (IS_ZEBRA_DEBUG_RIB_DETAILED) { - nexthop2str(nexthop, nh_str, sizeof(nh_str)); - zlog_debug( - "update_from_ctx: no match for rib nh %s", - nh_str); - } - matched = false; - - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) - changed_p = true; - - UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); - - /* Keep checking nexthops */ - continue; - } - - if (CHECK_FLAG(ctx_nexthop->flags, NEXTHOP_FLAG_FIB)) { - if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) - changed_p = true; - - SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); - } else { - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) - changed_p = true; - - UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); - } - - ctx_nexthop = nexthop_next_active_resolved(ctx_nexthop); - } + matched = rib_update_nhg_from_ctx(&(re->nhe->nhg), ctxnhg, &changed_p); /* If all nexthops were processed, we're done */ if (matched) { @@ -1502,7 +1537,7 @@ static bool rib_update_re_from_ctx(struct route_entry *re, "%s(%u):%s update_from_ctx(): rib nhg matched, changed '%s'", VRF_LOGNAME(vrf), re->vrf_id, dest_str, (changed_p ? "true" : "false")); - goto done; + goto check_backups; } no_nexthops: @@ -1527,7 +1562,81 @@ no_nexthops: _nexthop_add(&(re->fib_ng.nexthop), nexthop); } +check_backups: + + /* + * Check the status of the route's backup nexthops, if any. + * The logic for backups is somewhat different: if any backup is + * installed, a new fib nhg will be attached to the route. + */ + re_nhg = zebra_nhg_get_backup_nhg(re->nhe); + if (re_nhg == NULL) + goto done; /* No backup nexthops */ + + /* First check the route's 'fib' list of backups, if it's present + * from some previous event. + */ + re_nhg = &re->fib_backup_ng; + ctxnhg = dplane_ctx_get_backup_ng(ctx); + + matched = false; + if (re_nhg->nexthop && ctxnhg && nexthop_group_equal(re_nhg, ctxnhg)) + matched = true; + + /* If the new FIB set matches an existing FIB set, we're done. */ + if (matched) { + if (IS_ZEBRA_DEBUG_RIB) + zlog_debug( + "%s(%u):%s update_from_ctx(): existing fib backup nhg, no change", + VRF_LOGNAME(vrf), re->vrf_id, dest_str); + goto done; + + } else if (re->fib_backup_ng.nexthop) { + /* + * Free stale fib backup list and move on to check + * the route's backups. + */ + if (IS_ZEBRA_DEBUG_RIB) + zlog_debug( + "%s(%u):%s update_from_ctx(): replacing fib backup nhg", + VRF_LOGNAME(vrf), re->vrf_id, dest_str); + nexthops_free(re->fib_backup_ng.nexthop); + re->fib_backup_ng.nexthop = NULL; + + /* Note that the installed nexthops have changed */ + changed_p = true; + } else { + if (IS_ZEBRA_DEBUG_RIB) + zlog_debug("%s(%u):%s update_from_ctx(): no fib backup nhg", + VRF_LOGNAME(vrf), re->vrf_id, dest_str); + } + + /* + * If a FIB backup nexthop set exists: attach a copy + * to the route if any backup is installed + */ + if (ctxnhg && ctxnhg->nexthop) { + + for (ALL_NEXTHOPS_PTR(ctxnhg, nexthop)) { + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) + break; + } + + /* If no installed backups, we're done */ + if (nexthop == NULL) + goto done; + + if (IS_ZEBRA_DEBUG_RIB) + zlog_debug("%s(%u):%s update_from_ctx(): changed %s, adding new backup fib nhg", + VRF_LOGNAME(vrf), re->vrf_id, dest_str, + (changed_p ? "true" : "false")); + + copy_nexthops(&(re->fib_backup_ng.nexthop), ctxnhg->nexthop, + NULL); + } + done: + return changed_p; } @@ -1814,6 +1923,38 @@ done: } /* + * Count installed/FIB nexthops + */ +static int rib_count_installed_nh(struct route_entry *re) +{ + int count = 0; + struct nexthop *nexthop; + struct nexthop_group *nhg; + + nhg = rib_get_fib_nhg(re); + + for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { + /* The meaningful flag depends on where the installed + * nexthops reside. + */ + if (nhg == &(re->fib_backup_ng)) { + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) + count++; + } else { + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) + count++; + } + } + + for (ALL_NEXTHOPS_PTR(rib_get_fib_backup_nhg(re), nexthop)) { + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) + count++; + } + + return count; +} + +/* * Handle notification from async dataplane: the dataplane has detected * some change to a route, and notifies zebra so that the control plane * can reflect that change. @@ -1930,12 +2071,8 @@ static void rib_process_dplane_notify(struct zebra_dplane_ctx *ctx) */ start_count = 0; - if (CHECK_FLAG(re->status, ROUTE_ENTRY_INSTALLED)) { - for (ALL_NEXTHOPS_PTR(rib_active_nhg(re), nexthop)) { - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) - start_count++; - } - } + if (CHECK_FLAG(re->status, ROUTE_ENTRY_INSTALLED)) + start_count = rib_count_installed_nh(re); /* Update zebra's nexthop FIB flags based on the context struct's * nexthops. @@ -1954,12 +2091,7 @@ static void rib_process_dplane_notify(struct zebra_dplane_ctx *ctx) * Perform follow-up work if the actual status of the prefix * changed. */ - - end_count = 0; - for (ALL_NEXTHOPS_PTR(rib_active_nhg(re), nexthop)) { - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) - end_count++; - } + end_count = rib_count_installed_nh(re); /* Various fib transitions: changed nexthops; from installed to * not-installed; or not-installed to installed. @@ -1988,7 +2120,7 @@ static void rib_process_dplane_notify(struct zebra_dplane_ctx *ctx) SET_FLAG(re->status, ROUTE_ENTRY_INSTALLED); /* Changed nexthops - update kernel/others */ - dplane_route_notif_update(rn, re, DPLANE_OP_ROUTE_INSTALL, ctx); + dplane_route_notif_update(rn, re, DPLANE_OP_ROUTE_UPDATE, ctx); /* Redistribute, lsp, and nht update */ redistribute_update(dest_pfx, src_pfx, re, NULL); diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c index 20af96a557..d1a5cf2a9d 100644 --- a/zebra/zebra_rnh.c +++ b/zebra/zebra_rnh.c @@ -419,7 +419,7 @@ static int zebra_rnh_apply_nht_rmap(afi_t afi, struct zebra_vrf *zvrf, at_least_one++; /* at least one valid NH */ else { SET_FLAG(nexthop->flags, - NEXTHOP_FLAG_RNH_FILTERED); + NEXTHOP_FLAG_RNH_FILTERED); } } } @@ -458,12 +458,12 @@ zebra_rnh_resolve_import_entry(struct zebra_vrf *zvrf, afi_t afi, if (IS_ZEBRA_DEBUG_NHT_DETAILED) { char buf[PREFIX_STRLEN]; - char buf1[PREFIX_STRLEN]; + char buf1[SRCDEST2STR_BUFFER]; zlog_debug("%s: %u:%s Resolved Import Entry to %s", __func__, rnh->vrf_id, prefix2str(&rnh->node->p, buf, sizeof(buf)), - srcdest_rnode2str(rn, buf1, sizeof(buf))); + srcdest_rnode2str(rn, buf1, sizeof(buf1))); } /* Identify appropriate route entry. */ @@ -974,12 +974,131 @@ static void copy_state(struct rnh *rnh, const struct route_entry *re, state->vrf_id = re->vrf_id; state->status = re->status; - state->nhe = zebra_nhg_alloc(); + state->nhe = zebra_nhe_copy(re->nhe, 0); + + /* Copy the 'fib' nexthops also, if present - we want to capture + * the true installed nexthops. + */ + if (re->fib_ng.nexthop) + nexthop_group_copy(&state->fib_ng, &re->fib_ng); + if (re->fib_backup_ng.nexthop) + nexthop_group_copy(&state->fib_backup_ng, &re->fib_backup_ng); - nexthop_group_copy(&(state->nhe->nhg), &(re->nhe->nhg)); rnh->state = state; } +/* + * Compare two route_entries' nexthops. + */ +static bool compare_valid_nexthops(struct route_entry *r1, + struct route_entry *r2) +{ + bool matched_p = false; + struct nexthop_group *nhg1, *nhg2; + struct nexthop *nh1, *nh2; + + /* Account for backup nexthops and for the 'fib' nexthop lists, + * if present. + */ + nhg1 = rib_get_fib_nhg(r1); + nhg2 = rib_get_fib_nhg(r2); + + nh1 = nhg1->nexthop; + nh2 = nhg2->nexthop; + + while (1) { + /* Find each list's next valid nexthop */ + while ((nh1 != NULL) && !rnh_nexthop_valid(r1, nh1)) + nh1 = nexthop_next(nh1); + + while ((nh2 != NULL) && !rnh_nexthop_valid(r2, nh2)) + nh2 = nexthop_next(nh2); + + if (nh1 && nh2) { + /* Any difference is a no-match */ + if (nexthop_cmp(nh1, nh2) != 0) { + if (IS_ZEBRA_DEBUG_NHT_DETAILED) + zlog_debug("%s: nh1, nh2 differ", + __func__); + goto done; + } + + nh1 = nexthop_next(nh1); + nh2 = nexthop_next(nh2); + } else if (nh1 || nh2) { + /* One list has more valid nexthops than the other */ + if (IS_ZEBRA_DEBUG_NHT_DETAILED) + zlog_debug("%s: nh1 %s, nh2 %s", __func__, + nh1 ? "non-NULL" : "NULL", + nh2 ? "non-NULL" : "NULL"); + goto done; + } else + break; /* Done with both lists */ + } + + /* The test for the backups is slightly different: the only installed + * 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; + + while (1) { + /* Find each backup list's next valid nexthop */ + while ((nh1 != NULL) && !rnh_nexthop_valid(r1, nh1)) + nh1 = nexthop_next(nh1); + + while ((nh2 != NULL) && !rnh_nexthop_valid(r2, nh2)) + nh2 = nexthop_next(nh2); + + if (nh1 && nh2) { + /* Any difference is a no-match */ + if (nexthop_cmp(nh1, nh2) != 0) { + if (IS_ZEBRA_DEBUG_NHT_DETAILED) + zlog_debug("%s: backup nh1, nh2 differ", + __func__); + goto done; + } + + nh1 = nexthop_next(nh1); + nh2 = nexthop_next(nh2); + } else if (nh1 || nh2) { + /* One list has more valid nexthops than the other */ + if (IS_ZEBRA_DEBUG_NHT_DETAILED) + zlog_debug("%s: backup nh1 %s, nh2 %s", + __func__, + nh1 ? "non-NULL" : "NULL", + nh2 ? "non-NULL" : "NULL"); + goto done; + } else + break; /* Done with both lists */ + } + + /* Well, it's a match */ + if (IS_ZEBRA_DEBUG_NHT_DETAILED) + zlog_debug("%s: matched", __func__); + + matched_p = true; + +done: + + return matched_p; +} + static int compare_state(struct route_entry *r1, struct route_entry *r2) { if (!r1 && !r2) @@ -994,12 +1113,7 @@ static int compare_state(struct route_entry *r1, struct route_entry *r2) if (r1->metric != r2->metric) return 1; - if (nexthop_group_nexthop_num(&(r1->nhe->nhg)) - != nexthop_group_nexthop_num(&(r2->nhe->nhg))) - return 1; - - if (nexthop_group_hash(&(r1->nhe->nhg)) != - nexthop_group_hash(&(r2->nhe->nhg))) + if (!compare_valid_nexthops(r1, r2)) return 1; return 0; @@ -1044,6 +1158,7 @@ static int send_client(struct rnh *rnh, struct zserv *client, } if (re) { struct zapi_nexthop znh; + struct nexthop_group *nhg; stream_putc(s, re->type); stream_putw(s, re->instance); @@ -1052,7 +1167,9 @@ static int send_client(struct rnh *rnh, struct zserv *client, num = 0; nump = stream_get_endp(s); stream_putc(s, 0); - for (ALL_NEXTHOPS(re->nhe->nhg, nh)) + + nhg = rib_get_fib_nhg(re); + for (ALL_NEXTHOPS_PTR(nhg, nh)) if (rnh_nexthop_valid(re, nh)) { zapi_nexthop_from_nexthop(&znh, nh); ret = zapi_nexthop_encode(s, &znh, 0/*flags*/); @@ -1061,6 +1178,21 @@ static int send_client(struct rnh *rnh, struct zserv *client, num++; } + + 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)) { + zapi_nexthop_from_nexthop(&znh, nh); + zapi_nexthop_encode(s, &znh, + 0 /* flags */); + num++; + } + } + stream_putc_at(s, nump, num); } else { stream_putc(s, 0); // type diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 9718b40d9d..1da2660509 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -516,8 +516,189 @@ static void show_route_nexthop_helper(struct vty *vty, sizeof(buf), 1)); } - if ((re == NULL) && nexthop->weight) + 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); +} + +/* + * Render a nexthop into a json object; the caller allocates and owns + * the json object memory. + */ +static void show_nexthop_json_helper(json_object *json_nexthop, + const struct nexthop *nexthop, + const struct route_entry *re) +{ + char buf[SRCDEST2STR_BUFFER]; + struct vrf *vrf = NULL; + json_object *json_labels = NULL; + + json_object_int_add(json_nexthop, "flags", + nexthop->flags); + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE)) + json_object_boolean_true_add(json_nexthop, + "duplicate"); + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) + json_object_boolean_true_add(json_nexthop, + "fib"); + + switch (nexthop->type) { + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + json_object_string_add( + json_nexthop, "ip", + inet_ntoa(nexthop->gate.ipv4)); + json_object_string_add(json_nexthop, "afi", + "ipv4"); + + if (nexthop->ifindex) { + json_object_int_add(json_nexthop, + "interfaceIndex", + nexthop->ifindex); + json_object_string_add( + json_nexthop, "interfaceName", + ifindex2ifname( + nexthop->ifindex, + nexthop->vrf_id)); + } + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + json_object_string_add( + json_nexthop, "ip", + inet_ntop(AF_INET6, &nexthop->gate.ipv6, + buf, sizeof(buf))); + json_object_string_add(json_nexthop, "afi", + "ipv6"); + + if (nexthop->ifindex) { + json_object_int_add(json_nexthop, + "interfaceIndex", + nexthop->ifindex); + json_object_string_add( + json_nexthop, "interfaceName", + ifindex2ifname( + nexthop->ifindex, + nexthop->vrf_id)); + } + break; + + case NEXTHOP_TYPE_IFINDEX: + json_object_boolean_true_add( + json_nexthop, "directlyConnected"); + json_object_int_add(json_nexthop, + "interfaceIndex", + nexthop->ifindex); + json_object_string_add( + json_nexthop, "interfaceName", + ifindex2ifname(nexthop->ifindex, + nexthop->vrf_id)); + break; + case NEXTHOP_TYPE_BLACKHOLE: + json_object_boolean_true_add(json_nexthop, + "unreachable"); + switch (nexthop->bh_type) { + case BLACKHOLE_REJECT: + json_object_boolean_true_add( + json_nexthop, "reject"); + break; + case BLACKHOLE_ADMINPROHIB: + json_object_boolean_true_add( + json_nexthop, + "admin-prohibited"); + break; + case BLACKHOLE_NULL: + json_object_boolean_true_add( + json_nexthop, "blackhole"); + break; + case BLACKHOLE_UNSPEC: + break; + } + break; + default: + break; + } + + if ((nexthop->vrf_id != re->vrf_id) + && (nexthop->type != NEXTHOP_TYPE_BLACKHOLE)) { + vrf = vrf_lookup_by_id(nexthop->vrf_id); + json_object_string_add(json_nexthop, "vrf", + vrf->name); + } + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE)) + json_object_boolean_true_add(json_nexthop, + "duplicate"); + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) + json_object_boolean_true_add(json_nexthop, + "active"); + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK)) + json_object_boolean_true_add(json_nexthop, + "onLink"); + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + 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); + + switch (nexthop->type) { + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + if (nexthop->src.ipv4.s_addr) { + if (inet_ntop(AF_INET, + &nexthop->src.ipv4, buf, + sizeof(buf))) + json_object_string_add( + json_nexthop, "source", + buf); + } + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + if (!IPV6_ADDR_SAME(&nexthop->src.ipv6, + &in6addr_any)) { + if (inet_ntop(AF_INET6, + &nexthop->src.ipv6, buf, + sizeof(buf))) + json_object_string_add( + json_nexthop, "source", + buf); + } + break; + default: + break; + } + + if (nexthop->nh_label + && nexthop->nh_label->num_labels) { + json_labels = json_object_new_array(); + + for (int label_index = 0; + label_index + < nexthop->nh_label->num_labels; + label_index++) + json_object_array_add( + json_labels, + json_object_new_int( + nexthop->nh_label->label + [label_index])); + + json_object_object_add(json_nexthop, "labels", + json_labels); + } + + if (nexthop->weight) + json_object_int_add(json_nexthop, "weight", + nexthop->weight); + } static void vty_show_ip_route(struct vty *vty, struct route_node *rn, @@ -530,12 +711,12 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn, json_object *json_nexthops = NULL; json_object *json_nexthop = NULL; json_object *json_route = NULL; - json_object *json_labels = NULL; time_t uptime; struct vrf *vrf = NULL; rib_dest_t *dest = rib_dest_from_rnode(rn); struct nexthop_group *nhg; char up_str[MONOTIME_STRLEN]; + bool first_p; uptime = monotime(NULL); uptime -= re->uptime; @@ -546,7 +727,7 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn, * nexthops. */ if (is_fib) - nhg = rib_active_nhg(re); + nhg = rib_get_fib_nhg(re); else nhg = &(re->nhe->nhg); @@ -611,177 +792,44 @@ 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(); - json_object_int_add(json_nexthop, "flags", - nexthop->flags); + show_nexthop_json_helper(json_nexthop, nexthop, re); + json_object_array_add(json_nexthops, json_nexthop); + } - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE)) - json_object_boolean_true_add(json_nexthop, - "duplicate"); + json_object_object_add(json_route, "nexthops", json_nexthops); - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) - json_object_boolean_true_add(json_nexthop, - "fib"); + /* If there are backup nexthops, include them */ + if (is_fib) + nhg = rib_get_fib_backup_nhg(re); + else + nhg = zebra_nhg_get_backup_nhg(re->nhe); - switch (nexthop->type) { - case NEXTHOP_TYPE_IPV4: - case NEXTHOP_TYPE_IPV4_IFINDEX: - json_object_string_add( - json_nexthop, "ip", - inet_ntoa(nexthop->gate.ipv4)); - json_object_string_add(json_nexthop, "afi", - "ipv4"); - - if (nexthop->ifindex) { - json_object_int_add(json_nexthop, - "interfaceIndex", - nexthop->ifindex); - json_object_string_add( - json_nexthop, "interfaceName", - ifindex2ifname( - nexthop->ifindex, - nexthop->vrf_id)); - } - break; - case NEXTHOP_TYPE_IPV6: - case NEXTHOP_TYPE_IPV6_IFINDEX: - json_object_string_add( - json_nexthop, "ip", - inet_ntop(AF_INET6, &nexthop->gate.ipv6, - buf, sizeof(buf))); - json_object_string_add(json_nexthop, "afi", - "ipv6"); - - if (nexthop->ifindex) { - json_object_int_add(json_nexthop, - "interfaceIndex", - nexthop->ifindex); - json_object_string_add( - json_nexthop, "interfaceName", - ifindex2ifname( - nexthop->ifindex, - nexthop->vrf_id)); - } - break; - - case NEXTHOP_TYPE_IFINDEX: - json_object_boolean_true_add( - json_nexthop, "directlyConnected"); - json_object_int_add(json_nexthop, - "interfaceIndex", - nexthop->ifindex); - json_object_string_add( - json_nexthop, "interfaceName", - ifindex2ifname(nexthop->ifindex, - nexthop->vrf_id)); - break; - case NEXTHOP_TYPE_BLACKHOLE: - json_object_boolean_true_add(json_nexthop, - "unreachable"); - switch (nexthop->bh_type) { - case BLACKHOLE_REJECT: - json_object_boolean_true_add( - json_nexthop, "reject"); - break; - case BLACKHOLE_ADMINPROHIB: - json_object_boolean_true_add( - json_nexthop, - "admin-prohibited"); - break; - case BLACKHOLE_NULL: - json_object_boolean_true_add( - json_nexthop, "blackhole"); - break; - case BLACKHOLE_UNSPEC: - break; - } - break; - default: - break; - } + if (nhg) { + json_nexthops = json_object_new_array(); - if ((nexthop->vrf_id != re->vrf_id) - && (nexthop->type != NEXTHOP_TYPE_BLACKHOLE)) { - vrf = vrf_lookup_by_id(nexthop->vrf_id); - json_object_string_add(json_nexthop, "vrf", - vrf->name); - } - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE)) - json_object_boolean_true_add(json_nexthop, - "duplicate"); - - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) - json_object_boolean_true_add(json_nexthop, - "active"); - - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK)) - json_object_boolean_true_add(json_nexthop, - "onLink"); - - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) - json_object_boolean_true_add(json_nexthop, - "recursive"); - - switch (nexthop->type) { - case NEXTHOP_TYPE_IPV4: - case NEXTHOP_TYPE_IPV4_IFINDEX: - if (nexthop->src.ipv4.s_addr) { - if (inet_ntop(AF_INET, - &nexthop->src.ipv4, buf, - sizeof(buf))) - json_object_string_add( - json_nexthop, "source", - buf); - } - break; - case NEXTHOP_TYPE_IPV6: - case NEXTHOP_TYPE_IPV6_IFINDEX: - if (!IPV6_ADDR_SAME(&nexthop->src.ipv6, - &in6addr_any)) { - if (inet_ntop(AF_INET6, - &nexthop->src.ipv6, buf, - sizeof(buf))) - json_object_string_add( - json_nexthop, "source", - buf); - } - break; - default: - break; - } + for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { + json_nexthop = json_object_new_object(); - if (nexthop->nh_label - && nexthop->nh_label->num_labels) { - json_labels = json_object_new_array(); - - for (int label_index = 0; - label_index - < nexthop->nh_label->num_labels; - label_index++) - json_object_array_add( - json_labels, - json_object_new_int( - nexthop->nh_label->label - [label_index])); - - json_object_object_add(json_nexthop, "labels", - json_labels); + show_nexthop_json_helper(json_nexthop, + nexthop, re); + json_object_array_add(json_nexthops, + json_nexthop); } - if (nexthop->weight) - json_object_int_add(json_nexthop, "weight", - nexthop->weight); - - json_object_array_add(json_nexthops, json_nexthop); + json_object_object_add(json_route, "backupNexthops", + json_nexthops); } - json_object_object_add(json_route, "nexthops", json_nexthops); json_object_array_add(json, json_route); return; } /* Nexthop information. */ + first_p = true; for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { - if (nexthop == nhg->nexthop) { + if (first_p) { + first_p = false; + /* Prefix information. */ len = vty_out(vty, "%c", zebra_route_char(re->type)); if (re->instance) @@ -808,40 +856,36 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn, show_route_nexthop_helper(vty, re, nexthop); - if (nexthop->weight) - vty_out(vty, ", weight %u", nexthop->weight); - vty_out(vty, ", %s\n", up_str); + } - /* Check for backup info */ - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) { - struct nexthop *backup; - int i; + /* Check for backup info if present */ + if (is_fib) + nhg = rib_get_fib_backup_nhg(re); + else + nhg = zebra_nhg_get_backup_nhg(re->nhe); - if (re->nhe->backup_info == NULL || - re->nhe->backup_info->nhe == NULL) - continue; + if (nhg == NULL) + return; - i = 0; - for (ALL_NEXTHOPS(re->nhe->backup_info->nhe->nhg, - backup)) { - if (i == nexthop->backup_idx) - break; - i++; - } + /* Print backup info */ + for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { + bool star_p = false; - /* Print useful backup info */ - if (backup) { - /* TODO -- install state is not accurate */ - vty_out(vty, " %*c [backup %d]", - /*re_status_output_char(re, backup),*/ - len - 3 + (2 * nexthop_level(nexthop)), - ' ', nexthop->backup_idx); - show_route_nexthop_helper(vty, re, backup); - vty_out(vty, "\n"); - } - } + if (is_fib) + star_p = CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); + + /* TODO -- it'd be nice to be able to include + * the entire list of backups, *and* include the + * real installation state. + */ + vty_out(vty, " b%c %*c", + (star_p ? '*' : ' '), + len - 3 + (2 * nexthop_level(nexthop)), ' '); + show_route_nexthop_helper(vty, re, nexthop); + vty_out(vty, "\n"); } + } static void vty_show_ip_route_detail_json(struct vty *vty, |
