diff options
331 files changed, 10760 insertions, 2844 deletions
diff --git a/.dir-locals.el b/.dir-locals.el index e47f245db7..1332f7b6a2 100644 --- a/.dir-locals.el +++ b/.dir-locals.el @@ -4,5 +4,7 @@ ((c-mode . ((indent-tabs-mode . t) (show-trailing-whitespace . t) - (c-basic-offset . 8) - ))) + (c-basic-offset . 8))) + (json-mode . ((js-indent-level 4))) + (python-mode . ((python-formatter . black) + (python-fill-column . 88)))) diff --git a/babeld/babel_interface.c b/babeld/babel_interface.c index c1e5ffde3c..615ed9fee3 100644 --- a/babeld/babel_interface.c +++ b/babeld/babel_interface.c @@ -1362,7 +1362,7 @@ interface_config_write (struct vty *vty) write++; } } - vty_endframe (vty, "!\n"); + vty_endframe (vty, "exit\n!\n"); write++; } return write; diff --git a/babeld/babel_main.c b/babeld/babel_main.c index df1998c4fc..61a800eef4 100644 --- a/babeld/babel_main.c +++ b/babeld/babel_main.c @@ -183,8 +183,7 @@ main(int argc, char **argv) case 0: break; default: - frr_help_exit (1); - break; + frr_help_exit(1); } } diff --git a/babeld/babeld.c b/babeld/babeld.c index b9623b64b5..f61eac000f 100644 --- a/babeld/babeld.c +++ b/babeld/babeld.c @@ -132,6 +132,8 @@ babel_config_write (struct vty *vty) lines += config_write_distribute (vty, babel_routing_process->distribute_ctx); + vty_out (vty, "exit\n"); + return lines; } @@ -819,7 +821,7 @@ babeld_quagga_init(void) install_element(BABEL_NODE, &babel_ipv6_distribute_list_cmd); install_element(BABEL_NODE, &babel_no_ipv6_distribute_list_cmd); - vrf_cmd_init(NULL, &babeld_privs); + vrf_cmd_init(NULL); babel_if_init(); diff --git a/bfdd/bfdd.c b/bfdd/bfdd.c index 7a2c3cc3aa..188e47905c 100644 --- a/bfdd/bfdd.c +++ b/bfdd/bfdd.c @@ -364,7 +364,6 @@ int main(int argc, char *argv[]) default: frr_help_exit(1); - break; } } diff --git a/bfdd/bfdd_cli.c b/bfdd/bfdd_cli.c index 26ff4a758a..384bb26fd7 100644 --- a/bfdd/bfdd_cli.c +++ b/bfdd/bfdd_cli.c @@ -101,6 +101,7 @@ void bfd_cli_show_header(struct vty *vty, void bfd_cli_show_header_end(struct vty *vty, struct lyd_node *dnode __attribute__((__unused__))) { + vty_out(vty, "exit\n"); vty_out(vty, "!\n"); } @@ -275,6 +276,7 @@ void bfd_cli_show_multi_hop_peer(struct vty *vty, void bfd_cli_show_peer_end(struct vty *vty, struct lyd_node *dnode __attribute__((__unused__))) { + vty_out(vty, " exit\n"); vty_out(vty, " !\n"); } diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index 0870748f7e..fb97fea72d 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -789,9 +789,9 @@ static void attr_show_all_iterator(struct hash_bucket *bucket, struct vty *vty) inet_ntop(AF_INET6, &attr->srv6_vpn->sid, sid_str, BUFSIZ); vty_out(vty, - "\tflags: %" PRIu64" med: %u local_pref: %u origin: %u weight: %u label: %u sid: %s\n", - attr->flag, attr->med, attr->local_pref, attr->origin, - attr->weight, attr->label, sid_str); + "\tflags: %" PRIu64" distance: %u med: %u local_pref: %u origin: %u weight: %u label: %u sid: %s\n", + attr->flag, attr->distance, attr->med, attr->local_pref, + attr->origin, attr->weight, attr->label, sid_str); } void attr_show_all(struct vty *vty) diff --git a/bgpd/bgp_bmp.c b/bgpd/bgp_bmp.c index dbc35de80b..1bc3fd0dba 100644 --- a/bgpd/bgp_bmp.c +++ b/bgpd/bgp_bmp.c @@ -2400,6 +2400,8 @@ static int bmp_config_write(struct bgp *bgp, struct vty *vty) frr_each (bmp_actives, &bt->actives, ba) vty_out(vty, " bmp connect %s port %u min-retry %u max-retry %u\n", ba->hostname, ba->port, ba->minretry, ba->maxretry); + + vty_out(vty, " exit\n"); } return 0; diff --git a/bgpd/bgp_clist.c b/bgpd/bgp_clist.c index 50122ad7da..33e3db2c16 100644 --- a/bgpd/bgp_clist.c +++ b/bgpd/bgp_clist.c @@ -33,6 +33,7 @@ #include "bgpd/bgp_community.h" #include "bgpd/bgp_ecommunity.h" #include "bgpd/bgp_lcommunity.h" +#include "bgpd/bgp_community_alias.h" #include "bgpd/bgp_aspath.h" #include "bgpd/bgp_regex.h" #include "bgpd/bgp_clist.h" @@ -548,6 +549,8 @@ static bool community_regexp_include(regex_t *reg, struct community *com, int i) static bool community_regexp_match(struct community *com, regex_t *reg) { const char *str; + char *regstr; + int rv; /* When there is no communities attribute it is treated as empty string. */ @@ -556,12 +559,14 @@ static bool community_regexp_match(struct community *com, regex_t *reg) else str = community_str(com, false); + regstr = bgp_alias2community_str(str); + /* Regular expression match. */ - if (regexec(reg, str, 0, NULL, 0) == 0) - return true; + rv = regexec(reg, regstr, 0, NULL, 0); - /* No match. */ - return false; + XFREE(MTYPE_TMP, regstr); + + return rv == 0; } static char *lcommunity_str_get(struct lcommunity *lcom, int i) @@ -618,6 +623,8 @@ static bool lcommunity_regexp_include(regex_t *reg, struct lcommunity *lcom, static bool lcommunity_regexp_match(struct lcommunity *com, regex_t *reg) { const char *str; + char *regstr; + int rv; /* When there is no communities attribute it is treated as empty string. */ @@ -626,12 +633,14 @@ static bool lcommunity_regexp_match(struct lcommunity *com, regex_t *reg) else str = lcommunity_str(com, false); + regstr = bgp_alias2community_str(str); + /* Regular expression match. */ - if (regexec(reg, str, 0, NULL, 0) == 0) - return true; + rv = regexec(reg, regstr, 0, NULL, 0); - /* No match. */ - return false; + XFREE(MTYPE_TMP, regstr); + + return rv == 0; } @@ -1372,3 +1381,67 @@ void community_list_terminate(struct community_list_handler *ch) XFREE(MTYPE_COMMUNITY_LIST_HANDLER, ch); } + +static int bgp_community_list_vector_walker(struct hash_bucket *bucket, + void *data) +{ + vector *comps = data; + struct community_list *list = bucket->data; + + vector_set(*comps, XSTRDUP(MTYPE_COMPLETION, list->name)); + + return 1; +} + +static void bgp_community_list_cmd_completion(vector comps, + struct cmd_token *token) +{ + struct community_list_master *cm; + + cm = community_list_master_lookup(bgp_clist, COMMUNITY_LIST_MASTER); + + hash_walk(cm->hash, bgp_community_list_vector_walker, &comps); +} + +static void bgp_lcommunity_list_cmd_completion(vector comps, + struct cmd_token *token) +{ + struct community_list_master *cm; + + cm = community_list_master_lookup(bgp_clist, + LARGE_COMMUNITY_LIST_MASTER); + + hash_walk(cm->hash, bgp_community_list_vector_walker, &comps); +} + +static void bgp_extcommunity_list_cmd_completion(vector comps, + struct cmd_token *token) +{ + struct community_list_master *cm; + + cm = community_list_master_lookup(bgp_clist, EXTCOMMUNITY_LIST_MASTER); + + hash_walk(cm->hash, bgp_community_list_vector_walker, &comps); +} + +static const struct cmd_variable_handler community_list_handlers[] = { + {.tokenname = "COMMUNITY_LIST_NAME", + .completions = bgp_community_list_cmd_completion}, + {.completions = NULL}}; + +static const struct cmd_variable_handler lcommunity_list_handlers[] = { + {.tokenname = "LCOMMUNITY_LIST_NAME", + .completions = bgp_lcommunity_list_cmd_completion}, + {.completions = NULL}}; + +static const struct cmd_variable_handler extcommunity_list_handlers[] = { + {.tokenname = "EXTCOMMUNITY_LIST_NAME", + .completions = bgp_extcommunity_list_cmd_completion}, + {.completions = NULL}}; + +void bgp_community_list_command_completion_setup(void) +{ + cmd_variable_handler_register(community_list_handlers); + cmd_variable_handler_register(lcommunity_list_handlers); + cmd_variable_handler_register(extcommunity_list_handlers); +} diff --git a/bgpd/bgp_clist.h b/bgpd/bgp_clist.h index 632e1730cc..bb7c1363b2 100644 --- a/bgpd/bgp_clist.h +++ b/bgpd/bgp_clist.h @@ -184,4 +184,6 @@ static inline uint32_t bgp_clist_hash_key(char *name) return jhash(name, strlen(name), 0xdeadbeaf); } +extern void bgp_community_list_command_completion_setup(void); + #endif /* _QUAGGA_BGP_CLIST_H */ diff --git a/bgpd/bgp_community_alias.c b/bgpd/bgp_community_alias.c index f770ebdd5d..793f3ac9ac 100644 --- a/bgpd/bgp_community_alias.c +++ b/bgpd/bgp_community_alias.c @@ -20,6 +20,7 @@ #include "memory.h" #include "lib/jhash.h" +#include "frrstr.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_community_alias.h" @@ -153,6 +154,48 @@ const char *bgp_community2alias(char *community) return community; } +const char *bgp_alias2community(char *alias) +{ + struct community_alias ca; + struct community_alias *find; + + memset(&ca, 0, sizeof(ca)); + strlcpy(ca.alias, alias, sizeof(ca.alias)); + + find = bgp_ca_alias_lookup(&ca); + if (find) + return find->community; + + return alias; +} + +/* Communities structs have `->str` which is used + * for vty outputs and extended BGP community lists + * with regexp. + * This is a helper to convert already aliased version + * of communities into numerical-only format. + */ +char *bgp_alias2community_str(const char *str) +{ + char **aliases; + char *comstr; + int num, i; + + frrstr_split(str, " ", &aliases, &num); + const char *communities[num]; + + for (i = 0; i < num; i++) + communities[i] = bgp_alias2community(aliases[i]); + + comstr = frrstr_join(communities, num, " "); + + for (i = 0; i < num; i++) + XFREE(MTYPE_TMP, aliases[i]); + XFREE(MTYPE_TMP, aliases); + + return comstr; +} + static int bgp_community_alias_vector_walker(struct hash_bucket *bucket, void *data) { diff --git a/bgpd/bgp_community_alias.h b/bgpd/bgp_community_alias.h index ab8ed06ee6..57cde0ad44 100644 --- a/bgpd/bgp_community_alias.h +++ b/bgpd/bgp_community_alias.h @@ -42,6 +42,8 @@ extern void bgp_ca_community_delete(struct community_alias *ca); extern void bgp_ca_alias_delete(struct community_alias *ca); extern int bgp_community_alias_write(struct vty *vty); extern const char *bgp_community2alias(char *community); +extern const char *bgp_alias2community(char *alias); +extern char *bgp_alias2community_str(const char *str); extern void bgp_community_alias_command_completion_setup(void); #endif /* FRR_BGP_COMMUNITY_ALIAS_H */ diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index 88581736a3..cbd29c146a 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -2385,6 +2385,7 @@ bgp_create_evpn_bgp_path_info(struct bgp_path_info *parent_pi, memcpy(&pi->extra->label, &parent_pi->extra->label, sizeof(pi->extra->label)); pi->extra->num_labels = parent_pi->extra->num_labels; + pi->extra->igpmetric = parent_pi->extra->igpmetric; } bgp_path_info_add(dest, pi); diff --git a/bgpd/bgp_io.c b/bgpd/bgp_io.c index e9b0f9e46a..9b5a31f289 100644 --- a/bgpd/bgp_io.c +++ b/bgpd/bgp_io.c @@ -111,6 +111,7 @@ void bgp_reads_off(struct peer *peer) thread_cancel_async(fpt->master, &peer->t_read, NULL); THREAD_OFF(peer->t_process_packet); + THREAD_OFF(peer->t_process_packet_error); UNSET_FLAG(peer->thread_flags, PEER_THREAD_READS_ON); } @@ -208,7 +209,7 @@ static int bgp_process_reads(struct thread *thread) * specific state change from 'bgp_read'. */ thread_add_event(bm->master, bgp_packet_process_error, - peer, code, NULL); + peer, code, &peer->t_process_packet_error); } while (more) { diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c index 38cc781338..08776d200f 100644 --- a/bgpd/bgp_main.c +++ b/bgpd/bgp_main.c @@ -490,7 +490,6 @@ int main(int argc, char **argv) break; default: frr_help_exit(1); - break; } } if (skip_runas) diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index cab58b45b8..1af2ab384f 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -595,7 +595,7 @@ void ensure_vrf_tovpn_sid(struct bgp *bgp_vpn, struct bgp *bgp_vrf, afi_t afi) return; /* check invalid case both configured index and auto */ - if (tovpn_sid_index != 0 && tovpn_sid_index) { + if (tovpn_sid_index != 0 && tovpn_sid_auto) { zlog_err("%s: index-mode and auto-mode both selected. ignored.", __func__); return; diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index 3c01c3b486..783115baaf 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -1353,6 +1353,16 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size) return BGP_Stop; } + /* Send notification message when Hold Time received in the OPEN message + * is smaller than configured minimum Hold Time. */ + if (holdtime < peer->bgp->default_min_holdtime + && peer->bgp->default_min_holdtime != 0) { + bgp_notify_send_with_data(peer, BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_UNACEP_HOLDTIME, + (uint8_t *)holdtime_ptr, 2); + return BGP_Stop; + } + /* From the rfc: A reasonable maximum time between KEEPALIVE messages would be one third of the Hold Time interval. KEEPALIVE messages MUST NOT be sent more frequently than one per second. An diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 716ba26305..f97a791dae 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -8063,9 +8063,9 @@ DEFPY(aggregate_addressv6, aggregate_addressv6_cmd, /* Redistribute route treatment. */ void bgp_redistribute_add(struct bgp *bgp, struct prefix *p, const union g_addr *nexthop, ifindex_t ifindex, - enum nexthop_types_t nhtype, uint32_t metric, - uint8_t type, unsigned short instance, - route_tag_t tag) + enum nexthop_types_t nhtype, uint8_t distance, + uint32_t metric, uint8_t type, + unsigned short instance, route_tag_t tag) { struct bgp_path_info *new; struct bgp_path_info *bpi; @@ -8112,6 +8112,7 @@ void bgp_redistribute_add(struct bgp *bgp, struct prefix *p, attr.nh_ifindex = ifindex; attr.med = metric; + attr.distance = distance; attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC); attr.tag = tag; @@ -10727,12 +10728,14 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, const char *com2alias = bgp_community2alias( communities[i]); - if (strcmp(alias, com2alias) - == 0) { + if (!found + && strcmp(alias, com2alias) + == 0) found = true; - break; - } + XFREE(MTYPE_TMP, + communities[i]); } + XFREE(MTYPE_TMP, communities); } if (!found && pi->attr->lcommunity) { @@ -10742,12 +10745,14 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, const char *com2alias = bgp_community2alias( communities[i]); - if (strcmp(alias, com2alias) - == 0) { + if (!found + && strcmp(alias, com2alias) + == 0) found = true; - break; - } + XFREE(MTYPE_TMP, + communities[i]); } + XFREE(MTYPE_TMP, communities); } if (!found) @@ -11714,7 +11719,7 @@ static int bgp_show_lcommunity_list(struct vty *vty, struct bgp *bgp, DEFUN (show_ip_bgp_large_community_list, show_ip_bgp_large_community_list_cmd, - "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] large-community-list <(1-500)|WORD> [exact-match] [json]", + "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] large-community-list <(1-500)|LCOMMUNITY_LIST_NAME> [exact-match] [json]", SHOW_STR IP_STR BGP_STR @@ -11947,7 +11952,7 @@ DEFPY(show_ip_bgp, show_ip_bgp_cmd, |route-map WORD\ |prefix-list WORD\ |filter-list WORD\ - |community-list <(1-500)|WORD> [exact-match]\ + |community-list <(1-500)|COMMUNITY_LIST_NAME> [exact-match]\ |A.B.C.D/M longer-prefixes\ |X:X::X:X/M longer-prefixes\ >", diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index 72fcfe2466..75da2723e6 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -641,9 +641,9 @@ extern bool bgp_maximum_prefix_overflow(struct peer *, afi_t, safi_t, int); extern void bgp_redistribute_add(struct bgp *bgp, struct prefix *p, const union g_addr *nexthop, ifindex_t ifindex, - enum nexthop_types_t nhtype, uint32_t metric, - uint8_t type, unsigned short instance, - route_tag_t tag); + enum nexthop_types_t nhtype, uint8_t distance, + uint32_t metric, uint8_t type, + unsigned short instance, route_tag_t tag); extern void bgp_redistribute_delete(struct bgp *, struct prefix *, uint8_t, unsigned short); extern void bgp_redistribute_withdraw(struct bgp *, afi_t, int, unsigned short); diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index 09dd71c020..5e566f360a 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -1189,27 +1189,38 @@ route_match_alias(void *rule, const struct prefix *prefix, void *object) struct bgp_path_info *path = object; char **communities; int num; + bool found; if (path->attr->community) { + found = false; frrstr_split(path->attr->community->str, " ", &communities, &num); for (int i = 0; i < num; i++) { const char *com2alias = bgp_community2alias(communities[i]); - if (strcmp(alias, com2alias) == 0) - return RMAP_MATCH; + if (!found && strcmp(alias, com2alias) == 0) + found = true; + XFREE(MTYPE_TMP, communities[i]); } + XFREE(MTYPE_TMP, communities); + if (found) + return RMAP_MATCH; } if (path->attr->lcommunity) { + found = false; frrstr_split(path->attr->lcommunity->str, " ", &communities, &num); for (int i = 0; i < num; i++) { const char *com2alias = bgp_community2alias(communities[i]); - if (strcmp(alias, com2alias) == 0) - return RMAP_MATCH; + if (!found && strcmp(alias, com2alias) == 0) + found = true; + XFREE(MTYPE_TMP, communities[i]); } + XFREE(MTYPE_TMP, communities); + if (found) + return RMAP_MATCH; } return RMAP_NOMATCH; @@ -2038,9 +2049,8 @@ route_set_aspath_prepend(void *rule, const struct prefix *prefix, void *object) aspath_prepend(aspath, new); } else { as_t as = aspath_leftmost(new); - if (!as) - as = path->peer->as; - new = aspath_add_seq_n(new, as, (uintptr_t)rule); + if (as) + new = aspath_add_seq_n(new, as, (uintptr_t)rule); } path->attr->aspath = new; @@ -5208,7 +5218,7 @@ DEFUN_YANG (set_aspath_prepend_lastas, SET_STR "Transform BGP AS_PATH attribute\n" "Prepend to the as-path\n" - "Use the peer's AS-number\n" + "Use the last AS-number in the as-path\n" "Number of times to insert\n") { int idx_num = 4; @@ -5751,7 +5761,7 @@ DEFUN_YANG (set_ecommunity_lb, "Attribute is set as non-transitive\n") { int idx_lb = 3; - int idx_non_transitive = 4; + int idx_non_transitive = 0; const char *xpath = "./set-action[action='frr-bgp-route-map:set-extcommunity-lb']"; char xpath_lb_type[XPATH_MAXLEN]; @@ -5783,7 +5793,7 @@ DEFUN_YANG (set_ecommunity_lb, argv[idx_lb]->arg); } - if (argv[idx_non_transitive]) + if (argv_find(argv, argc, "non-transitive", &idx_non_transitive)) nb_cli_enqueue_change(vty, xpath_non_transitive, NB_OP_MODIFY, "true"); else diff --git a/bgpd/bgp_routemap_nb_config.c b/bgpd/bgp_routemap_nb_config.c index 45f5c8f4bc..d7ba986a2f 100644 --- a/bgpd/bgp_routemap_nb_config.c +++ b/bgpd/bgp_routemap_nb_config.c @@ -2592,12 +2592,6 @@ lib_route_map_entry_set_action_rmap_set_action_comm_list_name_destroy( return NB_OK; } -enum e_community_lb_type { - EXPLICIT_BANDWIDTH, - CUMULATIVE_BANDWIDTH, - COMPUTED_BANDWIDTH -}; - /* * XPath: * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-lb @@ -2607,7 +2601,7 @@ lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_finish( struct nb_cb_apply_finish_args *args) { struct routemap_hook_context *rhc; - int lb_type; + enum ecommunity_lb_type lb_type; char str[VTY_BUFSIZ]; uint16_t bandwidth; diff --git a/bgpd/bgp_rpki.c b/bgpd/bgp_rpki.c index a8bccecacf..ca3f93899b 100644 --- a/bgpd/bgp_rpki.c +++ b/bgpd/bgp_rpki.c @@ -388,33 +388,25 @@ static int bgpd_sync_callback(struct thread *thread) afi_t afi = (rec.prefix.ver == LRTR_IPV4) ? AFI_IP : AFI_IP6; for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp)) { - struct peer *peer; - struct listnode *peer_listnode; - - for (ALL_LIST_ELEMENTS_RO(bgp->peer, peer_listnode, peer)) { - safi_t safi; - - for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) { - if (!peer->bgp->rib[afi][safi]) - continue; + safi_t safi; - struct bgp_dest *match; - struct bgp_dest *node; + for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) { + if (!bgp->rib[afi][safi]) + continue; - match = bgp_table_subtree_lookup( - peer->bgp->rib[afi][safi], prefix); - node = match; + struct bgp_dest *match; + struct bgp_dest *node; - while (node) { - if (bgp_dest_has_bgp_path_info_data( - node)) { - revalidate_bgp_node(node, afi, - safi); - } + match = bgp_table_subtree_lookup(bgp->rib[afi][safi], + prefix); + node = match; - node = bgp_route_next_until(node, - match); + while (node) { + if (bgp_dest_has_bgp_path_info_data(node)) { + revalidate_bgp_node(node, afi, safi); } + + node = bgp_route_next_until(node, match); } } } @@ -429,7 +421,6 @@ static void revalidate_bgp_node(struct bgp_dest *bgp_dest, afi_t afi, struct bgp_adj_in *ain; for (ain = bgp_dest->adj_in; ain; ain = ain->next) { - int ret; struct bgp_path_info *path = bgp_dest_get_bgp_path_info(bgp_dest); mpls_label_t *label = NULL; @@ -439,13 +430,10 @@ static void revalidate_bgp_node(struct bgp_dest *bgp_dest, afi_t afi, label = path->extra->label; num_labels = path->extra->num_labels; } - ret = bgp_update(ain->peer, bgp_dest_get_prefix(bgp_dest), + (void)bgp_update(ain->peer, bgp_dest_get_prefix(bgp_dest), ain->addpath_rx_id, ain->attr, afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, NULL, label, num_labels, 1, NULL); - - if (ret < 0) - return; } } @@ -909,6 +897,12 @@ static int config_write(struct vty *vty) vty_out(vty, "!\n"); vty_out(vty, "rpki\n"); vty_out(vty, " rpki polling_period %d\n", polling_period); + + if (retry_interval != RETRY_INTERVAL_DEFAULT) + vty_out(vty, " rpki retry_interval %d\n", retry_interval); + if (expire_interval != EXPIRE_INTERVAL_DEFAULT) + vty_out(vty, " rpki expire_interval %d\n", expire_interval); + for (ALL_LIST_ELEMENTS_RO(cache_list, cache_node, cache)) { switch (cache->type) { struct tr_tcp_config *tcp_config; @@ -938,7 +932,7 @@ static int config_write(struct vty *vty) vty_out(vty, "preference %hhu\n", cache->preference); } - vty_out(vty, " exit\n"); + vty_out(vty, "exit\n"); return 1; } diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index ebd6e92d9b..9a1453b937 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -1662,7 +1662,7 @@ DEFPY (no_bgp_send_extra_data, DEFUN (bgp_confederation_identifier, bgp_confederation_identifier_cmd, "bgp confederation identifier (1-4294967295)", - "BGP specific commands\n" + BGP_STR "AS confederation parameters\n" "AS number\n" "Set routing domain confederation AS\n") @@ -1682,7 +1682,7 @@ DEFUN (no_bgp_confederation_identifier, no_bgp_confederation_identifier_cmd, "no bgp confederation identifier [(1-4294967295)]", NO_STR - "BGP specific commands\n" + BGP_STR "AS confederation parameters\n" "AS number\n" "Set routing domain confederation AS\n") @@ -1696,7 +1696,7 @@ DEFUN (no_bgp_confederation_identifier, DEFUN (bgp_confederation_peers, bgp_confederation_peers_cmd, "bgp confederation peers (1-4294967295)...", - "BGP specific commands\n" + BGP_STR "AS confederation parameters\n" "Peer ASs in BGP confederation\n" AS_STR) @@ -1724,7 +1724,7 @@ DEFUN (no_bgp_confederation_peers, no_bgp_confederation_peers_cmd, "no bgp confederation peers (1-4294967295)...", NO_STR - "BGP specific commands\n" + BGP_STR "AS confederation parameters\n" "Peer ASs in BGP confederation\n" AS_STR) @@ -2332,11 +2332,43 @@ DEFUN (no_bgp_timers, return CMD_SUCCESS; } +/* BGP minimum holdtime. */ + +DEFUN(bgp_minimum_holdtime, bgp_minimum_holdtime_cmd, + "bgp minimum-holdtime (1-65535)", + "BGP specific commands\n" + "BGP minimum holdtime\n" + "Seconds\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + int idx_number = 2; + unsigned long min_holdtime; + + min_holdtime = strtoul(argv[idx_number]->arg, NULL, 10); + + bgp->default_min_holdtime = min_holdtime; + + return CMD_SUCCESS; +} + +DEFUN(no_bgp_minimum_holdtime, no_bgp_minimum_holdtime_cmd, + "no bgp minimum-holdtime [(1-65535)]", + NO_STR + "BGP specific commands\n" + "BGP minimum holdtime\n" + "Seconds\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + + bgp->default_min_holdtime = 0; + + return CMD_SUCCESS; +} DEFUN (bgp_client_to_client_reflection, bgp_client_to_client_reflection_cmd, "bgp client-to-client reflection", - "BGP specific commands\n" + BGP_STR "Configure client to client route reflection\n" "reflection of routes allowed\n") { @@ -2351,7 +2383,7 @@ DEFUN (no_bgp_client_to_client_reflection, no_bgp_client_to_client_reflection_cmd, "no bgp client-to-client reflection", NO_STR - "BGP specific commands\n" + BGP_STR "Configure client to client route reflection\n" "reflection of routes allowed\n") { @@ -2366,7 +2398,7 @@ DEFUN (no_bgp_client_to_client_reflection, DEFUN (bgp_always_compare_med, bgp_always_compare_med_cmd, "bgp always-compare-med", - "BGP specific commands\n" + BGP_STR "Allow comparing MED from different neighbors\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); @@ -2380,7 +2412,7 @@ DEFUN (no_bgp_always_compare_med, no_bgp_always_compare_med_cmd, "no bgp always-compare-med", NO_STR - "BGP specific commands\n" + BGP_STR "Allow comparing MED from different neighbors\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); @@ -2393,7 +2425,7 @@ DEFUN (no_bgp_always_compare_med, DEFUN(bgp_ebgp_requires_policy, bgp_ebgp_requires_policy_cmd, "bgp ebgp-requires-policy", - "BGP specific commands\n" + BGP_STR "Require in and out policy for eBGP peers (RFC8212)\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); @@ -2404,7 +2436,7 @@ DEFUN(bgp_ebgp_requires_policy, bgp_ebgp_requires_policy_cmd, DEFUN(no_bgp_ebgp_requires_policy, no_bgp_ebgp_requires_policy_cmd, "no bgp ebgp-requires-policy", NO_STR - "BGP specific commands\n" + BGP_STR "Require in and out policy for eBGP peers (RFC8212)\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); @@ -2414,7 +2446,7 @@ DEFUN(no_bgp_ebgp_requires_policy, no_bgp_ebgp_requires_policy_cmd, DEFUN(bgp_suppress_duplicates, bgp_suppress_duplicates_cmd, "bgp suppress-duplicates", - "BGP specific commands\n" + BGP_STR "Suppress duplicate updates if the route actually not changed\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); @@ -2425,7 +2457,7 @@ DEFUN(bgp_suppress_duplicates, bgp_suppress_duplicates_cmd, DEFUN(no_bgp_suppress_duplicates, no_bgp_suppress_duplicates_cmd, "no bgp suppress-duplicates", NO_STR - "BGP specific commands\n" + BGP_STR "Suppress duplicate updates if the route actually not changed\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); @@ -2435,7 +2467,7 @@ DEFUN(no_bgp_suppress_duplicates, no_bgp_suppress_duplicates_cmd, DEFUN(bgp_reject_as_sets, bgp_reject_as_sets_cmd, "bgp reject-as-sets", - "BGP specific commands\n" + BGP_STR "Reject routes with AS_SET or AS_CONFED_SET flag\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); @@ -2461,7 +2493,7 @@ DEFUN(bgp_reject_as_sets, bgp_reject_as_sets_cmd, DEFUN(no_bgp_reject_as_sets, no_bgp_reject_as_sets_cmd, "no bgp reject-as-sets", NO_STR - "BGP specific commands\n" + BGP_STR "Reject routes with AS_SET or AS_CONFED_SET flag\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); @@ -2488,7 +2520,7 @@ DEFUN(no_bgp_reject_as_sets, no_bgp_reject_as_sets_cmd, DEFUN (bgp_deterministic_med, bgp_deterministic_med_cmd, "bgp deterministic-med", - "BGP specific commands\n" + BGP_STR "Pick the best-MED path among paths advertised from the neighboring AS\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); @@ -2505,7 +2537,7 @@ DEFUN (no_bgp_deterministic_med, no_bgp_deterministic_med_cmd, "no bgp deterministic-med", NO_STR - "BGP specific commands\n" + BGP_STR "Pick the best-MED path among paths advertised from the neighboring AS\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); @@ -2547,7 +2579,7 @@ DEFUN (no_bgp_deterministic_med, DEFUN (bgp_graceful_restart, bgp_graceful_restart_cmd, "bgp graceful-restart", - "BGP specific commands\n" + BGP_STR GR_CMD ) { @@ -2574,7 +2606,7 @@ DEFUN (no_bgp_graceful_restart, no_bgp_graceful_restart_cmd, "no bgp graceful-restart", NO_STR - "BGP specific commands\n" + BGP_STR NO_GR_CMD ) { @@ -2601,7 +2633,7 @@ DEFUN (no_bgp_graceful_restart, DEFUN (bgp_graceful_restart_stalepath_time, bgp_graceful_restart_stalepath_time_cmd, "bgp graceful-restart stalepath-time (1-4095)", - "BGP specific commands\n" + BGP_STR "Graceful restart capability parameters\n" "Set the max time to hold onto restarting peer's stale paths\n" "Delay value (seconds)\n") @@ -2618,7 +2650,7 @@ DEFUN (bgp_graceful_restart_stalepath_time, DEFUN (bgp_graceful_restart_restart_time, bgp_graceful_restart_restart_time_cmd, "bgp graceful-restart restart-time (1-4095)", - "BGP specific commands\n" + BGP_STR "Graceful restart capability parameters\n" "Set the time to wait to delete stale routes before a BGP open message is received\n" "Delay value (seconds)\n") @@ -2635,7 +2667,7 @@ DEFUN (bgp_graceful_restart_restart_time, DEFUN (bgp_graceful_restart_select_defer_time, bgp_graceful_restart_select_defer_time_cmd, "bgp graceful-restart select-defer-time (0-3600)", - "BGP specific commands\n" + BGP_STR "Graceful restart capability parameters\n" "Set the time to defer the BGP route selection after restart\n" "Delay value (seconds, 0 - disable)\n") @@ -2658,7 +2690,7 @@ DEFUN (no_bgp_graceful_restart_stalepath_time, no_bgp_graceful_restart_stalepath_time_cmd, "no bgp graceful-restart stalepath-time [(1-4095)]", NO_STR - "BGP specific commands\n" + BGP_STR "Graceful restart capability parameters\n" "Set the max time to hold onto restarting peer's stale paths\n" "Delay value (seconds)\n") @@ -2673,7 +2705,7 @@ DEFUN (no_bgp_graceful_restart_restart_time, no_bgp_graceful_restart_restart_time_cmd, "no bgp graceful-restart restart-time [(1-4095)]", NO_STR - "BGP specific commands\n" + BGP_STR "Graceful restart capability parameters\n" "Set the time to wait to delete stale routes before a BGP open message is received\n" "Delay value (seconds)\n") @@ -2688,7 +2720,7 @@ DEFUN (no_bgp_graceful_restart_select_defer_time, no_bgp_graceful_restart_select_defer_time_cmd, "no bgp graceful-restart select-defer-time [(0-3600)]", NO_STR - "BGP specific commands\n" + BGP_STR "Graceful restart capability parameters\n" "Set the time to defer the BGP route selection after restart\n" "Delay value (seconds)\n") @@ -2704,7 +2736,7 @@ DEFUN (no_bgp_graceful_restart_select_defer_time, DEFUN (bgp_graceful_restart_preserve_fw, bgp_graceful_restart_preserve_fw_cmd, "bgp graceful-restart preserve-fw-state", - "BGP specific commands\n" + BGP_STR "Graceful restart capability parameters\n" "Sets F-bit indication that fib is preserved while doing Graceful Restart\n") { @@ -2717,7 +2749,7 @@ DEFUN (no_bgp_graceful_restart_preserve_fw, no_bgp_graceful_restart_preserve_fw_cmd, "no bgp graceful-restart preserve-fw-state", NO_STR - "BGP specific commands\n" + BGP_STR "Graceful restart capability parameters\n" "Unsets F-bit indication that fib is preserved while doing Graceful Restart\n") { @@ -2729,7 +2761,7 @@ DEFUN (no_bgp_graceful_restart_preserve_fw, DEFUN (bgp_graceful_restart_disable, bgp_graceful_restart_disable_cmd, "bgp graceful-restart-disable", - "BGP specific commands\n" + BGP_STR GR_DISABLE) { int ret = BGP_GR_FAILURE; @@ -2758,7 +2790,7 @@ DEFUN (no_bgp_graceful_restart_disable, no_bgp_graceful_restart_disable_cmd, "no bgp graceful-restart-disable", NO_STR - "BGP specific commands\n" + BGP_STR NO_GR_DISABLE ) { @@ -3011,7 +3043,7 @@ DEFUN (no_bgp_neighbor_graceful_restart_disable, DEFUN_HIDDEN (bgp_graceful_restart_disable_eor, bgp_graceful_restart_disable_eor_cmd, "bgp graceful-restart disable-eor", - "BGP specific commands\n" + BGP_STR "Graceful restart configuration parameters\n" "Disable EOR Check\n") { @@ -3025,7 +3057,7 @@ DEFUN_HIDDEN (no_bgp_graceful_restart_disable_eor, no_bgp_graceful_restart_disable_eor_cmd, "no bgp graceful-restart disable-eor", NO_STR - "BGP specific commands\n" + BGP_STR "Graceful restart configuration parameters\n" "Disable EOR Check\n") { @@ -3038,7 +3070,7 @@ DEFUN_HIDDEN (no_bgp_graceful_restart_disable_eor, DEFUN (bgp_graceful_restart_rib_stale_time, bgp_graceful_restart_rib_stale_time_cmd, "bgp graceful-restart rib-stale-time (1-3600)", - "BGP specific commands\n" + BGP_STR "Graceful restart configuration parameters\n" "Specify the stale route removal timer in rib\n" "Delay value (seconds)\n") @@ -3060,7 +3092,7 @@ DEFUN (no_bgp_graceful_restart_rib_stale_time, no_bgp_graceful_restart_rib_stale_time_cmd, "no bgp graceful-restart rib-stale-time [(1-3600)]", NO_STR - "BGP specific commands\n" + BGP_STR "Graceful restart configuration parameters\n" "Specify the stale route removal timer in rib\n" "Delay value (seconds)\n") @@ -3220,7 +3252,7 @@ DEFUN (no_bgp_fast_external_failover, DEFUN (bgp_bestpath_compare_router_id, bgp_bestpath_compare_router_id_cmd, "bgp bestpath compare-routerid", - "BGP specific commands\n" + BGP_STR "Change the default bestpath selection\n" "Compare router-id for identical EBGP paths\n") { @@ -3235,7 +3267,7 @@ DEFUN (no_bgp_bestpath_compare_router_id, no_bgp_bestpath_compare_router_id_cmd, "no bgp bestpath compare-routerid", NO_STR - "BGP specific commands\n" + BGP_STR "Change the default bestpath selection\n" "Compare router-id for identical EBGP paths\n") { @@ -3250,7 +3282,7 @@ DEFUN (no_bgp_bestpath_compare_router_id, DEFUN (bgp_bestpath_aspath_ignore, bgp_bestpath_aspath_ignore_cmd, "bgp bestpath as-path ignore", - "BGP specific commands\n" + BGP_STR "Change the default bestpath selection\n" "AS-path attribute\n" "Ignore as-path length in selecting a route\n") @@ -3266,7 +3298,7 @@ DEFUN (no_bgp_bestpath_aspath_ignore, no_bgp_bestpath_aspath_ignore_cmd, "no bgp bestpath as-path ignore", NO_STR - "BGP specific commands\n" + BGP_STR "Change the default bestpath selection\n" "AS-path attribute\n" "Ignore as-path length in selecting a route\n") @@ -3282,7 +3314,7 @@ DEFUN (no_bgp_bestpath_aspath_ignore, DEFUN (bgp_bestpath_aspath_confed, bgp_bestpath_aspath_confed_cmd, "bgp bestpath as-path confed", - "BGP specific commands\n" + BGP_STR "Change the default bestpath selection\n" "AS-path attribute\n" "Compare path lengths including confederation sets & sequences in selecting a route\n") @@ -3298,7 +3330,7 @@ DEFUN (no_bgp_bestpath_aspath_confed, no_bgp_bestpath_aspath_confed_cmd, "no bgp bestpath as-path confed", NO_STR - "BGP specific commands\n" + BGP_STR "Change the default bestpath selection\n" "AS-path attribute\n" "Compare path lengths including confederation sets & sequences in selecting a route\n") @@ -3314,7 +3346,7 @@ DEFUN (no_bgp_bestpath_aspath_confed, DEFUN (bgp_bestpath_aspath_multipath_relax, bgp_bestpath_aspath_multipath_relax_cmd, "bgp bestpath as-path multipath-relax [<as-set|no-as-set>]", - "BGP specific commands\n" + BGP_STR "Change the default bestpath selection\n" "AS-path attribute\n" "Allow load sharing across routes that have different AS paths (but same length)\n" @@ -3341,7 +3373,7 @@ DEFUN (no_bgp_bestpath_aspath_multipath_relax, no_bgp_bestpath_aspath_multipath_relax_cmd, "no bgp bestpath as-path multipath-relax [<as-set|no-as-set>]", NO_STR - "BGP specific commands\n" + BGP_STR "Change the default bestpath selection\n" "AS-path attribute\n" "Allow load sharing across routes that have different AS paths (but same length)\n" @@ -3391,7 +3423,7 @@ DEFUN(no_bgp_bestpath_peer_type_multipath_relax, DEFUN (bgp_log_neighbor_changes, bgp_log_neighbor_changes_cmd, "bgp log-neighbor-changes", - "BGP specific commands\n" + BGP_STR "Log neighbor up/down and reset reason\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); @@ -3403,7 +3435,7 @@ DEFUN (no_bgp_log_neighbor_changes, no_bgp_log_neighbor_changes_cmd, "no bgp log-neighbor-changes", NO_STR - "BGP specific commands\n" + BGP_STR "Log neighbor up/down and reset reason\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); @@ -3415,7 +3447,7 @@ DEFUN (no_bgp_log_neighbor_changes, DEFUN (bgp_bestpath_med, bgp_bestpath_med_cmd, "bgp bestpath med <confed [missing-as-worst]|missing-as-worst [confed]>", - "BGP specific commands\n" + BGP_STR "Change the default bestpath selection\n" "MED attribute\n" "Compare MED among confederation paths\n" @@ -3441,7 +3473,7 @@ DEFUN (no_bgp_bestpath_med, no_bgp_bestpath_med_cmd, "no bgp bestpath med <confed [missing-as-worst]|missing-as-worst [confed]>", NO_STR - "BGP specific commands\n" + BGP_STR "Change the default bestpath selection\n" "MED attribute\n" "Compare MED among confederation paths\n" @@ -3467,7 +3499,7 @@ DEFUN (no_bgp_bestpath_med, DEFPY (bgp_bestpath_bw, bgp_bestpath_bw_cmd, "bgp bestpath bandwidth <ignore|skip-missing|default-weight-for-missing>$bw_cfg", - "BGP specific commands\n" + BGP_STR "Change the default bestpath selection\n" "Link Bandwidth attribute\n" "Ignore link bandwidth (i.e., do regular ECMP, not weighted)\n" @@ -3505,7 +3537,7 @@ DEFPY (no_bgp_bestpath_bw, no_bgp_bestpath_bw_cmd, "no bgp bestpath bandwidth [<ignore|skip-missing|default-weight-for-missing>$bw_cfg]", NO_STR - "BGP specific commands\n" + BGP_STR "Change the default bestpath selection\n" "Link Bandwidth attribute\n" "Ignore link bandwidth (i.e., do regular ECMP, not weighted)\n" @@ -3540,7 +3572,7 @@ DEFPY(bgp_default_afi_safi, bgp_default_afi_safi_cmd, "ipv6-flowspec|" "l2vpn-evpn>$afi_safi", NO_STR - "BGP specific commands\n" + BGP_STR "Configure BGP defaults\n" "Activate ipv4-unicast for a peer by default\n" "Activate ipv4-multicast for a peer by default\n" @@ -3588,7 +3620,7 @@ DEFPY(bgp_default_afi_safi, bgp_default_afi_safi_cmd, DEFUN (bgp_default_show_hostname, bgp_default_show_hostname_cmd, "bgp default show-hostname", - "BGP specific commands\n" + BGP_STR "Configure BGP defaults\n" "Show hostname in certain command outputs\n") { @@ -3601,7 +3633,7 @@ DEFUN (no_bgp_default_show_hostname, no_bgp_default_show_hostname_cmd, "no bgp default show-hostname", NO_STR - "BGP specific commands\n" + BGP_STR "Configure BGP defaults\n" "Show hostname in certain command outputs\n") { @@ -3614,7 +3646,7 @@ DEFUN (no_bgp_default_show_hostname, DEFUN (bgp_default_show_nexthop_hostname, bgp_default_show_nexthop_hostname_cmd, "bgp default show-nexthop-hostname", - "BGP specific commands\n" + BGP_STR "Configure BGP defaults\n" "Show hostname for nexthop in certain command outputs\n") { @@ -3627,7 +3659,7 @@ DEFUN (no_bgp_default_show_nexthop_hostname, no_bgp_default_show_nexthop_hostname_cmd, "no bgp default show-nexthop-hostname", NO_STR - "BGP specific commands\n" + BGP_STR "Configure BGP defaults\n" "Show hostname for nexthop in certain command outputs\n") { @@ -3640,7 +3672,7 @@ DEFUN (no_bgp_default_show_nexthop_hostname, DEFUN (bgp_network_import_check, bgp_network_import_check_cmd, "bgp network import-check", - "BGP specific commands\n" + BGP_STR "BGP network command\n" "Check BGP network route exists in IGP\n") { @@ -3655,7 +3687,7 @@ DEFUN (bgp_network_import_check, ALIAS_HIDDEN(bgp_network_import_check, bgp_network_import_check_exact_cmd, "bgp network import-check exact", - "BGP specific commands\n" + BGP_STR "BGP network command\n" "Check BGP network route exists in IGP\n" "Match route precisely\n") @@ -3664,7 +3696,7 @@ DEFUN (no_bgp_network_import_check, no_bgp_network_import_check_cmd, "no bgp network import-check", NO_STR - "BGP specific commands\n" + BGP_STR "BGP network command\n" "Check BGP network route exists in IGP\n") { @@ -3680,7 +3712,7 @@ DEFUN (no_bgp_network_import_check, DEFUN (bgp_default_local_preference, bgp_default_local_preference_cmd, "bgp default local-preference (0-4294967295)", - "BGP specific commands\n" + BGP_STR "Configure BGP defaults\n" "local preference (higher=more preferred)\n" "Configure default local preference value\n") @@ -3701,7 +3733,7 @@ DEFUN (no_bgp_default_local_preference, no_bgp_default_local_preference_cmd, "no bgp default local-preference [(0-4294967295)]", NO_STR - "BGP specific commands\n" + BGP_STR "Configure BGP defaults\n" "local preference (higher=more preferred)\n" "Configure default local preference value\n") @@ -3717,7 +3749,7 @@ DEFUN (no_bgp_default_local_preference, DEFUN (bgp_default_subgroup_pkt_queue_max, bgp_default_subgroup_pkt_queue_max_cmd, "bgp default subgroup-pkt-queue-max (20-100)", - "BGP specific commands\n" + BGP_STR "Configure BGP defaults\n" "subgroup-pkt-queue-max\n" "Configure subgroup packet queue max\n") @@ -3737,7 +3769,7 @@ DEFUN (no_bgp_default_subgroup_pkt_queue_max, no_bgp_default_subgroup_pkt_queue_max_cmd, "no bgp default subgroup-pkt-queue-max [(20-100)]", NO_STR - "BGP specific commands\n" + BGP_STR "Configure BGP defaults\n" "subgroup-pkt-queue-max\n" "Configure subgroup packet queue max\n") @@ -3751,7 +3783,7 @@ DEFUN (no_bgp_default_subgroup_pkt_queue_max, DEFUN (bgp_rr_allow_outbound_policy, bgp_rr_allow_outbound_policy_cmd, "bgp route-reflector allow-outbound-policy", - "BGP specific commands\n" + BGP_STR "Allow modifications made by out route-map\n" "on ibgp neighbors\n") { @@ -3770,7 +3802,7 @@ DEFUN (no_bgp_rr_allow_outbound_policy, no_bgp_rr_allow_outbound_policy_cmd, "no bgp route-reflector allow-outbound-policy", NO_STR - "BGP specific commands\n" + BGP_STR "Allow modifications made by out route-map\n" "on ibgp neighbors\n") { @@ -3788,7 +3820,7 @@ DEFUN (no_bgp_rr_allow_outbound_policy, DEFUN (bgp_listen_limit, bgp_listen_limit_cmd, "bgp listen limit (1-65535)", - "BGP specific commands\n" + BGP_STR "BGP Dynamic Neighbors listen commands\n" "Maximum number of BGP Dynamic Neighbors that can be created\n" "Configure Dynamic Neighbors listen limit value\n") @@ -3808,7 +3840,7 @@ DEFUN (no_bgp_listen_limit, no_bgp_listen_limit_cmd, "no bgp listen limit [(1-65535)]", NO_STR - "BGP specific commands\n" + BGP_STR "BGP Dynamic Neighbors listen commands\n" "Maximum number of BGP Dynamic Neighbors that can be created\n" "Configure Dynamic Neighbors listen limit value\n") @@ -3853,7 +3885,7 @@ static struct peer_group *listen_range_exists(struct bgp *bgp, DEFUN (bgp_listen_range, bgp_listen_range_cmd, "bgp listen range <A.B.C.D/M|X:X::X:X/M> peer-group PGNAME", - "BGP specific commands\n" + BGP_STR "Configure BGP dynamic neighbors listen range\n" "Configure BGP dynamic neighbors listen range\n" NEIGHBOR_ADDR_STR @@ -3924,7 +3956,7 @@ DEFUN (no_bgp_listen_range, no_bgp_listen_range_cmd, "no bgp listen range <A.B.C.D/M|X:X::X:X/M> peer-group PGNAME", NO_STR - "BGP specific commands\n" + BGP_STR "Unconfigure BGP dynamic neighbors listen range\n" "Unconfigure BGP dynamic neighbors listen range\n" NEIGHBOR_ADDR_STR @@ -3998,7 +4030,7 @@ void bgp_config_write_listen(struct vty *vty, struct bgp *bgp) DEFUN (bgp_disable_connected_route_check, bgp_disable_connected_route_check_cmd, "bgp disable-ebgp-connected-route-check", - "BGP specific commands\n" + BGP_STR "Disable checking if nexthop is connected on ebgp sessions\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); @@ -4012,7 +4044,7 @@ DEFUN (no_bgp_disable_connected_route_check, no_bgp_disable_connected_route_check_cmd, "no bgp disable-ebgp-connected-route-check", NO_STR - "BGP specific commands\n" + BGP_STR "Disable checking if nexthop is connected on ebgp sessions\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); @@ -6880,19 +6912,21 @@ DEFUN (neighbor_interface, { int idx_ip = 1; int idx_word = 3; + return peer_interface_vty(vty, argv[idx_ip]->arg, argv[idx_word]->arg); } DEFUN (no_neighbor_interface, no_neighbor_interface_cmd, - "no neighbor <A.B.C.D|X:X::X:X|WORD> interface WORD", + "no neighbor <A.B.C.D|X:X::X:X> interface WORD", NO_STR NEIGHBOR_STR - NEIGHBOR_ADDR_STR2 + NEIGHBOR_ADDR_STR "Interface\n" "Interface name\n") { int idx_peer = 2; + return peer_interface_vty(vty, argv[idx_peer]->arg, NULL); } @@ -16004,6 +16038,7 @@ static void bgp_vpn_policy_config_write_afi(struct vty *vty, struct bgp *bgp, afi_t afi) { int indent = 2; + uint32_t tovpn_sid_index = 0; if (bgp->vpn_policy[afi].rmap_name[BGP_VPN_POLICY_DIR_FROMVPN]) { if (CHECK_FLAG(bgp->af_flags[afi][SAFI_UNICAST], @@ -16033,6 +16068,16 @@ static void bgp_vpn_policy_config_write_afi(struct vty *vty, struct bgp *bgp, bgp->vpn_policy[afi].tovpn_label); } } + + tovpn_sid_index = bgp->vpn_policy[afi].tovpn_sid_index; + if (CHECK_FLAG(bgp->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_SID_AUTO)) { + vty_out(vty, "%*ssid vpn export %s\n", indent, "", "auto"); + } else if (tovpn_sid_index != 0) { + vty_out(vty, "%*ssid vpn export %d\n", indent, "", + tovpn_sid_index); + } + if (CHECK_FLAG(bgp->vpn_policy[afi].flags, BGP_VPN_POLICY_TOVPN_RD_SET)) { char buf[RD_ADDRSTRLEN]; @@ -17134,6 +17179,12 @@ int bgp_config_write(struct vty *vty) vty_out(vty, " timers bgp %u %u\n", bgp->default_keepalive, bgp->default_holdtime); + /* BGP minimum holdtime configuration. */ + if (bgp->default_min_holdtime != SAVE_BGP_HOLDTIME + && bgp->default_min_holdtime != 0) + vty_out(vty, " bgp minimum-holdtime %u\n", + bgp->default_min_holdtime); + /* Conditional advertisement timer configuration */ if (bgp->condition_check_period != DEFAULT_CONDITIONAL_ROUTES_POLL_TIME) @@ -17177,6 +17228,7 @@ int bgp_config_write(struct vty *vty) if (strlen(bgp->srv6_locator_name)) vty_out(vty, " locator %s\n", bgp->srv6_locator_name); + vty_endframe(vty, " exit\n"); } @@ -17226,6 +17278,7 @@ int bgp_config_write(struct vty *vty) bgp_rfapi_cfg_write(vty, bgp); #endif + vty_out(vty, "exit\n"); vty_out(vty, "!\n"); } return 0; @@ -17535,6 +17588,10 @@ void bgp_vty_init(void) install_element(BGP_NODE, &bgp_timers_cmd); install_element(BGP_NODE, &no_bgp_timers_cmd); + /* "minimum-holdtime" commands. */ + install_element(BGP_NODE, &bgp_minimum_holdtime_cmd); + install_element(BGP_NODE, &no_bgp_minimum_holdtime_cmd); + /* route-map delay-timer commands - per instance for backwards compat. */ install_element(BGP_NODE, &bgp_set_route_map_delay_timer_cmd); @@ -19939,6 +19996,8 @@ static void community_list_vty(void) install_element(CONFIG_NODE, &no_bgp_lcommunity_list_name_expanded_cmd); install_element(VIEW_NODE, &show_bgp_lcommunity_list_cmd); install_element(VIEW_NODE, &show_bgp_lcommunity_list_arg_cmd); + + bgp_community_list_command_completion_setup(); } static struct cmd_node community_alias_node = { diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index fa290743c7..5ef49e5108 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -517,8 +517,8 @@ static int zebra_read_route(ZAPI_CALLBACK_ARGS) /* Now perform the add/update. */ bgp_redistribute_add(bgp, &api.prefix, &nexthop, ifindex, - nhtype, api.metric, api.type, api.instance, - api.tag); + nhtype, api.distance, api.metric, api.type, + api.instance, api.tag); } else { bgp_redistribute_delete(bgp, &api.prefix, api.type, api.instance); @@ -838,6 +838,12 @@ bool bgp_zebra_nexthop_set(union sockunion *local, union sockunion *remote, if (direct) v6_ll_avail = if_get_ipv6_local( ifp, &nexthop->v6_local); + /* + * It's fine to not have a v6 LL when using + * update-source loopback/vrf + */ + if (!v6_ll_avail && if_is_loopback_or_vrf(ifp)) + v6_ll_avail = true; } else /* Link-local address. */ { @@ -1543,6 +1549,7 @@ void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p, label_buf[0] = '\0'; eth_buf[0] = '\0'; + segs_buf[0] = '\0'; if (has_valid_label && !CHECK_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE)) snprintf(label_buf, sizeof(label_buf), diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 1c558cc550..cea7fb4c85 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -3154,6 +3154,7 @@ static struct bgp *bgp_create(as_t *as, const char *name, bgp->default_subgroup_pkt_queue_max = BGP_DEFAULT_SUBGROUP_PKT_QUEUE_MAX; bgp_timers_unset(bgp); + bgp->default_min_holdtime = 0; bgp->restart_time = BGP_DEFAULT_RESTART_TIME; bgp->stalepath_time = BGP_DEFAULT_STALEPATH_TIME; bgp->select_defer_time = BGP_DEFAULT_SELECT_DEFERRAL_TIME; diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index ed7642f315..dc8677d3a1 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -602,6 +602,9 @@ struct bgp { uint32_t default_connect_retry; uint32_t default_delayopen; + /* BGP minimum holdtime. */ + uint16_t default_min_holdtime; + /* BGP graceful restart */ uint32_t restart_time; uint32_t stalepath_time; @@ -1417,6 +1420,7 @@ struct peer { struct thread *t_gr_stale; struct thread *t_generate_updgrp_packets; struct thread *t_process_packet; + struct thread *t_process_packet_error; struct thread *t_refresh_stalepath; /* Thread flags. */ diff --git a/bgpd/rfapi/bgp_rfapi_cfg.c b/bgpd/rfapi/bgp_rfapi_cfg.c index cc64261388..2437bd8cfe 100644 --- a/bgpd/rfapi/bgp_rfapi_cfg.c +++ b/bgpd/rfapi/bgp_rfapi_cfg.c @@ -4043,7 +4043,7 @@ int bgp_rfapi_cfg_write(struct vty *vty, struct bgp *bgp) rfg->routemap_redist_name [ZEBRA_ROUTE_BGP_DIRECT_EXT]); } - vty_out(vty, " exit-vrf-policy\n"); + vty_out(vty, " exit-vrf-policy\n"); vty_out(vty, "!\n"); } if (hc->flags & BGP_VNC_CONFIG_ADV_UN_METHOD_ENCAP) { @@ -4121,7 +4121,7 @@ int bgp_rfapi_cfg_write(struct vty *vty, struct bgp *bgp) vty, bgp->rfapi->rfp, RFAPI_RFP_CFG_GROUP_L2, rfgc->name, rfgc->rfp_cfg); - vty_out(vty, " exit-vnc\n"); + vty_out(vty, " exit-vnc\n"); vty_out(vty, "!\n"); } } @@ -4199,7 +4199,7 @@ int bgp_rfapi_cfg_write(struct vty *vty, struct bgp *bgp) vty, bgp->rfapi->rfp, RFAPI_RFP_CFG_GROUP_DEFAULT, NULL, bgp->rfapi_cfg->default_rfp_cfg); - vty_out(vty, " exit-vnc\n"); + vty_out(vty, " exit-vnc\n"); vty_out(vty, "!\n"); } @@ -4364,7 +4364,7 @@ int bgp_rfapi_cfg_write(struct vty *vty, struct bgp *bgp) vty, bgp->rfapi->rfp, RFAPI_RFP_CFG_GROUP_NVE, rfg->name, rfg->rfp_cfg); - vty_out(vty, " exit-vnc\n"); + vty_out(vty, " exit-vnc\n"); vty_out(vty, "!\n"); } } /* have listen ports */ diff --git a/doc/developer/scripting.rst b/doc/developer/scripting.rst index d543ed3560..202f0036f8 100644 --- a/doc/developer/scripting.rst +++ b/doc/developer/scripting.rst @@ -117,6 +117,7 @@ Load The function to be called must first be loaded. Use ``frrscript_load()`` which takes a ``frrscript`` object, the name of the Lua function and a callback function. +The script file will be read to load and compile the function. For example, to load the Lua function ``on_foo`` in ``/etc/frr/scripts/bingus.lua``: @@ -138,7 +139,7 @@ should not be able to write the scripts directory anyway. Call ^^^^ -After loading, Lua functions may be called. +After loading, a Lua function can be called any number of times. Input """"" @@ -231,6 +232,18 @@ In the example, ``d`` is a "new" value in C space, so memory allocation might take place. Hence the caller is responsible for memory deallocation. +``frrscript_call()`` may be called multiple times without re-loading with +``frrscript_load()``. Results are not preserved between consecutive calls. + +.. code-block:: c + + frrscript_load(fs, "on_foo"); + + frrscript_call(fs, "on_foo"); + frrscript_get_result(fs, "on_foo", ...); + frrscript_call(fs, "on_foo"); + frrscript_get_result(fs, "on_foo", ...); + Delete ^^^^^^ @@ -385,7 +398,7 @@ Again, for ``struct prefix *``: { lua_getfield(L, idx, "network"); (void)str2prefix(lua_tostring(L, -1), prefix); - /* pop the netork string */ + /* pop the network string */ lua_pop(L, 1); /* pop the prefix table */ lua_pop(L, 1); diff --git a/doc/developer/workflow.rst b/doc/developer/workflow.rst index dae175a732..b6fde2b283 100644 --- a/doc/developer/workflow.rst +++ b/doc/developer/workflow.rst @@ -88,22 +88,45 @@ FRR employs a ``<MAJOR>.<MINOR>.<BUGFIX>`` versioning scheme. ``BUGFIX`` Fixes for actual bugs and/or security issues. Fully compatible. -We will pull a new development branch for the next release every 4 months. The -current schedule is Feb/June/October 1. The decision for a ``MAJOR/MINOR`` -release is made at the time of branch pull based on what has been received the -previous 4 months. The branch name will be ``dev/MAJOR.MINOR``. At this point -in time the master branch and this new branch, :file:`configure.ac`, -documentation and packaging systems will be updated to reflect the next -possible release name to allow for easy distinguishing. - -After one month the development branch will be renamed to -``stable/MAJOR.MINOR``. The branch is a stable branch. This process is not -held up unless a crash or security issue has been found and needs to -be addressed. Issues being fixed will not cause a delay. - -Bugfix releases are made as needed at 1 month intervals until the next -``MAJOR.MINOR`` release branch is pulled. Depending on the severity of the bugs, -bugfix releases may occur sooner. +Releases are scheduled in a 4-month cycle on the first Tuesday each +March/July/November. Walking backwards from this date: + + - 6 weeks earlier, ``master`` is frozen for new features, and feature PRs + are considered lowest priority (regardless of when they were opened.) + + - 4 weeks earlier, the stable branch separates from master (named + ``dev/MAJOR.MINOR`` at this point) and a ``rc1`` release candidate is + tagged. Master is unfrozen and new features may again proceed. + + - 2 weeks earlier, a ``rc2`` release candidate is tagged. + + - on release date, the branch is renamed to ``stable/MAJOR.MINOR``. + +The 2 week window between each of these events should be used to run any and +all testing possible for the release in progress. However, the current +intention is to stick to the schedule even if known issues remain. This would +hopefully occur only after all avenues of fixing issues are exhausted, but to +achieve this, an as exhaustive as possible list of issues needs to be available +as early as possible, i.e. the first 2-week window. + +For reference, the expected release schedule according to the above is: + ++------------+------------+------------+------------+------------+------------+ +| Release | 2021-11-02 | 2022-03-01 | 2022-07-05 | 2022-11-01 | 2023-03-07 | ++------------+------------+------------+------------+------------+------------+ +| rc2 | 2021-10-19 | 2022-02-15 | 2022-06-21 | 2022-10-18 | 2023-02-21 | ++------------+------------+------------+------------+------------+------------+ +| rc1/branch | 2021-10-05 | 2022-02-01 | 2022-06-07 | 2022-10-04 | 2023-02-07 | ++------------+------------+------------+------------+------------+------------+ +| freeze | 2021-09-21 | 2022-01-18 | 2022-05-24 | 2022-09-20 | 2023-01-24 | ++------------+------------+------------+------------+------------+------------+ + +Each release is managed by one or more volunteer release managers from the FRR +community. To spread and distribute this workload, this should be rotated for +subsequent releases. The release managers are currently assumed/expected to +run a release management meeting during the weeks listed above. Barring other +constraints, this would be scheduled before the regular weekly FRR community +call such that important items can be carried over into that call. Bugfixes are applied to the two most recent releases. However, backporting of bug fixes to older than the two most recent releases will not be prevented, if acked diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index 6586b42722..2383906150 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -424,8 +424,8 @@ Administrative Distance Metrics .. clicmd:: distance bgp (1-255) (1-255) (1-255) - This command change distance value of BGP. The arguments are the distance - values for for external routes, internal routes and local routes + This command changes distance value of BGP. The arguments are the distance + values for external routes, internal routes and local routes respectively. .. clicmd:: distance (1-255) A.B.C.D/M @@ -464,9 +464,9 @@ Require policy on EBGP RIB entries 7, using 1344 bytes of memory Peers 2, using 43 KiB of memory - Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt - 192.168.0.2 4 65002 8 10 0 0 0 00:03:09 5 (Policy) - fe80:1::2222 4 65002 9 11 0 0 0 00:03:09 (Policy) (Policy) + Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt Desc + 192.168.0.2 4 65002 8 10 0 0 0 00:03:09 5 (Policy) N/A + fe80:1::2222 4 65002 9 11 0 0 0 00:03:09 (Policy) (Policy) N/A Additionally a `show bgp neighbor` command would indicate in the `For address family:` block that: @@ -959,7 +959,7 @@ BGP GR Global Mode Commands .. clicmd:: bgp graceful-restart - This command will enable BGP graceful restart ifunctionality at the global + This command will enable BGP graceful restart functionality at the global level. .. clicmd:: bgp graceful-restart disable @@ -975,7 +975,7 @@ BGP GR Peer Mode Commands .. clicmd:: neighbor A.B.C.D graceful-restart - This command will enable BGP graceful restart ifunctionality at the peer + This command will enable BGP graceful restart functionality at the peer level. .. clicmd:: neighbor A.B.C.D graceful-restart-helper @@ -1082,7 +1082,7 @@ IPv6 Support This configuration demonstrates how the 'no bgp default ipv4-unicast' might be used in a setup with two upstreams where each of the upstreams should only - receive either IPv4 or IPv6 annocuments. + receive either IPv4 or IPv6 announcements. Using the ``bgp default ipv6-unicast`` configuration, IPv6 unicast address family is enabled by default for all new neighbors. @@ -1102,6 +1102,13 @@ Route Aggregation-IPv4 Address Family This command specifies an aggregate address. + In order to advertise an aggregated prefix, a more specific (longer) prefix + MUST exist in the BGP table. For example, if you want to create an + ``aggregate-address 10.0.0.0/24``, you should make sure you have something + like ``10.0.0.5/32`` or ``10.0.0.0/26``, or any other smaller prefix in the + BGP table. The routing information table (RIB) is not enough, you have to + redistribute them into the BGP table. + .. clicmd:: aggregate-address A.B.C.D/M route-map NAME Apply a route-map for an aggregated prefix. @@ -1117,8 +1124,9 @@ Route Aggregation-IPv4 Address Family .. clicmd:: aggregate-address A.B.C.D/M summary-only - This command specifies an aggregate address. Aggregated routes will - not be announced. + This command specifies an aggregate address. + + Longer prefixes advertisements of more specific routes to all neighbors are suppressed. .. clicmd:: aggregate-address A.B.C.D/M matching-MED-only @@ -1169,8 +1177,9 @@ Route Aggregation-IPv6 Address Family .. clicmd:: aggregate-address X:X::X:X/M summary-only - This command specifies an aggregate address. Aggregated routes will - not be announced. + This command specifies an aggregate address. + + Longer prefixes advertisements of more specific routes to all neighbors are suppressed .. clicmd:: aggregate-address X:X::X:X/M matching-MED-only @@ -1254,7 +1263,7 @@ Redistribute routes from other protocols into BGP. This feature is used to enable read-only mode on BGP process restart or when a BGP process is cleared using 'clear ip bgp \*'. Note that this command is - configured under the specific bgp instance/vrf that the feaure is enabled for. + configured under the specific bgp instance/vrf that the feature is enabled for. It cannot be used at the same time as the global "bgp update-delay" described above, which is entered at the global level and applies to all bgp instances. The global and per-vrf approaches to defining update-delay are mutually @@ -1359,7 +1368,7 @@ Defining Peers limit is set to 100 by default. Increasing this value will really be possible if more file descriptors are available in the BGP process. This value is defined by the underlying system (ulimit value), and can be - overriden by `--limit-fds`. More information is available in chapter + overridden by `--limit-fds`. More information is available in chapter (:ref:`common-invocation-options`). .. clicmd:: coalesce-time (0-4294967295) @@ -1546,7 +1555,7 @@ Configuring Peers can't connect them directly. This is an alternative to `neighbor WORD as-override`. - The parameter `(1-10)` configures the amount of accepted occurences of the + The parameter `(1-10)` configures the amount of accepted occurrences of the system AS number in AS path. The parameter `origin` configures BGP to only accept routes originated with @@ -1671,7 +1680,7 @@ Configuring Peers Set keepalive and hold timers for a neighbor. The first value is keepalive and the second is hold time. -.. clicmd:: neighbor PEER connect (1-65535) +.. clicmd:: neighbor PEER timers connect (1-65535) Set connect timer for a neighbor. The connect timer controls how long BGP waits between connection attempts to a neighbor. @@ -1684,6 +1693,12 @@ Configuring Peers default, the DelayOpenTimer is disabled. The timer interval may be set to a duration of 1 to 240 seconds. +.. clicmd:: bgp minimum-holdtime (1-65535) + + This command allows user to prevent session establishment with BGP peers + with lower holdtime less than configured minimum holdtime. + When this command is not set, minimum holdtime does not work. + Displaying Information about Peers ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -2557,7 +2572,7 @@ BGP routes may be leaked (i.e. copied) between a unicast VRF RIB and the VPN SAFI RIB of the default VRF for use in MPLS-based L3VPNs. Unicast routes may also be leaked between any VRFs (including the unicast RIB of the default BGP instanced). A shortcut syntax is also available for specifying leaking from one -VRF to another VRF using the default instance's VPN RIB as the intemediary. A +VRF to another VRF using the default instance's VPN RIB as the intermediary. A common application of the VRF-VRF feature is to connect a customer's private routing domain to a provider's VPN service. Leaking is configured from the point of view of an individual VRF: ``import`` refers to routes leaked from VPN @@ -2738,7 +2753,7 @@ the same behavior of using same next-hop and RMAC values. .. clicmd:: advertise-pip [ip <addr> [mac <addr>]] -Enables or disables advertise-pip feature, specifiy system-IP and/or system-MAC +Enables or disables advertise-pip feature, specify system-IP and/or system-MAC parameters. EVPN advertise-svi-ip @@ -2794,7 +2809,7 @@ Topology requirements: 1. This feature is supported for asymmetric routing model only. While sending packets to SN1, ingress PE (PE2) performs routing and egress PE (PE1) performs only bridging. -2. This feature supports only tratitional(non vlan-aware) bridge model. Bridge +2. This feature supports only traditional(non vlan-aware) bridge model. Bridge interface associated with L2VNI is an L3 interface. i.e., this interface is configured with an address in the L2VNI subnet. Note that the gateway IP should also have an address in the same subnet. @@ -2872,7 +2887,7 @@ This group of server links is referred to as an Ethernet Segment. Ethernet Segments """"""""""""""""" An Ethernet Segment can be configured by specifying a system-MAC and a -local discriminatior against the bond interface on the PE (via zebra) - +local discriminator against the bond interface on the PE (via zebra) - .. clicmd:: evpn mh es-id (1-16777215) @@ -2903,7 +2918,7 @@ The DF preference is configurable per-ES (via zebra) - BUM traffic is rxed via the overlay by all PEs attached to a server but only the DF can forward the de-capsulated traffic to the access port. To -accomodate that non-DF filters are installed in the dataplane to drop +accommodate that non-DF filters are installed in the dataplane to drop the traffic. Similarly traffic received from ES peers via the overlay cannot be forwarded @@ -3463,12 +3478,12 @@ structure is extended with :clicmd:`show bgp [afi] [safi]`. .. clicmd:: show bgp [afi] [safi] [all] summary failed [json] - Show a bgp peer summary for peers that are not succesfully exchanging routes + Show a bgp peer summary for peers that are not successfully exchanging routes for the specified address family, and subsequent address-family. .. clicmd:: show bgp [afi] [safi] [all] summary established [json] - Show a bgp peer summary for peers that are succesfully exchanging routes + Show a bgp peer summary for peers that are successfully exchanging routes for the specified address family, and subsequent address-family. .. clicmd:: show bgp [afi] [safi] [all] summary neighbor [PEER] [json] @@ -3626,7 +3641,7 @@ attribute. If ``summary`` option is specified, output is a summary of the counts for the chunks, inuse, ledger and requests list along with the count of - outstanding chunk requests to Zebra and the nummber of zebra reconnects + outstanding chunk requests to Zebra and the number of zebra reconnects that have happened If ``json`` option is specified, output is displayed in JSON format. diff --git a/doc/user/ospf6d.rst b/doc/user/ospf6d.rst index 948527d774..499788ae87 100644 --- a/doc/user/ospf6d.rst +++ b/doc/user/ospf6d.rst @@ -70,20 +70,6 @@ OSPF6 router Use this command to control the maximum number of parallel routes that OSPFv3 can support. The default is 64. -.. clicmd:: area A.B.C.D range X:X::X:X/M [<advertise|not-advertise|cost (0-16777215)>] - -.. clicmd:: area (0-4294967295) range X:X::X:X/M [<advertise|not-advertise|cost (0-16777215)>] - - Summarize a group of internal subnets into a single Inter-Area-Prefix LSA. - This command can only be used at the area boundary (ABR router). - - By default, the metric of the summary route is calculated as the highest - metric among the summarized routes. The `cost` option, however, can be used - to set an explicit metric. - - The `not-advertise` option, when present, prevents the summary route from - being advertised, effectively filtering the summarized routes. - .. clicmd:: write-multiplier (1-100) Use this command to tune the amount of work done in the packet read and @@ -171,89 +157,42 @@ ASBR Summarisation Support in OSPFv3 When detail option is used, it shows all the prefixes falling under each summary-configuration apart from other information. -.. clicmd:: debug ospf6 lsa aggregation - - This command can be used to enable the debugs related to the summarisation - of these LSAs. - -.. _ospf6-debugging: - -OSPFv3 Debugging -================ - -The following debug commands are supported: - -.. clicmd:: debug ospf6 abr - - Toggle OSPFv3 ABR debugging messages. - -.. clicmd:: debug ospf6 asbr - - Toggle OSPFv3 ASBR debugging messages. - -.. clicmd:: debug ospf6 border-routers - - Toggle OSPFv3 border router debugging messages. - -.. clicmd:: debug ospf6 flooding - - Toggle OSPFv3 flooding debugging messages. - -.. clicmd:: debug ospf6 interface - - Toggle OSPFv3 interface related debugging messages. - -.. clicmd:: debug ospf6 lsa - - Toggle OSPFv3 Link State Advertisements debugging messages. - -.. clicmd:: debug ospf6 message - - Toggle OSPFv3 message exchange debugging messages. - -.. clicmd:: debug ospf6 neighbor - - Toggle OSPFv3 neighbor interaction debugging messages. - -.. clicmd:: debug ospf6 nssa - - Toggle OSPFv3 Not So Stubby Area (NSSA) debugging messages. - -.. clicmd:: debug ospf6 route - - Toggle OSPFv3 routes debugging messages. +.. _ospf6-area: -.. clicmd:: debug ospf6 spf +OSPF6 area +========== - Toggle OSPFv3 Shortest Path calculation debugging messages. +.. clicmd:: area A.B.C.D range X:X::X:X/M [<advertise|not-advertise|cost (0-16777215)>] -.. clicmd:: debug ospf6 zebra +.. clicmd:: area (0-4294967295) range X:X::X:X/M [<advertise|not-advertise|cost (0-16777215)>] - Toggle OSPFv3 zebra interaction debugging messages. + Summarize a group of internal subnets into a single Inter-Area-Prefix LSA. + This command can only be used at the area boundary (ABR router). -.. _ospf6-area: + By default, the metric of the summary route is calculated as the highest + metric among the summarized routes. The `cost` option, however, can be used + to set an explicit metric. -OSPF6 area -========== + The `not-advertise` option, when present, prevents the summary route from + being advertised, effectively filtering the summarized routes. .. clicmd:: area A.B.C.D nssa -NSSA Support in OSPFv3 -======================= +.. clicmd:: area (0-4294967295) nssa -The configuration of NSSA areas in OSPFv3 is supported using the CLI command -``area A.B.C.D nssa`` in ospf6 router configuration mode. -The following functionalities are implemented as per RFC 3101: + Configure the area to be a NSSA (Not-So-Stubby Area). -1. Advertising Type-7 LSA into NSSA area when external route is redistributed - into OSPFv3 -2. Processing Type-7 LSA received from neighbor and installing route in the - route table -3. Support for NSSA ABR functionality which is generating Type-5 LSA when - backbone area is configured. Currently translation of Type-7 LSA to Type-5 LSA - is enabled by default. -4. Support for NSSA Translator functionality when there are multiple NSSA ABR - in an area + The following functionalities are implemented as per RFC 3101: + + 1. Advertising Type-7 LSA into NSSA area when external route is + redistributed into OSPFv3. + 2. Processing Type-7 LSA received from neighbor and installing route in the + route table. + 3. Support for NSSA ABR functionality which is generating Type-5 LSA when + backbone area is configured. Currently translation of Type-7 LSA to + Type-5 LSA is enabled by default. + 4. Support for NSSA Translator functionality when there are multiple NSSA + ABR in an area. .. _ospf6-interface: @@ -309,9 +248,11 @@ Usage of *ospfd6*'s route-map support. Redistribute routes to OSPF6 ============================ -.. clicmd:: redistribute <babel|bgp|connected|isis|kernel|openfabric|ripng|sharp|static|table> [route-map WORD] +.. clicmd:: redistribute <babel|bgp|connected|isis|kernel|openfabric|ripng|sharp|static|table> [metric-type (1-2)] [metric (0-16777214)] [route-map WORD] - Redistribute routes from other protocols into OSPFv3. + Redistribute routes of the specified protocol or kind into OSPFv3, with the + metric type and metric set if specified, filtering the routes using the + given route-map if specified. .. clicmd:: default-information originate [{always|metric (0-16777214)|metric-type (1-2)|route-map WORD}] @@ -319,6 +260,39 @@ Redistribute routes to OSPF6 argument injects the default route regardless of it being present in the router. Metric values and route-map can also be specified optionally. +Graceful Restart Helper +======================= + +.. clicmd:: graceful-restart helper-only [A.B.C.D] + + + Configure Graceful Restart (RFC 5187) helper support. + By default, helper support is disabled for all neighbours. + This config enables/disables helper support on this router + for all neighbours. + To enable/disable helper support for a specific + neighbour, the router-id (A.B.C.D) has to be specified. + +.. clicmd:: graceful-restart helper strict-lsa-checking + + + If 'strict-lsa-checking' is configured then the helper will + abort the Graceful Restart when a LSA change occurs which + affects the restarting router. + By default 'strict-lsa-checking' is enabled" + +.. clicmd:: graceful-restart helper supported-grace-time (10-1800) + + + Supports as HELPER for configured grace period. + +.. clicmd:: graceful-restart helper planned-only + + + It helps to support as HELPER only for planned + restarts. By default, it supports both planned and + unplanned outages. + .. _showing-ospf6-information: Showing OSPF6 information @@ -412,6 +386,73 @@ Showing OSPF6 information JSON object, with each router having "cost", "isLeafNode" and "children" as arguments. +.. clicmd:: show ipv6 ospf6 graceful-restart helper [detail] [json] + + This command shows the graceful-restart helper details including helper + configuration parameters. + +.. _ospf6-debugging: + +OSPFv3 Debugging +================ + +The following debug commands are supported: + +.. clicmd:: debug ospf6 abr + + Toggle OSPFv3 ABR debugging messages. + +.. clicmd:: debug ospf6 asbr + + Toggle OSPFv3 ASBR debugging messages. + +.. clicmd:: debug ospf6 border-routers + + Toggle OSPFv3 border router debugging messages. + +.. clicmd:: debug ospf6 flooding + + Toggle OSPFv3 flooding debugging messages. + +.. clicmd:: debug ospf6 interface + + Toggle OSPFv3 interface related debugging messages. + +.. clicmd:: debug ospf6 lsa + + Toggle OSPFv3 Link State Advertisements debugging messages. + +.. clicmd:: debug ospf6 lsa aggregation + + Toggle OSPFv3 Link State Advertisements summarization debugging messages. + +.. clicmd:: debug ospf6 message + + Toggle OSPFv3 message exchange debugging messages. + +.. clicmd:: debug ospf6 neighbor + + Toggle OSPFv3 neighbor interaction debugging messages. + +.. clicmd:: debug ospf6 nssa + + Toggle OSPFv3 Not So Stubby Area (NSSA) debugging messages. + +.. clicmd:: debug ospf6 route + + Toggle OSPFv3 routes debugging messages. + +.. clicmd:: debug ospf6 spf + + Toggle OSPFv3 Shortest Path calculation debugging messages. + +.. clicmd:: debug ospf6 zebra + + Toggle OSPFv3 zebra interaction debugging messages. + +.. clicmd:: debug ospf6 graceful-restart + + Toggle OSPFv3 graceful-restart helper debugging messages. Sample configuration ==================== diff --git a/doc/user/pathd.rst b/doc/user/pathd.rst index f27360cca7..bdbf895299 100644 --- a/doc/user/pathd.rst +++ b/doc/user/pathd.rst @@ -52,10 +52,10 @@ Example: index 20 mpls label 16030 ! segment-list SL2 - index 10 nai prefix 10.1.2.1/32 iface 1 - index 20 nai adjacency 10.1.20.1 10.1.20.2 - index 30 nai prefix 10.10.10.5/32 algorithm 0 - index 40 mpls label 18001 + index 10 nai prefix 10.1.2.1/32 iface 1 + index 20 nai adjacency 10.1.20.1 10.1.20.2 + index 30 nai prefix 10.10.10.5/32 algorithm 0 + index 40 mpls label 18001 ! policy color 1 endpoint 1.1.1.1 name default @@ -70,7 +70,7 @@ Example: ! pcep pce-config GROUP1 - source-address 1.1.1.1 + source-address ip 1.1.1.1 tcp-md5-auth secret timer keep-alive 30 ! diff --git a/doc/user/pim.rst b/doc/user/pim.rst index 6f9aa289b4..899a6b0078 100644 --- a/doc/user/pim.rst +++ b/doc/user/pim.rst @@ -93,7 +93,7 @@ Certain signals have special meanings to *pimd*. down. This command is vrf aware, to configure for a vrf, enter the vrf submode. -.. clicmd:: ip pim join-prune-interval (5-600) +.. clicmd:: ip pim join-prune-interval (1-65535) Modify the join/prune interval that pim uses to the new value. Time is specified in seconds. This command is vrf aware, to configure for a vrf, @@ -101,14 +101,14 @@ Certain signals have special meanings to *pimd*. a value smaller than 60 seconds be aware that this can and will affect convergence at scale. -.. clicmd:: ip pim keep-alive-timer (31-60000) +.. clicmd:: ip pim keep-alive-timer (1-65535) - Modify the time out value for a S,G flow from 31-60000 seconds. 31 seconds - is chosen for a lower bound because some hardware platforms cannot see data + Modify the time out value for a S,G flow from 1-60000 seconds. If choosing + a value below 31 seconds be aware that some hardware platforms cannot see data flowing in better than 30 second chunks. This command is vrf aware, to configure for a vrf, enter the vrf submode. -.. clicmd:: ip pim packets (1-100) +.. clicmd:: ip pim packets (1-255) When processing packets from a neighbor process the number of packets incoming at one time before moving on to the next task. The default value is @@ -116,7 +116,7 @@ Certain signals have special meanings to *pimd*. a large number of pim control packets flowing. This command is vrf aware, to configure for a vrf, enter the vrf submode. -.. clicmd:: ip pim register-suppress-time (5-60000) +.. clicmd:: ip pim register-suppress-time (1-65535) Modify the time that pim will register suppress a FHR will send register notifications to the kernel. This command is vrf aware, to configure for a @@ -162,7 +162,7 @@ Certain signals have special meanings to *pimd*. the existing IGMP general query timer.If no version is provided in the cli, it will be considered as default v2 query.This is a hidden command. -.. clicmd:: ip igmp watermark-warn (10-60000) +.. clicmd:: ip igmp watermark-warn (1-65535) Configure watermark warning generation for an igmp group limit. Generates warning once the configured group limit is reached while adding new groups. @@ -201,7 +201,7 @@ is in a vrf, enter the interface command with the vrf keyword at the end. Set the DR Priority for the interface. This command is useful to allow the user to influence what node becomes the DR for a lan segment. -.. clicmd:: ip pim hello (1-180) (1-630) +.. clicmd:: ip pim hello (1-65535) (1-65535) Set the pim hello and hold interval for a interface. @@ -227,11 +227,11 @@ is in a vrf, enter the interface command with the vrf keyword at the end. Join multicast group or source-group on an interface. -.. clicmd:: ip igmp query-interval (1-1800) +.. clicmd:: ip igmp query-interval (1-65535) Set the IGMP query interval that PIM will use. -.. clicmd:: ip igmp query-max-response-time (10-250) +.. clicmd:: ip igmp query-max-response-time (1-65535) Set the IGMP query response timeout value. If an report is not returned in the specified time we will assume the S,G or \*,G has timed out. @@ -246,12 +246,12 @@ is in a vrf, enter the interface command with the vrf keyword at the end. or IGMP report is received on this interface and the Group is denied by the prefix-list, PIM will ignore the join or report. -.. clicmd:: ip igmp last-member-query-count (1-7) +.. clicmd:: ip igmp last-member-query-count (1-255) Set the IGMP last member query count. The default value is 2. 'no' form of this command is used to to configure back to the default value. -.. clicmd:: ip igmp last-member-query-interval (1-255) +.. clicmd:: ip igmp last-member-query-interval (1-65535) Set the IGMP last member query interval in deciseconds. The default value is 10 deciseconds. 'no' form of this command is used to to configure back to the @@ -319,17 +319,17 @@ MSDP can be setup in different ways: Commands available for MSDP: -.. clicmd:: ip msdp timers (2-600) (3-600) [(1-600)] +.. clicmd:: ip msdp timers (1-65535) (1-65535) [(1-65535)] Configure global MSDP timers. - First value is the keep-alive interval and it must be less than the - second value which is hold-time. This configures the interval in - seconds between keep-alive messages. The default value is 60 seconds. + First value is the keep-alive interval. This configures the interval in + seconds between keep-alive messages. The default value is 60 seconds. It + should be less than the remote hold time. - Second value is the hold-time and it must be greater than the keep-alive - interval. This configures the interval in seconds before closing a non - responding connection. The default value is 75. + Second value is the hold-time. This configures the interval in seconds before + closing a non responding connection. The default value is 75. This value + should be greater than the remote keep alive time. Third value is the connection retry interval and it is optional. This configures the interval between connection attempts. The default value diff --git a/doc/user/zebra.rst b/doc/user/zebra.rst index 8b4e2c8bdc..79036320b8 100644 --- a/doc/user/zebra.rst +++ b/doc/user/zebra.rst @@ -222,7 +222,7 @@ Link Parameters Commands Bandwidth for each 0-7 priority and Admin Group (ISIS) or Resource Class/Color (OSPF). - Note that BANDIWDTH is specified in IEEE floating point format and express + Note that BANDWIDTH is specified in IEEE floating point format and express in Bytes/second. .. clicmd:: delay (0-16777215) [min (0-16777215) | max (0-16777215)] diff --git a/eigrpd/eigrp_cli.c b/eigrpd/eigrp_cli.c index 35536979ea..533d7de2c2 100644 --- a/eigrpd/eigrp_cli.c +++ b/eigrpd/eigrp_cli.c @@ -96,6 +96,7 @@ void eigrp_cli_show_header(struct vty *vty, struct lyd_node *dnode, void eigrp_cli_show_end_header(struct vty *vty, struct lyd_node *dnode) { + vty_out(vty, "exit\n"); vty_out(vty, "!\n"); } @@ -909,7 +910,7 @@ eigrp_cli_init(void) install_element(EIGRP_NODE, &eigrp_neighbor_cmd); install_element(EIGRP_NODE, &eigrp_redistribute_source_metric_cmd); - vrf_cmd_init(NULL, &eigrpd_privs); + vrf_cmd_init(NULL); if_cmd_init(eigrp_write_interface); diff --git a/eigrpd/eigrp_main.c b/eigrpd/eigrp_main.c index b775c841f0..9acb517d8c 100644 --- a/eigrpd/eigrp_main.c +++ b/eigrpd/eigrp_main.c @@ -177,7 +177,6 @@ int main(int argc, char **argv, char **envp) break; default: frr_help_exit(1); - break; } } diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h index 74a0d795ab..5888492a52 100644 --- a/include/linux/rtnetlink.h +++ b/include/linux/rtnetlink.h @@ -178,6 +178,13 @@ enum { RTM_GETVLAN, #define RTM_GETVLAN RTM_GETVLAN + RTM_NEWNEXTHOPBUCKET = 116, +#define RTM_NEWNEXTHOPBUCKET RTM_NEWNEXTHOPBUCKET + RTM_DELNEXTHOPBUCKET, +#define RTM_DELNEXTHOPBUCKET RTM_DELNEXTHOPBUCKET + RTM_GETNEXTHOPBUCKET, +#define RTM_GETNEXTHOPBUCKET RTM_GETNEXTHOPBUCKET + __RTM_MAX, #define RTM_MAX (((__RTM_MAX + 3) & ~3) - 1) }; @@ -283,6 +290,7 @@ enum { #define RTPROT_MROUTED 17 /* Multicast daemon */ #define RTPROT_KEEPALIVED 18 /* Keepalived daemon */ #define RTPROT_BABEL 42 /* Babel daemon */ +#define RTPROT_OPENR 99 /* Open Routing (Open/R) Routes */ #define RTPROT_BGP 186 /* BGP Routes */ #define RTPROT_ISIS 187 /* ISIS Routes */ #define RTPROT_OSPF 188 /* OSPF Routes */ @@ -319,7 +327,11 @@ enum rt_scope_t { #define RTM_F_FIB_MATCH 0x2000 /* return full fib lookup match */ #define RTM_F_OFFLOAD 0x4000 /* route is offloaded */ #define RTM_F_TRAP 0x8000 /* route is trapping packets */ -#define RTM_F_OFFLOAD_FAILED 0x10000 /* route offload failed */ +#define RTM_F_OFFLOAD_FAILED 0x20000000 /* route offload failed, this value + * is chosen to avoid conflicts with + * other flags defined in + * include/uapi/linux/ipv6_route.h + */ /* Reserved table identifiers */ @@ -397,11 +409,13 @@ struct rtnexthop { #define RTNH_F_DEAD 1 /* Nexthop is dead (used by multipath) */ #define RTNH_F_PERVASIVE 2 /* Do recursive gateway lookup */ #define RTNH_F_ONLINK 4 /* Gateway is forced on link */ -#define RTNH_F_OFFLOAD 8 /* offloaded route */ +#define RTNH_F_OFFLOAD 8 /* Nexthop is offloaded */ #define RTNH_F_LINKDOWN 16 /* carrier-down on nexthop */ #define RTNH_F_UNRESOLVED 32 /* The entry is unresolved (ipmr) */ +#define RTNH_F_TRAP 64 /* Nexthop is trapping packets */ -#define RTNH_COMPARE_MASK (RTNH_F_DEAD | RTNH_F_LINKDOWN | RTNH_F_OFFLOAD) +#define RTNH_COMPARE_MASK (RTNH_F_DEAD | RTNH_F_LINKDOWN | \ + RTNH_F_OFFLOAD | RTNH_F_TRAP) /* Macros to handle hexthops */ @@ -767,12 +781,18 @@ enum { #define TA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct tcamsg)) /* tcamsg flags stored in attribute TCA_ROOT_FLAGS * - * TCA_FLAG_LARGE_DUMP_ON user->kernel to request for larger than TCA_ACT_MAX_PRIO - * actions in a dump. All dump responses will contain the number of actions - * being dumped stored in for user app's consumption in TCA_ROOT_COUNT + * TCA_ACT_FLAG_LARGE_DUMP_ON user->kernel to request for larger than + * TCA_ACT_MAX_PRIO actions in a dump. All dump responses will contain the + * number of actions being dumped stored in for user app's consumption in + * TCA_ROOT_COUNT + * + * TCA_ACT_FLAG_TERSE_DUMP user->kernel to request terse (brief) dump that only + * includes essential action info (kind, index, etc.) * */ #define TCA_FLAG_LARGE_DUMP_ON (1 << 0) +#define TCA_ACT_FLAG_LARGE_DUMP_ON TCA_FLAG_LARGE_DUMP_ON +#define TCA_ACT_FLAG_TERSE_DUMP (1 << 1) /* New extended info filters for IFLA_EXT_MASK */ #define RTEXT_FILTER_VF (1 << 0) @@ -780,6 +800,8 @@ enum { #define RTEXT_FILTER_BRVLAN_COMPRESSED (1 << 2) #define RTEXT_FILTER_SKIP_STATS (1 << 3) #define RTEXT_FILTER_MRP (1 << 4) +#define RTEXT_FILTER_CFM_CONFIG (1 << 5) +#define RTEXT_FILTER_CFM_STATUS (1 << 6) /* End of information exported to user level */ diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c index a78e4996b4..6f4a91be67 100644 --- a/isisd/isis_circuit.c +++ b/isisd/isis_circuit.c @@ -1290,7 +1290,7 @@ static int isis_interface_config_write(struct vty *vty) write += hook_call(isis_circuit_config_write, circuit, vty); } - vty_endframe(vty, "!\n"); + vty_endframe(vty, "exit\n!\n"); } return write; diff --git a/isisd/isis_cli.c b/isisd/isis_cli.c index ef86d47b22..70ec66fd7f 100644 --- a/isisd/isis_cli.c +++ b/isisd/isis_cli.c @@ -146,6 +146,11 @@ void cli_show_router_isis(struct vty *vty, struct lyd_node *dnode, vty_out(vty, "\n"); } +void cli_show_router_isis_end(struct vty *vty, struct lyd_node *dnode) +{ + vty_out(vty, "exit\n"); +} + /* * XPath: /frr-interface:lib/interface/frr-isisd:isis/ * XPath: /frr-interface:lib/interface/frr-isisd:isis/ipv4-routing diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c index 5c013d634b..e3de6f08c0 100644 --- a/isisd/isis_lsp.c +++ b/isisd/isis_lsp.c @@ -641,7 +641,7 @@ void lsp_insert(struct lspdb_head *head, struct isis_lsp *lsp) } /* - * Build a list of LSPs with non-zero ht bounded by start and stop ids + * Build a list of LSPs with non-zero ht and seqno bounded by start and stop ids */ void lsp_build_list_nonzero_ht(struct lspdb_head *head, const uint8_t *start_id, const uint8_t *stop_id, struct list *list) @@ -657,7 +657,7 @@ void lsp_build_list_nonzero_ht(struct lspdb_head *head, const uint8_t *start_id, ISIS_SYS_ID_LEN + 2) > 0) break; - if (lsp->hdr.rem_lifetime) + if (lsp->hdr.rem_lifetime && lsp->hdr.seqno) listnode_add(list, lsp); } } diff --git a/isisd/isis_main.c b/isisd/isis_main.c index acfa1a29d4..6deeebda95 100644 --- a/isisd/isis_main.c +++ b/isisd/isis_main.c @@ -231,7 +231,6 @@ int main(int argc, char **argv, char **envp) break; default: frr_help_exit(1); - break; } } diff --git a/isisd/isis_nb.c b/isisd/isis_nb.c index ecad16229c..f62a8d4813 100644 --- a/isisd/isis_nb.c +++ b/isisd/isis_nb.c @@ -32,6 +32,7 @@ const struct frr_yang_module_info frr_isisd_info = { .xpath = "/frr-isisd:isis/instance", .cbs = { .cli_show = cli_show_router_isis, + .cli_show_end = cli_show_router_isis_end, .create = isis_instance_create, .destroy = isis_instance_destroy, }, diff --git a/isisd/isis_nb.h b/isisd/isis_nb.h index 0c2f7b6b7e..4680dd5ded 100644 --- a/isisd/isis_nb.h +++ b/isisd/isis_nb.h @@ -415,6 +415,7 @@ void isis_instance_segment_routing_prefix_sid_map_prefix_sid_apply_finish( /* Optional 'cli_show' callbacks. */ void cli_show_router_isis(struct vty *vty, struct lyd_node *dnode, bool show_defaults); +void cli_show_router_isis_end(struct vty *vty, struct lyd_node *dnode); void cli_show_ip_isis_ipv4(struct vty *vty, struct lyd_node *dnode, bool show_defaults); void cli_show_ip_isis_ipv6(struct vty *vty, struct lyd_node *dnode, diff --git a/isisd/isisd.c b/isisd/isisd.c index 43efa0164d..65764654ee 100644 --- a/isisd/isisd.c +++ b/isisd/isisd.c @@ -701,7 +701,7 @@ void isis_vrf_init(void) vrf_init(isis_vrf_new, isis_vrf_enable, isis_vrf_disable, isis_vrf_delete, isis_vrf_enable); - vrf_cmd_init(NULL, &isisd_privs); + vrf_cmd_init(NULL); } void isis_terminate() @@ -3011,6 +3011,8 @@ static int isis_config_write(struct vty *vty) write += area_write_mt_settings(area, vty); write += fabricd_write_settings(area, vty); + + vty_out(vty, "exit\n"); } } diff --git a/ldpd/ldp_vty_conf.c b/ldpd/ldp_vty_conf.c index b35d3dfa00..fbd718bb09 100644 --- a/ldpd/ldp_vty_conf.c +++ b/ldpd/ldp_vty_conf.c @@ -133,6 +133,8 @@ ldp_af_iface_config_write(struct vty *vty, int af) ia->hello_interval != 0) vty_out (vty, " discovery hello interval %u\n", ia->hello_interval); + + vty_out (vty, " exit\n"); } } @@ -314,6 +316,7 @@ ldp_config_write(struct vty *vty) ldp_af_config_write(vty, AF_INET, ldpd_conf, &ldpd_conf->ipv4); ldp_af_config_write(vty, AF_INET6, ldpd_conf, &ldpd_conf->ipv6); vty_out (vty, " !\n"); + vty_out (vty, "exit\n"); vty_out (vty, "!\n"); return (1); @@ -353,6 +356,8 @@ ldp_l2vpn_pw_config_write(struct vty *vty, struct l2vpn_pw *pw) " ! Incomplete config, specify a neighbor lsr-id\n"); if (missing_pwid) vty_out (vty," ! Incomplete config, specify a pw-id\n"); + + vty_out (vty, " exit\n"); } static int @@ -383,6 +388,7 @@ ldp_l2vpn_config_write(struct vty *vty) ldp_l2vpn_pw_config_write(vty, pw); vty_out (vty, " !\n"); + vty_out (vty, "exit\n"); vty_out (vty, "!\n"); } diff --git a/ldpd/ldpd.c b/ldpd/ldpd.c index 800b954d65..000d1a3301 100644 --- a/ldpd/ldpd.c +++ b/ldpd/ldpd.c @@ -304,7 +304,6 @@ main(int argc, char *argv[]) break; default: frr_help_exit(1); - break; } } @@ -366,6 +366,9 @@ int zclient_bfd_command(struct zclient *zc, struct bfd_session_arg *args) if (args->ifnamelen) stream_put(s, args->ifname, args->ifnamelen); } + + /* Send the C bit indicator. */ + stream_putc(s, args->cbit); #endif /* HAVE_BFDD */ /* Finish the message by writing the size. */ diff --git a/lib/command.c b/lib/command.c index 422544b70b..fcaf466c65 100644 --- a/lib/command.c +++ b/lib/command.c @@ -852,96 +852,13 @@ char **cmd_complete_command(vector vline, struct vty *vty, int *status) /* MUST eventually converge on CONFIG_NODE */ enum node_type node_parent(enum node_type node) { - enum node_type ret; + struct cmd_node *cnode; assert(node > CONFIG_NODE); - switch (node) { - case BGP_VPNV4_NODE: - case BGP_VPNV6_NODE: - case BGP_FLOWSPECV4_NODE: - case BGP_FLOWSPECV6_NODE: - case BGP_VRF_POLICY_NODE: - case BGP_VNC_DEFAULTS_NODE: - case BGP_VNC_NVE_GROUP_NODE: - case BGP_VNC_L2_GROUP_NODE: - case BGP_IPV4_NODE: - case BGP_IPV4M_NODE: - case BGP_IPV4L_NODE: - case BGP_IPV6_NODE: - case BGP_IPV6M_NODE: - case BGP_EVPN_NODE: - case BGP_IPV6L_NODE: - case BMP_NODE: - ret = BGP_NODE; - break; - case BGP_EVPN_VNI_NODE: - ret = BGP_EVPN_NODE; - break; - case KEYCHAIN_KEY_NODE: - ret = KEYCHAIN_NODE; - break; - case LINK_PARAMS_NODE: - ret = INTERFACE_NODE; - break; - case LDP_IPV4_NODE: - case LDP_IPV6_NODE: - ret = LDP_NODE; - break; - case LDP_IPV4_IFACE_NODE: - ret = LDP_IPV4_NODE; - break; - case LDP_IPV6_IFACE_NODE: - ret = LDP_IPV6_NODE; - break; - case LDP_PSEUDOWIRE_NODE: - ret = LDP_L2VPN_NODE; - break; - case BFD_PEER_NODE: - ret = BFD_NODE; - break; - case BFD_PROFILE_NODE: - ret = BFD_NODE; - break; - case SR_TRAFFIC_ENG_NODE: - ret = SEGMENT_ROUTING_NODE; - break; - case SR_SEGMENT_LIST_NODE: - ret = SR_TRAFFIC_ENG_NODE; - break; - case SR_POLICY_NODE: - ret = SR_TRAFFIC_ENG_NODE; - break; - case SR_CANDIDATE_DYN_NODE: - ret = SR_POLICY_NODE; - break; - case PCEP_NODE: - ret = SR_TRAFFIC_ENG_NODE; - break; - case PCEP_PCE_CONFIG_NODE: - ret = PCEP_NODE; - break; - case PCEP_PCE_NODE: - ret = PCEP_NODE; - break; - case PCEP_PCC_NODE: - ret = PCEP_NODE; - break; - case SRV6_NODE: - ret = SEGMENT_ROUTING_NODE; - break; - case SRV6_LOCS_NODE: - ret = SRV6_NODE; - break; - case SRV6_LOC_NODE: - ret = SRV6_LOCS_NODE; - break; - default: - ret = CONFIG_NODE; - break; - } + cnode = vector_lookup(cmdvec, node); - return ret; + return cnode->parent_node; } /* Execute command by argument vline vector. */ diff --git a/lib/command.h b/lib/command.h index 2b50bc2374..c76fc1e8eb 100644 --- a/lib/command.h +++ b/lib/command.h @@ -389,6 +389,7 @@ struct cmd_node { #define SRTE_STR "SR-TE information\n" #define SRTE_COLOR_STR "SR-TE Color information\n" #define NO_STR "Negate a command or set its defaults\n" +#define IGNORED_IN_NO_STR "Ignored value in no form\n" #define REDIST_STR "Redistribute information from another routing protocol\n" #define CLEAR_STR "Reset functions\n" #define RIP_STR "RIP information\n" diff --git a/lib/filter.h b/lib/filter.h index 941fabd38b..d1956ec019 100644 --- a/lib/filter.h +++ b/lib/filter.h @@ -207,11 +207,10 @@ struct plist_dup_args { /** Entry action. */ const char *pda_action; -#define PDA_MAX_VALUES 4 - /** Entry XPath for value. */ - const char *pda_xpath[PDA_MAX_VALUES]; - /** Entry value to match. */ - const char *pda_value[PDA_MAX_VALUES]; + bool any; + struct prefix prefix; + int ge; + int le; /** Duplicated entry found in list? */ bool pda_found; diff --git a/lib/filter_cli.c b/lib/filter_cli.c index f030ce1b33..45c7544a3b 100644 --- a/lib/filter_cli.c +++ b/lib/filter_cli.c @@ -1196,11 +1196,9 @@ static int plist_remove_if_empty(struct vty *vty, const char *iptype, static int plist_remove(struct vty *vty, const char *iptype, const char *name, const char *seq, const char *action, - const char *prefix_str, const char *ge_str, - const char *le_str) + union prefixconstptr prefix, int ge, int le) { int64_t sseq; - int arg_idx = 0; struct plist_dup_args pda = {}; char xpath[XPATH_MAXLEN]; char xpath_entry[XPATH_MAXLEN + 32]; @@ -1225,43 +1223,13 @@ static int plist_remove(struct vty *vty, const char *iptype, const char *name, pda.pda_type = iptype; pda.pda_name = name; pda.pda_action = action; - if (prefix_str) { - if (strmatch(iptype, "ipv4")) { - pda.pda_xpath[arg_idx] = "./ipv4-prefix"; - pda.pda_value[arg_idx] = prefix_str; - arg_idx++; - if (ge_str) { - pda.pda_xpath[arg_idx] = - "./ipv4-prefix-length-greater-or-equal"; - pda.pda_value[arg_idx] = ge_str; - arg_idx++; - } - if (le_str) { - pda.pda_xpath[arg_idx] = - "./ipv4-prefix-length-lesser-or-equal"; - pda.pda_value[arg_idx] = le_str; - arg_idx++; - } - } else { - pda.pda_xpath[arg_idx] = "./ipv6-prefix"; - pda.pda_value[arg_idx] = prefix_str; - arg_idx++; - if (ge_str) { - pda.pda_xpath[arg_idx] = - "./ipv6-prefix-length-greater-or-equal"; - pda.pda_value[arg_idx] = ge_str; - arg_idx++; - } - if (le_str) { - pda.pda_xpath[arg_idx] = - "./ipv6-prefix-length-lesser-or-equal"; - pda.pda_value[arg_idx] = le_str; - arg_idx++; - } - } + if (prefix.p) { + prefix_copy(&pda.prefix, prefix); + apply_mask(&pda.prefix); + pda.ge = ge; + pda.le = le; } else { - pda.pda_xpath[0] = "./any"; - pda.pda_value[0] = ""; + pda.any = true; } if (plist_is_dup(vty->candidate_config->dnode, &pda)) @@ -1298,7 +1266,6 @@ DEFPY_YANG( "Maximum prefix length\n") { int64_t sseq; - int arg_idx = 0; struct plist_dup_args pda = {}; char xpath[XPATH_MAXLEN]; char xpath_entry[XPATH_MAXLEN + 128]; @@ -1312,24 +1279,11 @@ DEFPY_YANG( pda.pda_name = name; pda.pda_action = action; if (prefix_str) { - pda.pda_xpath[arg_idx] = "./ipv4-prefix"; - pda.pda_value[arg_idx] = prefix_str; - arg_idx++; - if (ge_str) { - pda.pda_xpath[arg_idx] = - "./ipv4-prefix-length-greater-or-equal"; - pda.pda_value[arg_idx] = ge_str; - arg_idx++; - } - if (le_str) { - pda.pda_xpath[arg_idx] = - "./ipv4-prefix-length-lesser-or-equal"; - pda.pda_value[arg_idx] = le_str; - arg_idx++; - } + prefix_copy(&pda.prefix, prefix); + pda.ge = ge; + pda.le = le; } else { - pda.pda_xpath[0] = "./any"; - pda.pda_value[0] = ""; + pda.any = true; } /* Duplicated entry without sequence, just quit. */ @@ -1408,8 +1362,8 @@ DEFPY_YANG( "Maximum prefix length to be matched\n" "Maximum prefix length\n") { - return plist_remove(vty, "ipv4", name, seq_str, action, prefix_str, - ge_str, le_str); + return plist_remove(vty, "ipv4", name, seq_str, action, + prefix_str ? prefix : NULL, ge, le); } DEFPY_YANG( @@ -1421,7 +1375,7 @@ DEFPY_YANG( PREFIX_LIST_NAME_STR ACCESS_LIST_SEQ_STR) { - return plist_remove(vty, "ipv4", name, seq_str, NULL, NULL, NULL, NULL); + return plist_remove(vty, "ipv4", name, seq_str, NULL, NULL, 0, 0); } DEFPY_YANG( @@ -1516,7 +1470,6 @@ DEFPY_YANG( "Minimum prefix length\n") { int64_t sseq; - int arg_idx = 0; struct plist_dup_args pda = {}; char xpath[XPATH_MAXLEN]; char xpath_entry[XPATH_MAXLEN + 128]; @@ -1530,24 +1483,11 @@ DEFPY_YANG( pda.pda_name = name; pda.pda_action = action; if (prefix_str) { - pda.pda_xpath[arg_idx] = "./ipv6-prefix"; - pda.pda_value[arg_idx] = prefix_str; - arg_idx++; - if (ge_str) { - pda.pda_xpath[arg_idx] = - "./ipv6-prefix-length-greater-or-equal"; - pda.pda_value[arg_idx] = ge_str; - arg_idx++; - } - if (le_str) { - pda.pda_xpath[arg_idx] = - "./ipv6-prefix-length-lesser-or-equal"; - pda.pda_value[arg_idx] = le_str; - arg_idx++; - } + prefix_copy(&pda.prefix, prefix); + pda.ge = ge; + pda.le = le; } else { - pda.pda_xpath[0] = "./any"; - pda.pda_value[0] = ""; + pda.any = true; } /* Duplicated entry without sequence, just quit. */ @@ -1626,8 +1566,8 @@ DEFPY_YANG( "Minimum prefix length to be matched\n" "Minimum prefix length\n") { - return plist_remove(vty, "ipv6", name, seq_str, action, prefix_str, - ge_str, le_str); + return plist_remove(vty, "ipv6", name, seq_str, action, + prefix_str ? prefix : NULL, ge, le); } DEFPY_YANG( @@ -1639,7 +1579,7 @@ DEFPY_YANG( PREFIX_LIST_NAME_STR ACCESS_LIST_SEQ_STR) { - return plist_remove(vty, "ipv6", name, seq_str, NULL, NULL, NULL, NULL); + return plist_remove(vty, "ipv6", name, seq_str, NULL, NULL, 0, 0); } DEFPY_YANG( diff --git a/lib/filter_nb.c b/lib/filter_nb.c index 85805ffa47..80ea7a57cb 100644 --- a/lib/filter_nb.c +++ b/lib/filter_nb.c @@ -387,10 +387,50 @@ static bool acl_zebra_is_dup(const struct lyd_node *dnode, return acl_is_dup(entry_dnode, &ada); } +static void plist_dnode_to_prefix(const struct lyd_node *dnode, bool *any, + struct prefix *p, int *ge, int *le) +{ + *any = false; + *ge = 0; + *le = 0; + + if (yang_dnode_exists(dnode, "./any")) { + *any = true; + return; + } + + switch (yang_dnode_get_enum(dnode, "../type")) { + case YPLT_IPV4: + yang_dnode_get_prefix(p, dnode, "./ipv4-prefix"); + if (yang_dnode_exists(dnode, + "./ipv4-prefix-length-greater-or-equal")) + *ge = yang_dnode_get_uint8( + dnode, "./ipv4-prefix-length-greater-or-equal"); + if (yang_dnode_exists(dnode, + "./ipv4-prefix-length-lesser-or-equal")) + *le = yang_dnode_get_uint8( + dnode, "./ipv4-prefix-length-lesser-or-equal"); + break; + case YPLT_IPV6: + yang_dnode_get_prefix(p, dnode, "./ipv6-prefix"); + if (yang_dnode_exists(dnode, + "./ipv6-prefix-length-greater-or-equal")) + *ge = yang_dnode_get_uint8( + dnode, "./ipv6-prefix-length-greater-or-equal"); + if (yang_dnode_exists(dnode, + "./ipv6-prefix-length-lesser-or-equal")) + *le = yang_dnode_get_uint8( + dnode, "./ipv6-prefix-length-lesser-or-equal"); + break; + } +} + static int _plist_is_dup(const struct lyd_node *dnode, void *arg) { struct plist_dup_args *pda = arg; - int idx; + struct prefix p; + int ge, le; + bool any; /* This entry is the caller, so skip it. */ if (pda->pda_entry_dnode @@ -400,19 +440,14 @@ static int _plist_is_dup(const struct lyd_node *dnode, void *arg) if (strcmp(yang_dnode_get_string(dnode, "action"), pda->pda_action)) return YANG_ITER_CONTINUE; - /* Check if all values match. */ - for (idx = 0; idx < PDA_MAX_VALUES; idx++) { - /* No more values. */ - if (pda->pda_xpath[idx] == NULL) - break; + plist_dnode_to_prefix(dnode, &any, &p, &ge, &le); - /* Not same type, just skip it. */ - if (!yang_dnode_exists(dnode, pda->pda_xpath[idx])) + if (pda->any) { + if (!any) return YANG_ITER_CONTINUE; - - /* Check if different value. */ - if (strcmp(yang_dnode_get_string(dnode, pda->pda_xpath[idx]), - pda->pda_value[idx])) + } else { + if (!prefix_same(&pda->prefix, &p) || pda->ge != ge + || pda->le != le) return YANG_ITER_CONTINUE; } @@ -439,17 +474,6 @@ static bool plist_is_dup_nb(const struct lyd_node *dnode) const struct lyd_node *entry_dnode = yang_dnode_get_parent(dnode, "entry"); struct plist_dup_args pda = {}; - int idx = 0, arg_idx = 0; - static const char *entries[] = { - "./ipv4-prefix", - "./ipv4-prefix-length-greater-or-equal", - "./ipv4-prefix-length-lesser-or-equal", - "./ipv6-prefix", - "./ipv6-prefix-length-greater-or-equal", - "./ipv6-prefix-length-lesser-or-equal", - "./any", - NULL - }; /* Initialize. */ pda.pda_type = yang_dnode_get_string(entry_dnode, "../type"); @@ -457,19 +481,8 @@ static bool plist_is_dup_nb(const struct lyd_node *dnode) pda.pda_action = yang_dnode_get_string(entry_dnode, "action"); pda.pda_entry_dnode = entry_dnode; - /* Load all values/XPaths. */ - while (entries[idx] != NULL) { - if (!yang_dnode_exists(entry_dnode, entries[idx])) { - idx++; - continue; - } - - pda.pda_xpath[arg_idx] = entries[idx]; - pda.pda_value[arg_idx] = - yang_dnode_get_string(entry_dnode, entries[idx]); - arg_idx++; - idx++; - } + plist_dnode_to_prefix(entry_dnode, &pda.any, &pda.prefix, &pda.ge, + &pda.le); return plist_is_dup(entry_dnode, &pda); } diff --git a/lib/frrlua.c b/lib/frrlua.c index 96d7269440..00491568f6 100644 --- a/lib/frrlua.c +++ b/lib/frrlua.c @@ -240,7 +240,9 @@ void lua_pushsockunion(lua_State *L, const union sockunion *su) void lua_decode_sockunion(lua_State *L, int idx, union sockunion *su) { lua_getfield(L, idx, "string"); - str2sockunion(lua_tostring(L, -1), su); + if (str2sockunion(lua_tostring(L, -1), su) < 0) + zlog_err("Lua hook call: Failed to decode sockunion"); + lua_pop(L, 1); /* pop the table */ lua_pop(L, 1); diff --git a/lib/frrscript.c b/lib/frrscript.c index d00b84ccbb..0e0d3c030c 100644 --- a/lib/frrscript.c +++ b/lib/frrscript.c @@ -281,48 +281,23 @@ int frrscript_load(struct frrscript *fs, const char *function_name, scriptdir, fs->name); goto fail; } - int ret = luaL_dofile(L, script_name); - switch (ret) { - case LUA_OK: - break; - case LUA_ERRSYNTAX: - zlog_err( - "frrscript: failed loading script '%s.lua': syntax error: %s", - script_name, lua_tostring(L, -1)); - break; - case LUA_ERRMEM: - zlog_err( - "frrscript: failed loading script '%s.lua': out-of-memory error: %s", - script_name, lua_tostring(L, -1)); - break; - case LUA_ERRGCMM: - zlog_err( - "frrscript: failed loading script '%s.lua': garbage collector error: %s", - script_name, lua_tostring(L, -1)); - break; - case LUA_ERRFILE: - zlog_err( - "frrscript: failed loading script '%s.lua': file read error: %s", - script_name, lua_tostring(L, -1)); - break; - default: - zlog_err( - "frrscript: failed loading script '%s.lua': unknown error: %s", - script_name, lua_tostring(L, -1)); - break; - } - - if (ret != LUA_OK) + if (luaL_dofile(L, script_name) != 0) { + zlog_err("frrscript: failed loading script '%s.lua': error: %s", + script_name, lua_tostring(L, -1)); goto fail; + } - /* Push the Lua function we want */ + /* To check the Lua function, we get it from the global table */ lua_getglobal(L, function_name); if (lua_isfunction(L, lua_gettop(L)) == 0) { zlog_err("frrscript: loaded script '%s.lua' but %s not found", script_name, function_name); goto fail; } + /* Then pop the function (frrscript_call will push it when it needs it) + */ + lua_pop(L, 1); if (load_cb && (*load_cb)(fs) != 0) { zlog_err( diff --git a/lib/frrscript.h b/lib/frrscript.h index 540676c099..c089df61fc 100644 --- a/lib/frrscript.h +++ b/lib/frrscript.h @@ -203,8 +203,18 @@ const struct prefix * : lua_decode_noop \ int _frrscript_call_lua(struct lua_function_state *lfs, int nargs); /* - * Wrapper for calling Lua function state. Maps values passed in to their - * encoder and decoder types. + * Wrapper for calling Lua function state. + * + * The Lua function name (f) to run should have already been checked by + * frrscript_load. So this wrapper will: + * 1) Find the Lua function state, which contains the Lua state + * 2) Clear the Lua state (there may be leftovers items from previous call) + * 3) Push the Lua function (f) + * 4) Map frrscript_call arguments onto their encoder and decoders, push those + * 5) Call _frrscript_call_lua (Lua execution takes place) + * 6) Write back to frrscript_call arguments using their decoders + * + * This wrapper can be called multiple times (after one frrscript_load). * * fs * The struct frrscript in which the Lua fuction was loaded into @@ -226,6 +236,8 @@ int _frrscript_call_lua(struct lua_function_state *lfs, int nargs); 1; \ }) \ : ({ \ + lua_settop(lfs->L, 0); \ + lua_getglobal(lfs->L, f); \ MAP_LISTS(ENCODE_ARGS, ##__VA_ARGS__); \ _frrscript_call_lua( \ lfs, PP_NARG(__VA_ARGS__)); \ diff --git a/lib/frrstr.c b/lib/frrstr.c index 7ef5fffd12..1b98b224cc 100644 --- a/lib/frrstr.c +++ b/lib/frrstr.c @@ -18,9 +18,7 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif +#include "zebra.h" #include <string.h> #include <ctype.h> @@ -217,3 +215,21 @@ int all_digit(const char *str) return 0; return 1; } + + +char *frrstr_hex(char *buff, size_t bufsiz, const uint8_t *str, size_t num) +{ + if (bufsiz == 0) + return buff; + + char tmp[3]; + + buff[0] = '\0'; + + for (size_t i = 0; i < num; i++) { + snprintf(tmp, sizeof(tmp), "%02x", (unsigned char)str[i]); + strlcat(buff, tmp, bufsiz); + } + + return buff; +} diff --git a/lib/frrstr.h b/lib/frrstr.h index 441d7b8670..d52d6a4482 100644 --- a/lib/frrstr.h +++ b/lib/frrstr.h @@ -154,6 +154,26 @@ bool frrstr_endswith(const char *str, const char *suffix); */ int all_digit(const char *str); +/* + * Copy the hexadecimal representation of the string to a buffer. + * + * buff + * Buffer to copy result into with size of at least (2 * num) + 1. + * + * bufsiz + * Size of destination buffer. + * + * str + * String to represent as hexadecimal. + * + * num + * Number of characters to copy. + * + * Returns: + * Pointer to buffer containing resulting hexadecimal representation. + */ +char *frrstr_hex(char *buff, size_t bufsiz, const uint8_t *str, size_t num); + #ifdef __cplusplus } #endif @@ -1291,6 +1291,11 @@ static void cli_show_interface(struct vty *vty, struct lyd_node *dnode, vty_out(vty, "\n"); } +static void cli_show_interface_end(struct vty *vty, struct lyd_node *dnode) +{ + vty_out(vty, "exit\n"); +} + /* * XPath: /frr-interface:lib/interface/description */ @@ -1652,6 +1657,7 @@ const struct frr_yang_module_info frr_interface_info = { .create = lib_interface_create, .destroy = lib_interface_destroy, .cli_show = cli_show_interface, + .cli_show_end = cli_show_interface_end, .get_next = lib_interface_get_next, .get_keys = lib_interface_get_keys, .lookup_entry = lib_interface_lookup_entry, diff --git a/lib/keychain.c b/lib/keychain.c index db5c23b1ba..02f83ef0a8 100644 --- a/lib/keychain.c +++ b/lib/keychain.c @@ -1044,6 +1044,7 @@ static int keychain_config_write(struct vty *vty) vty_out(vty, " exit\n"); } + vty_out(vty, "exit\n"); vty_out(vty, "!\n"); } diff --git a/lib/libfrr.c b/lib/libfrr.c index 97dab74d9b..d03437328b 100644 --- a/lib/libfrr.c +++ b/lib/libfrr.c @@ -418,7 +418,6 @@ static int frr_opt(int opt) switch (opt) { case 'h': frr_help_exit(0); - break; case 'v': print_version(di->progname); exit(0); diff --git a/lib/nexthop_group.c b/lib/nexthop_group.c index 4fee9bde3c..97d70189ff 100644 --- a/lib/nexthop_group.c +++ b/lib/nexthop_group.c @@ -1156,6 +1156,7 @@ static int nexthop_group_write(struct vty *vty) nexthop_group_write_nexthop_internal(vty, nh); } + vty_out(vty, "exit\n"); vty_out(vty, "!\n"); } diff --git a/lib/route_opaque.h b/lib/route_opaque.h index 599a0363eb..7c4e9a16e1 100644 --- a/lib/route_opaque.h +++ b/lib/route_opaque.h @@ -21,12 +21,15 @@ #ifndef FRR_ROUTE_OPAQUE_H #define FRR_ROUTE_OPAQUE_H +#include "assert.h" +#include "zclient.h" + #include "bgpd/bgp_aspath.h" #include "bgpd/bgp_community.h" #include "bgpd/bgp_lcommunity.h" struct bgp_zebra_opaque { - char aspath[ASPATH_STR_DEFAULT_LEN]; + char aspath[256]; /* Show at least 10 communities AA:BB */ char community[COMMUNITY_SIZE * 20]; @@ -35,4 +38,7 @@ struct bgp_zebra_opaque { char lcommunity[LCOMMUNITY_SIZE * 30]; }; +static_assert(sizeof(struct bgp_zebra_opaque) <= ZAPI_MESSAGE_OPAQUE_LENGTH, + "BGP opaque data shouldn't be larger than zebra's buffer"); + #endif /* FRR_ROUTE_OPAQUE_H */ diff --git a/lib/routemap.c b/lib/routemap.c index 5d45dc1047..594dcf97cb 100644 --- a/lib/routemap.c +++ b/lib/routemap.c @@ -1431,7 +1431,7 @@ enum rmap_compile_rets route_map_add_match(struct route_map_index *index, * the same as the existing configuration then, * ignore the duplicate configuration. */ - if (strcmp(match_arg, rule->rule_str) == 0) { + if (rulecmp(match_arg, rule->rule_str) == 0) { if (cmd->func_free) (*cmd->func_free)(compile); diff --git a/lib/routemap.h b/lib/routemap.h index 8af3b2c3c0..1fbd142ad2 100644 --- a/lib/routemap.h +++ b/lib/routemap.h @@ -354,6 +354,8 @@ DECLARE_QOBJ_TYPE(route_map); (strmatch(A, "frr-bgp-route-map:set-extcommunity-rt")) #define IS_SET_EXTCOMMUNITY_SOO(A) \ (strmatch(A, "frr-bgp-route-map:set-extcommunity-soo")) +#define IS_SET_EXTCOMMUNITY_LB(A) \ + (strmatch(A, "frr-bgp-route-map:set-extcommunity-lb")) #define IS_SET_AGGREGATOR(A) \ (strmatch(A, "frr-bgp-route-map:aggregator")) #define IS_SET_AS_PREPEND(A) \ @@ -377,6 +379,12 @@ DECLARE_QOBJ_TYPE(route_map); #define IS_SET_BGP_EVPN_GATEWAY_IP_IPV6(A) \ (strmatch(A, "frr-bgp-route-map:set-evpn-gateway-ip-ipv6")) +enum ecommunity_lb_type { + EXPLICIT_BANDWIDTH, + CUMULATIVE_BANDWIDTH, + COMPUTED_BANDWIDTH +}; + /* Prototypes. */ extern void route_map_init(void); diff --git a/lib/routemap_cli.c b/lib/routemap_cli.c index ec9033b3aa..045a8cddd8 100644 --- a/lib/routemap_cli.c +++ b/lib/routemap_cli.c @@ -124,6 +124,7 @@ void route_map_instance_show(struct vty *vty, struct lyd_node *dnode, void route_map_instance_show_end(struct vty *vty, struct lyd_node *dnode) { + vty_out(vty, "exit\n"); vty_out(vty, "!\n"); } @@ -1194,6 +1195,34 @@ void route_map_action_show(struct vty *vty, struct lyd_node *dnode, yang_dnode_get_string( dnode, "./rmap-set-action/frr-bgp-route-map:extcommunity-soo")); + } else if (IS_SET_EXTCOMMUNITY_LB(action)) { + enum ecommunity_lb_type lb_type; + char str[VTY_BUFSIZ]; + uint16_t bandwidth; + + lb_type = yang_dnode_get_enum( + dnode, + "./rmap-set-action/frr-bgp-route-map:extcommunity-lb/lb-type"); + switch (lb_type) { + case EXPLICIT_BANDWIDTH: + bandwidth = yang_dnode_get_uint16( + dnode, + "./rmap-set-action/frr-bgp-route-map:extcommunity-lb/bandwidth"); + snprintf(str, sizeof(str), "%d", bandwidth); + break; + case CUMULATIVE_BANDWIDTH: + snprintf(str, sizeof(str), "%s", "cumulative"); + break; + case COMPUTED_BANDWIDTH: + snprintf(str, sizeof(str), "%s", "num-multipaths"); + } + + if (yang_dnode_get_bool( + dnode, + "./rmap-set-action/frr-bgp-route-map:extcommunity-lb/two-octet-as-specific")) + strlcat(str, " non-transitive", sizeof(str)); + + vty_out(vty, " set extcommunity bandwidth %s\n", str); } else if (IS_SET_AGGREGATOR(action)) { vty_out(vty, " set aggregator as %s %s\n", yang_dnode_get_string( diff --git a/lib/subdir.am b/lib/subdir.am index 714af43238..dab5fb9e83 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -144,7 +144,6 @@ vtysh_scan += \ lib/log_vty.c \ lib/nexthop_group.c \ lib/plist.c \ - lib/resolver.c \ lib/routemap.c \ lib/routemap_cli.c \ lib/spf_backoff.c \ @@ -335,6 +334,7 @@ lib_libfrrsnmp_la_SOURCES = \ if CARES lib_LTLIBRARIES += lib/libfrrcares.la pkginclude_HEADERS += lib/resolver.h +vtysh_scan += lib/resolver.c endif lib_libfrrcares_la_CFLAGS = $(AM_CFLAGS) $(CARES_CFLAGS) @@ -58,7 +58,6 @@ struct vrf_name_head vrfs_by_name = RB_INITIALIZER(&vrfs_by_name); static int vrf_backend; static int vrf_backend_configured; -static struct zebra_privs_t *vrf_daemon_privs; static char vrf_default_name[VRF_NAMSIZ] = VRF_DEFAULT_NAME_INTERNAL; /* @@ -856,62 +855,6 @@ static struct cmd_node vrf_node = { .prompt = "%s(config-vrf)# ", }; -DEFUN_NOSH (vrf_netns, - vrf_netns_cmd, - "netns NAME", - "Attach VRF to a Namespace\n" - "The file name in " NS_RUN_DIR ", or a full pathname\n") -{ - int idx_name = 1, ret; - char *pathname = ns_netns_pathname(vty, argv[idx_name]->arg); - - VTY_DECLVAR_CONTEXT(vrf, vrf); - - if (!pathname) - return CMD_WARNING_CONFIG_FAILED; - - frr_with_privs(vrf_daemon_privs) { - ret = vrf_netns_handler_create(vty, vrf, pathname, - NS_UNKNOWN, - NS_UNKNOWN, - NS_UNKNOWN); - } - return ret; -} - -DEFUN_NOSH (no_vrf_netns, - no_vrf_netns_cmd, - "no netns [NAME]", - NO_STR - "Detach VRF from a Namespace\n" - "The file name in " NS_RUN_DIR ", or a full pathname\n") -{ - struct ns *ns = NULL; - - VTY_DECLVAR_CONTEXT(vrf, vrf); - - if (!vrf_is_backend_netns()) { - vty_out(vty, "VRF backend is not Netns. Aborting\n"); - return CMD_WARNING_CONFIG_FAILED; - } - if (!vrf->ns_ctxt) { - vty_out(vty, "VRF %s(%u) is not configured with NetNS\n", - vrf->name, vrf->vrf_id); - return CMD_WARNING_CONFIG_FAILED; - } - - ns = (struct ns *)vrf->ns_ctxt; - - ns->vrf_ctxt = NULL; - vrf_disable(vrf); - /* vrf ID from VRF is necessary for Zebra - * so that propagate to other clients is done - */ - ns_delete(ns); - vrf->ns_ctxt = NULL; - return CMD_SUCCESS; -} - /* * Debug CLI for vrf's */ @@ -964,8 +907,7 @@ void vrf_install_commands(void) install_element(ENABLE_NODE, &no_vrf_debug_cmd); } -void vrf_cmd_init(int (*writefunc)(struct vty *vty), - struct zebra_privs_t *daemon_privs) +void vrf_cmd_init(int (*writefunc)(struct vty *vty)) { install_element(CONFIG_NODE, &vrf_cmd); install_element(CONFIG_NODE, &no_vrf_cmd); @@ -973,12 +915,6 @@ void vrf_cmd_init(int (*writefunc)(struct vty *vty), install_node(&vrf_node); install_default(VRF_NODE); install_element(VRF_NODE, &vrf_exit_cmd); - if (vrf_is_backend_netns() && ns_have_netns()) { - /* Install NS commands. */ - vrf_daemon_privs = daemon_privs; - install_element(VRF_NODE, &vrf_netns_cmd); - install_element(VRF_NODE, &no_vrf_netns_cmd); - } } void vrf_set_default_name(const char *default_name, bool force) @@ -285,8 +285,7 @@ extern int vrf_switchback_to_initial(void); /* VRF vty command initialisation */ -extern void vrf_cmd_init(int (*writefunc)(struct vty *vty), - struct zebra_privs_t *daemon_priv); +extern void vrf_cmd_init(int (*writefunc)(struct vty *vty)); /* VRF vty debugging */ @@ -3040,7 +3040,7 @@ DEFPY (log_commands, /* Display current configuration. */ static int vty_config_write(struct vty *vty) { - vty_out(vty, "line vty\n"); + vty_frame(vty, "line vty\n"); if (vty_accesslist_name) vty_out(vty, " access-class %s\n", vty_accesslist_name); @@ -3058,6 +3058,8 @@ static int vty_config_write(struct vty *vty) if (no_password_check) vty_out(vty, " no login\n"); + vty_endframe(vty, "exit\n"); + if (do_log_commands) vty_out(vty, "log commands\n"); diff --git a/lib/zclient.c b/lib/zclient.c index 0815e77d4e..a1e7194890 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -1299,7 +1299,13 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api) stream_putl(s, api->tableid); if (CHECK_FLAG(api->message, ZAPI_MESSAGE_OPAQUE)) { - assert(api->opaque.length <= ZAPI_MESSAGE_OPAQUE_LENGTH); + if (api->opaque.length > ZAPI_MESSAGE_OPAQUE_LENGTH) { + flog_err( + EC_LIB_ZAPI_ENCODE, + "%s: opaque length %u is greater than allowed value", + __func__, api->opaque.length); + return -1; + } stream_putw(s, api->opaque.length); stream_write(s, api->opaque.data, api->opaque.length); @@ -1537,7 +1543,13 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api) if (CHECK_FLAG(api->message, ZAPI_MESSAGE_OPAQUE)) { STREAM_GETW(s, api->opaque.length); - assert(api->opaque.length <= ZAPI_MESSAGE_OPAQUE_LENGTH); + if (api->opaque.length > ZAPI_MESSAGE_OPAQUE_LENGTH) { + flog_err( + EC_LIB_ZAPI_ENCODE, + "%s: opaque length %u is greater than allowed value", + __func__, api->opaque.length); + return -1; + } STREAM_GET(api->opaque.data, s, api->opaque.length); } @@ -3430,6 +3442,14 @@ int zapi_labels_decode(struct stream *s, struct zapi_labels *zl) if (zapi_nexthop_decode(s, znh, 0, 0) < 0) return -1; + + if (znh->type == NEXTHOP_TYPE_BLACKHOLE) { + flog_warn( + EC_LIB_ZAPI_ENCODE, + "%s: Prefix %pFX has a blackhole nexthop which we cannot use for a label", + __func__, &zl->route.prefix); + return -1; + } } if (CHECK_FLAG(zl->message, ZAPI_LABELS_HAS_BACKUPS)) { @@ -3451,6 +3471,14 @@ int zapi_labels_decode(struct stream *s, struct zapi_labels *zl) if (zapi_nexthop_decode(s, znh, 0, 0) < 0) return -1; + + if (znh->type == NEXTHOP_TYPE_BLACKHOLE) { + flog_warn( + EC_LIB_ZAPI_ENCODE, + "%s: Prefix %pFX has a backup blackhole nexthop which we cannot use for a label", + __func__, &zl->route.prefix); + return -1; + } } } @@ -4459,11 +4487,9 @@ static int zclient_neigh_ip_read_entry(struct stream *s, struct ipaddr *add) return -1; } -int zclient_neigh_ip_encode(struct stream *s, - uint16_t cmd, - union sockunion *in, - union sockunion *out, - struct interface *ifp) +int zclient_neigh_ip_encode(struct stream *s, uint16_t cmd, union sockunion *in, + union sockunion *out, struct interface *ifp, + int ndm_state) { int ret = 0; @@ -4478,7 +4504,7 @@ int zclient_neigh_ip_encode(struct stream *s, stream_putc(s, AF_UNSPEC); stream_putl(s, ifp->ifindex); if (out) - stream_putl(s, ZEBRA_NEIGH_STATE_REACHABLE); + stream_putl(s, ndm_state); else stream_putl(s, ZEBRA_NEIGH_STATE_FAILED); return ret; diff --git a/lib/zclient.h b/lib/zclient.h index a25c5800b7..71187ccae7 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -856,9 +856,18 @@ extern struct zclient_options zclient_options_default; * ip_in is the underlay IP, ip_out is the tunnel dest * index stands for the index of the interface * ndm state stands for the NDM value in netlink + * (see linux/neighbour.h) */ +#define ZEBRA_NEIGH_STATE_INCOMPLETE (0x01) #define ZEBRA_NEIGH_STATE_REACHABLE (0x02) -#define ZEBRA_NEIGH_STATE_FAILED (0x20) +#define ZEBRA_NEIGH_STATE_STALE (0x04) +#define ZEBRA_NEIGH_STATE_DELAY (0x08) +#define ZEBRA_NEIGH_STATE_PROBE (0x10) +#define ZEBRA_NEIGH_STATE_FAILED (0x20) +#define ZEBRA_NEIGH_STATE_NOARP (0x40) +#define ZEBRA_NEIGH_STATE_PERMANENT (0x80) +#define ZEBRA_NEIGH_STATE_NONE (0x00) + struct zapi_neigh_ip { int cmd; struct ipaddr ip_in; @@ -867,11 +876,9 @@ struct zapi_neigh_ip { uint32_t ndm_state; }; int zclient_neigh_ip_decode(struct stream *s, struct zapi_neigh_ip *api); -int zclient_neigh_ip_encode(struct stream *s, - uint16_t cmd, - union sockunion *in, - union sockunion *out, - struct interface *ifp); +int zclient_neigh_ip_encode(struct stream *s, uint16_t cmd, union sockunion *in, + union sockunion *out, struct interface *ifp, + int ndm_state); /* * We reserve the top 4 bits for l2-NHG, everything else diff --git a/nhrpd/nhrp_main.c b/nhrpd/nhrp_main.c index 54b7850207..73684046a8 100644 --- a/nhrpd/nhrp_main.c +++ b/nhrpd/nhrp_main.c @@ -71,7 +71,6 @@ static void parse_arguments(int argc, char **argv) break; default: frr_help_exit(1); - break; } } } diff --git a/nhrpd/nhrp_route.c b/nhrpd/nhrp_route.c index ee8db277d9..12a2fc2fa0 100644 --- a/nhrpd/nhrp_route.c +++ b/nhrpd/nhrp_route.c @@ -452,7 +452,8 @@ void nhrp_send_zebra_nbr(union sockunion *in, stream_reset(s); zclient_neigh_ip_encode(s, out ? ZEBRA_NEIGH_IP_ADD : ZEBRA_NEIGH_IP_DEL, in, out, - ifp); + ifp, out ? ZEBRA_NEIGH_STATE_REACHABLE + : ZEBRA_NEIGH_STATE_FAILED); stream_putw_at(s, 0, stream_get_endp(s)); zclient_send_message(zclient); } diff --git a/nhrpd/nhrp_shortcut.c b/nhrpd/nhrp_shortcut.c index 56861551ea..0905ceb72a 100644 --- a/nhrpd/nhrp_shortcut.c +++ b/nhrpd/nhrp_shortcut.c @@ -427,8 +427,10 @@ static void nhrp_shortcut_send_resolution_req(struct nhrp_shortcut *s) * */ /* FIXME: push CIE for each local protocol address */ cie = nhrp_cie_push(zb, NHRP_CODE_SUCCESS, NULL, NULL); - cie->prefix_length = 0xff; if_ad = &nifp->afi[family2afi(sockunion_family(&s->addr))]; + cie->prefix_length = (if_ad->flags & NHRP_IFF_REG_NO_UNIQUE) + ? 8 * sockunion_get_addrlen(&s->addr) + : 0xff; cie->holding_time = htons(if_ad->holdtime); cie->mtu = htons(if_ad->mtu); debugf(NHRP_DEBUG_COMMON, diff --git a/nhrpd/nhrp_vty.c b/nhrpd/nhrp_vty.c index 8441d278fc..50161dae2f 100644 --- a/nhrpd/nhrp_vty.c +++ b/nhrpd/nhrp_vty.c @@ -835,6 +835,7 @@ static void show_ip_nhrp_shortcut(struct nhrp_shortcut *s, void *pctx) ctx->count++; c = s->cache; + buf2[0] = '\0'; if (c) sockunion2str(&c->remote_addr, buf2, sizeof(buf2)); prefix2str(s->p, buf1, sizeof(buf1)); @@ -1066,7 +1067,8 @@ static void clear_nhrp_cache(struct nhrp_cache *c, void *data) if (c->cur.type <= NHRP_CACHE_DYNAMIC) { nhrp_cache_update_binding(c, c->cur.type, -1, NULL, 0, NULL, NULL); - ctx->count++; + if (ctx) + ctx->count++; } } @@ -1096,6 +1098,12 @@ DEFUN(clear_nhrp, clear_nhrp_cmd, nhrp_cache_foreach(ifp, clear_nhrp_cache, &ctx); } else { nhrp_shortcut_foreach(ctx.afi, clear_nhrp_shortcut, &ctx); + /* Clear cache also because when a shortcut is cleared then its + * cache entry should be cleared as well (otherwise traffic + * continues via the shortcut path) + */ + FOR_ALL_INTERFACES (vrf, ifp) + nhrp_cache_foreach(ifp, clear_nhrp_cache, NULL); } if (!ctx.count) { @@ -1217,7 +1225,7 @@ static int interface_config_write(struct vty *vty) } } - vty_endframe(vty, "!\n"); + vty_endframe(vty, "exit\n!\n"); } return 0; @@ -1251,7 +1259,7 @@ void nhrp_config_init(void) install_element(CONFIG_NODE, &nhrp_multicast_nflog_group_cmd); install_element(CONFIG_NODE, &no_nhrp_multicast_nflog_group_cmd); - vrf_cmd_init(NULL, &nhrpd_privs); + vrf_cmd_init(NULL); /* interface specific commands */ if_cmd_init(interface_config_write); diff --git a/ospf6d/ospf6_abr.c b/ospf6d/ospf6_abr.c index 69be807c13..650262f1ae 100644 --- a/ospf6d/ospf6_abr.c +++ b/ospf6d/ospf6_abr.c @@ -172,9 +172,19 @@ int ospf6_abr_originate_summary_to_area(struct ospf6_route *route, uint16_t type; int is_debug = 0; - if (IS_OSPF6_DEBUG_ABR) - zlog_debug("%s : start area %s, route %pFX", __func__, - area->name, &route->prefix); + if (IS_OSPF6_DEBUG_ABR) { + char buf[BUFSIZ]; + + if (route->type == OSPF6_DEST_TYPE_ROUTER) + inet_ntop(AF_INET, + &ADV_ROUTER_IN_PREFIX(&route->prefix), buf, + sizeof(buf)); + else + prefix2str(&route->prefix, buf, sizeof(buf)); + + zlog_debug("%s : start area %s, route %s", __func__, area->name, + buf); + } if (route->type == OSPF6_DEST_TYPE_ROUTER) summary_table = area->summary_router; @@ -684,8 +694,18 @@ void ospf6_abr_originate_summary(struct ospf6_route *route, struct ospf6 *ospf6) struct ospf6_area *oa; struct ospf6_route *range = NULL; - if (IS_OSPF6_DEBUG_ABR) - zlog_debug("%s: route %pFX", __func__, &route->prefix); + if (IS_OSPF6_DEBUG_ABR) { + char buf[BUFSIZ]; + + if (route->type == OSPF6_DEST_TYPE_ROUTER) + inet_ntop(AF_INET, + &ADV_ROUTER_IN_PREFIX(&route->prefix), buf, + sizeof(buf)); + else + prefix2str(&route->prefix, buf, sizeof(buf)); + + zlog_debug("%s: route %s", __func__, buf); + } if (route->type == OSPF6_DEST_TYPE_NETWORK) { oa = ospf6_area_lookup(route->path.area_id, ospf6); diff --git a/ospf6d/ospf6_area.c b/ospf6d/ospf6_area.c index 0f1f061225..098132b1f6 100644 --- a/ospf6d/ospf6_area.c +++ b/ospf6d/ospf6_area.c @@ -189,6 +189,9 @@ static void ospf6_area_stub_update(struct ospf6_area *area) static int ospf6_area_stub_set(struct ospf6 *ospf6, struct ospf6_area *area) { if (!IS_AREA_STUB(area)) { + /* Disable NSSA first. */ + ospf6_area_nssa_unset(ospf6, area); + SET_FLAG(area->flag, OSPF6_AREA_STUB); ospf6_area_stub_update(area); } @@ -196,7 +199,7 @@ static int ospf6_area_stub_set(struct ospf6 *ospf6, struct ospf6_area *area) return 1; } -static void ospf6_area_stub_unset(struct ospf6 *ospf6, struct ospf6_area *area) +void ospf6_area_stub_unset(struct ospf6 *ospf6, struct ospf6_area *area) { if (IS_AREA_STUB(area)) { UNSET_FLAG(area->flag, OSPF6_AREA_STUB); @@ -1266,6 +1269,8 @@ DEFUN(ospf6_area_nssa, ospf6_area_nssa_cmd, return CMD_WARNING_CONFIG_FAILED; } + ospf6_area_no_summary_unset(ospf6, area); + return CMD_SUCCESS; } diff --git a/ospf6d/ospf6_area.h b/ospf6d/ospf6_area.h index dd4d019015..b2a275d745 100644 --- a/ospf6d/ospf6_area.h +++ b/ospf6d/ospf6_area.h @@ -154,6 +154,7 @@ extern void ospf6_area_delete(struct ospf6_area *); extern struct ospf6_area *ospf6_area_lookup(uint32_t, struct ospf6 *); extern struct ospf6_area *ospf6_area_lookup_by_area_id(uint32_t area_id); +extern void ospf6_area_stub_unset(struct ospf6 *ospf6, struct ospf6_area *area); extern void ospf6_area_enable(struct ospf6_area *); extern void ospf6_area_disable(struct ospf6_area *); diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c index 165e409eed..f16a1975a8 100644 --- a/ospf6d/ospf6_asbr.c +++ b/ospf6d/ospf6_asbr.c @@ -570,6 +570,22 @@ void ospf6_asbr_lsa_add(struct ospf6_lsa *lsa) &asbr_id); return; } + + /* + * RFC 3101 - Section 2.5: + * "For a Type-7 LSA the matching routing table entry must + * specify an intra-area path through the LSA's originating + * NSSA". + */ + if (ntohs(lsa->header->type) == OSPF6_LSTYPE_TYPE_7 + && (asbr_entry->path.area_id != oa->area_id + || asbr_entry->path.type != OSPF6_PATH_TYPE_INTRA)) { + if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) + zlog_debug( + "Intra-area route to NSSA ASBR not found: %pFX", + &asbr_id); + return; + } } /* Check the forwarding address */ @@ -811,7 +827,7 @@ void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa, /* Compare LSA cost with current * route info. */ - if (!asbr_entry + if (asbr_entry && (o_path->cost != route_to_del->path.cost || o_path->u.cost_e2 != route_to_del->path.u @@ -1307,9 +1323,9 @@ void ospf6_asbr_remove_externals_from_area(struct ospf6_area *oa) struct ospf6 *ospf6 = oa->ospf6; const struct route_node *iterend; - /* skip if router is in other non-stub areas */ + /* skip if router is in other non-stub/non-NSSA areas */ for (ALL_LIST_ELEMENTS(ospf6->area_list, node, nnode, area)) - if (!IS_AREA_STUB(area)) + if (!IS_AREA_STUB(area) && !IS_AREA_NSSA(area)) return; /* if router is only in a stub area then purge AS-External LSAs */ @@ -1514,8 +1530,6 @@ static void ospf6_asbr_external_lsa_remove_by_id(struct ospf6 *ospf6, uint32_t id) { struct ospf6_lsa *lsa; - struct ospf6_area *oa; - struct listnode *lnode; lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL), htonl(id), ospf6->router_id, ospf6->lsdb); @@ -1524,20 +1538,6 @@ static void ospf6_asbr_external_lsa_remove_by_id(struct ospf6 *ospf6, ospf6_external_lsa_purge(ospf6, lsa); - /* Delete the NSSA LSA */ - for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, lnode, oa)) { - lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_TYPE_7), - htonl(id), ospf6->router_id, - oa->lsdb); - if (lsa) { - if (IS_OSPF6_DEBUG_ASBR) - zlog_debug("withdraw type 7 lsa, LS ID: %u", - htonl(id)); - - ospf6_lsa_purge(lsa); - } - } - } static void @@ -1641,61 +1641,55 @@ void ospf6_asbr_redistribute_remove(int type, ifindex_t ifindex, ospf6_asbr_status_update(ospf6, ospf6->redistribute); } -DEFUN (ospf6_redistribute, +DEFPY (ospf6_redistribute, ospf6_redistribute_cmd, - "redistribute " FRR_REDIST_STR_OSPF6D, - "Redistribute\n" - FRR_REDIST_HELP_STR_OSPF6D) -{ - int type; - struct ospf6_redist *red; - - VTY_DECLVAR_CONTEXT(ospf6, ospf6); - - char *proto = argv[argc - 1]->text; - - type = proto_redistnum(AFI_IP6, proto); - if (type < 0) - return CMD_WARNING_CONFIG_FAILED; - - red = ospf6_redist_lookup(ospf6, type, 0); - if (!red) - ospf6_redist_add(ospf6, type, 0); - else - ospf6_asbr_redistribute_unset(ospf6, red, type); - - ospf6_asbr_redistribute_set(ospf6, type); - - return CMD_SUCCESS; -} - -DEFUN (ospf6_redistribute_routemap, - ospf6_redistribute_routemap_cmd, - "redistribute " FRR_REDIST_STR_OSPF6D " route-map WORD", + "redistribute " FRR_REDIST_STR_OSPF6D "[{metric (0-16777214)|metric-type (1-2)$metric_type|route-map WORD$rmap_str}]", "Redistribute\n" FRR_REDIST_HELP_STR_OSPF6D + "Metric for redistributed routes\n" + "OSPF default metric\n" + "OSPF exterior metric type for redistributed routes\n" + "Set OSPF External Type 1/2 metrics\n" "Route map reference\n" "Route map name\n") { - int idx_protocol = 1; - int idx_word = 3; int type; struct ospf6_redist *red; + int idx_protocol = 1; + char *proto = argv[idx_protocol]->text; VTY_DECLVAR_CONTEXT(ospf6, ospf6); - char *proto = argv[idx_protocol]->text; type = proto_redistnum(AFI_IP6, proto); if (type < 0) return CMD_WARNING_CONFIG_FAILED; + if (!metric_str) + metric = -1; + if (!metric_type_str) + metric_type = -1; + red = ospf6_redist_lookup(ospf6, type, 0); - if (!red) + if (!red) { red = ospf6_redist_add(ospf6, type, 0); - else + } else { + /* Check if nothing has changed. */ + if (red->dmetric.value == metric + && red->dmetric.type == metric_type + && ((!ROUTEMAP_NAME(red) && !rmap_str) + || (ROUTEMAP_NAME(red) && rmap_str + && strmatch(ROUTEMAP_NAME(red), rmap_str)))) + return CMD_SUCCESS; + ospf6_asbr_redistribute_unset(ospf6, red, type); + } - ospf6_asbr_routemap_set(red, argv[idx_word]->arg); + red->dmetric.value = metric; + red->dmetric.type = metric_type; + if (rmap_str) + ospf6_asbr_routemap_set(red, rmap_str); + else + ospf6_asbr_routemap_unset(red); ospf6_asbr_redistribute_set(ospf6, type); return CMD_SUCCESS; @@ -1703,20 +1697,24 @@ DEFUN (ospf6_redistribute_routemap, DEFUN (no_ospf6_redistribute, no_ospf6_redistribute_cmd, - "no redistribute " FRR_REDIST_STR_OSPF6D " [route-map WORD]", + "no redistribute " FRR_REDIST_STR_OSPF6D "[{metric (0-16777214)|metric-type (1-2)|route-map WORD}]", NO_STR "Redistribute\n" FRR_REDIST_HELP_STR_OSPF6D + "Metric for redistributed routes\n" + "OSPF default metric\n" + "OSPF exterior metric type for redistributed routes\n" + "Set OSPF External Type 1/2 metrics\n" "Route map reference\n" "Route map name\n") { - int idx_protocol = 2; int type; struct ospf6_redist *red; + int idx_protocol = 2; + char *proto = argv[idx_protocol]->text; VTY_DECLVAR_CONTEXT(ospf6, ospf6); - char *proto = argv[idx_protocol]->text; type = proto_redistnum(AFI_IP6, proto); if (type < 0) return CMD_WARNING_CONFIG_FAILED; @@ -1743,11 +1741,14 @@ int ospf6_redistribute_config_write(struct vty *vty, struct ospf6 *ospf6) if (type == ZEBRA_ROUTE_OSPF6) continue; + vty_out(vty, " redistribute %s", ZROUTE_NAME(type)); + if (red->dmetric.value >= 0) + vty_out(vty, " metric %d", red->dmetric.value); + if (red->dmetric.type != DEFAULT_METRIC_TYPE) + vty_out(vty, " metric-type 1"); if (ROUTEMAP_NAME(red)) - vty_out(vty, " redistribute %s route-map %s\n", - ZROUTE_NAME(type), ROUTEMAP_NAME(red)); - else - vty_out(vty, " redistribute %s\n", ZROUTE_NAME(type)); + vty_out(vty, " route-map %s", ROUTEMAP_NAME(red)); + vty_out(vty, "\n"); } return 0; @@ -2555,7 +2556,6 @@ void ospf6_asbr_init(void) install_element(OSPF6_NODE, &no_ospf6_default_information_originate_cmd); install_element(OSPF6_NODE, &ospf6_redistribute_cmd); - install_element(OSPF6_NODE, &ospf6_redistribute_routemap_cmd); install_element(OSPF6_NODE, &no_ospf6_redistribute_cmd); } @@ -2715,21 +2715,47 @@ void ospf6_fill_aggr_route_details(struct ospf6 *ospf6, rt_aggr->path.origin.id = htonl(aggr->id); } +static void +ospf6_summary_add_aggr_route_and_blackhole(struct ospf6 *ospf6, + struct ospf6_external_aggr_rt *aggr) +{ + struct ospf6_route *rt_aggr; + struct ospf6_external_info *info; + + /* Create summary route and save it. */ + rt_aggr = ospf6_route_create(ospf6); + rt_aggr->type = OSPF6_DEST_TYPE_NETWORK; + /* Needed to install route while calling zebra api */ + SET_FLAG(rt_aggr->flag, OSPF6_ROUTE_BEST); + + info = XCALLOC(MTYPE_OSPF6_EXTERNAL_INFO, sizeof(*info)); + rt_aggr->route_option = info; + aggr->route = rt_aggr; + + /* Prepare the external_info for aggregator + * Fill all the details which will get advertised + */ + ospf6_fill_aggr_route_details(ospf6, aggr); + + /* Add next-hop to Null interface. */ + ospf6_add_route_nexthop_blackhole(rt_aggr); + + ospf6_zebra_route_update_add(rt_aggr, ospf6); +} + static void ospf6_originate_new_aggr_lsa(struct ospf6 *ospf6, struct ospf6_external_aggr_rt *aggr) { - struct prefix prefix_id; struct route_node *node; struct ospf6_lsa *lsa = NULL; - struct ospf6_route *rt_aggr; - struct ospf6_external_info *info; if (IS_OSPF6_DEBUG_AGGR) zlog_debug("%s: Originate new aggregate route(%pFX)", __func__, &aggr->p); aggr->id = ospf6->external_id++; + /* create/update binding in external_id_table */ prefix_id.family = AF_INET; prefix_id.prefixlen = 32; @@ -2742,28 +2768,10 @@ static void ospf6_originate_new_aggr_lsa(struct ospf6 *ospf6, "Advertise AS-External Id:%pI4 prefix %pFX metric %u", &prefix_id.u.prefix4, &aggr->p, aggr->metric); - /* Create summary route and save it. */ - rt_aggr = ospf6_route_create(ospf6); - rt_aggr->type = OSPF6_DEST_TYPE_NETWORK; - /* Needed to install route while calling zebra api */ - SET_FLAG(rt_aggr->flag, OSPF6_ROUTE_BEST); - - info = XCALLOC(MTYPE_OSPF6_EXTERNAL_INFO, sizeof(*info)); - rt_aggr->route_option = info; - aggr->route = rt_aggr; - - /* Prepare the external_info for aggregator - * Fill all the details which will get advertised - */ - ospf6_fill_aggr_route_details(ospf6, aggr); - - /* Add next-hop to Null interface. */ - ospf6_add_route_nexthop_blackhole(rt_aggr); - - ospf6_zebra_route_update_add(rt_aggr, ospf6); + ospf6_summary_add_aggr_route_and_blackhole(ospf6, aggr); /* Originate summary LSA */ - lsa = ospf6_originate_type5_type7_lsas(rt_aggr, ospf6); + lsa = ospf6_originate_type5_type7_lsas(aggr->route, ospf6); if (lsa) { if (IS_OSPF6_DEBUG_AGGR) zlog_debug("%s: Set the origination bit for aggregator", @@ -2842,12 +2850,10 @@ ospf6_originate_summary_lsa(struct ospf6 *ospf6, /* The key for ID field is a running number and not prefix */ info = rt->route_option; assert(info); - if (info->id) { + if (info->id) lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL), htonl(info->id), ospf6->router_id, ospf6->lsdb); - assert(lsa); - } aggr_lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL), htonl(aggr->id), ospf6->router_id, ospf6->lsdb); @@ -2939,20 +2945,22 @@ ospf6_originate_summary_lsa(struct ospf6 *ospf6, /* If the external route prefix same as aggregate route * and if external route is already originated as TYPE-5 - * then it need to be refreshed and originate bit should - * be set. + * then just update the aggr info and remove the route info */ if (lsa && prefix_same(&aggr->p, &rt->prefix)) { if (IS_OSPF6_DEBUG_AGGR) - zlog_debug("%s: External route prefix is same as aggr so refreshing LSA(%pFX)", - __PRETTY_FUNCTION__, - &aggr->p); + zlog_debug( + "%s: Route prefix is same as aggr so no need to re-originate LSA(%pFX)", + __PRETTY_FUNCTION__, &aggr->p); - THREAD_OFF(lsa->refresh); - thread_add_event(master, ospf6_lsa_refresh, lsa, 0, - &lsa->refresh); aggr->id = info->id; + info->id = 0; + rt->path.origin.id = 0; + + ospf6_summary_add_aggr_route_and_blackhole(ospf6, aggr); + SET_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_ORIGINATED); + return; } diff --git a/ospf6d/ospf6_flood.c b/ospf6d/ospf6_flood.c index 3d52597161..f13ed3e3bb 100644 --- a/ospf6d/ospf6_flood.c +++ b/ospf6d/ospf6_flood.c @@ -41,6 +41,7 @@ #include "ospf6_flood.h" #include "ospf6_nssa.h" +#include "ospf6_gr.h" unsigned char conf_debug_ospf6_flooding; @@ -169,9 +170,26 @@ void ospf6_remove_id_from_external_id_table(struct ospf6 *ospf6, void ospf6_external_lsa_purge(struct ospf6 *ospf6, struct ospf6_lsa *lsa) { + uint32_t id = lsa->header->id; + struct ospf6_area *oa; + struct listnode *lnode; + ospf6_lsa_purge(lsa); - ospf6_remove_id_from_external_id_table(ospf6, lsa->header->id); + ospf6_remove_id_from_external_id_table(ospf6, id); + + /* Delete the corresponding NSSA LSA */ + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, lnode, oa)) { + lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_TYPE_7), id, + ospf6->router_id, oa->lsdb); + if (lsa) { + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("withdraw type 7 lsa, LS ID: %u", + htonl(id)); + + ospf6_lsa_purge(lsa); + } + } } void ospf6_lsa_purge(struct ospf6_lsa *lsa) @@ -306,6 +324,22 @@ void ospf6_install_lsa(struct ospf6_lsa *lsa) /* actually install */ lsa->installed = now; + + /* Topo change handling */ + if (CHECK_LSA_TOPO_CHG_ELIGIBLE(ntohs(lsa->header->type))) { + + /* check if it is new lsa ? or existing lsa got modified ?*/ + if (!old || OSPF6_LSA_IS_CHANGED(old, lsa)) { + struct ospf6 *ospf6; + + ospf6 = ospf6_get_by_lsdb(lsa); + + assert(ospf6); + + ospf6_helper_handle_topo_chg(ospf6, lsa); + } + } + ospf6_lsdb_add(lsa, lsa->lsdb); if (ntohs(lsa->header->type) == OSPF6_LSTYPE_TYPE_7) { @@ -999,6 +1033,50 @@ void ospf6_receive_lsa(struct ospf6_neighbor *from, != from->ospf6_if->area->ospf6->router_id) ospf6_flood(from, new); + /* Received Grace-LSA */ + if (IS_GRACE_LSA(new)) { + struct ospf6 *ospf6; + + ospf6 = ospf6_get_by_lsdb(new); + + assert(ospf6); + + if (OSPF6_LSA_IS_MAXAGE(new)) { + + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "%s, Received a maxage GraceLSA from router %pI4", + __func__, + &new->header->adv_router); + if (old) { + ospf6_process_maxage_grace_lsa( + ospf6, new, from); + } else { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "%s, GraceLSA doesn't exist in lsdb, so discarding GraceLSA", + __func__); + return; + } + } else { + + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "%s, Received a GraceLSA from router %pI4", + __func__, + &new->header->adv_router); + + if (ospf6_process_grace_lsa(ospf6, new, from) + == OSPF6_GR_NOT_HELPER) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "%s, Not moving to HELPER role, So dicarding GraceLSA", + __func__); + return; + } + } + } + /* (d), installing lsdb, which may cause routing table calculation (replacing database copy) */ ospf6_install_lsa(new); diff --git a/ospf6d/ospf6_gr.h b/ospf6d/ospf6_gr.h new file mode 100644 index 0000000000..378b7193cd --- /dev/null +++ b/ospf6d/ospf6_gr.h @@ -0,0 +1,161 @@ +/* + * OSPF6 Graceful Retsart helper functions. + * + * Copyright (C) 2021-22 Vmware, Inc. + * Rajesh Kumar Girada + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef OSPF6_GR_H +#define OSPF6_GR_H + +#define OSPF6_GR_NOT_HELPER 0 +#define OSPF6_GR_ACTIVE_HELPER 1 + +#define OSPF6_GR_HELPER_NO_LSACHECK 0 +#define OSPF6_GR_HELPER_LSACHECK 1 + +#define OSPF6_MAX_GRACE_INTERVAL 1800 +#define OSPF6_MIN_GRACE_INTERVAL 1 + +/* Debug option */ +extern unsigned char conf_debug_ospf6_gr; + +#define OSPF6_DEBUG_GR 0x01 + +#define OSPF6_DEBUG_GR_ON() (conf_debug_ospf6_gr |= OSPF6_DEBUG_GR) + +#define OSPF6_DEBUG_GR_OFF() (conf_debug_ospf6_gr &= ~OSPF6_DEBUG_GR) + +#define IS_DEBUG_OSPF6_GR conf_debug_ospf6_gr + + +enum ospf6_helper_exit_reason { + OSPF6_GR_HELPER_EXIT_NONE = 0, + OSPF6_GR_HELPER_INPROGRESS, + OSPF6_GR_HELPER_TOPO_CHG, + OSPF6_GR_HELPER_GRACE_TIMEOUT, + OSPF6_GR_HELPER_COMPLETED +}; + +enum ospf6_gr_restart_reason { + OSPF6_GR_UNKNOWN_RESTART = 0, + OSPF6_GR_SW_RESTART = 1, + OSPF6_GR_SW_UPGRADE = 2, + OSPF6_GR_SWITCH_REDUNDANT_CARD = 3, + OSPF6_GR_INVALID_REASON_CODE = 4 +}; + +enum ospf6_gr_helper_rejected_reason { + OSPF6_HELPER_REJECTED_NONE, + OSPF6_HELPER_SUPPORT_DISABLED, + OSPF6_HELPER_NOT_A_VALID_NEIGHBOUR, + OSPF6_HELPER_PLANNED_ONLY_RESTART, + OSPF6_HELPER_TOPO_CHANGE_RTXMT_LIST, + OSPF6_HELPER_LSA_AGE_MORE +}; + +#ifdef roundup +#define ROUNDUP(val, gran) roundup(val, gran) +#else /* roundup */ +#define ROUNDUP(val, gran) (((val)-1 | (gran)-1) + 1) +#endif /* roundup */ + +/* + * Generic TLV (type, length, value) macros + */ +struct tlv_header { + uint16_t type; /* Type of Value */ + uint16_t length; /* Length of Value portion only, in bytes */ +}; + +#define TLV_HDR_SIZE (sizeof(struct tlv_header)) + +#define TLV_BODY_SIZE(tlvh) (ROUNDUP(ntohs((tlvh)->length), sizeof(uint32_t))) + +#define TLV_SIZE(tlvh) (TLV_HDR_SIZE + TLV_BODY_SIZE(tlvh)) + +#define TLV_HDR_TOP(lsah) \ + (struct tlv_header *)((char *)(lsah) + OSPF6_LSA_HEADER_SIZE) + +#define TLV_HDR_NEXT(tlvh) \ + (struct tlv_header *)((char *)(tlvh) + TLV_SIZE(tlvh)) + +/* Ref RFC5187 appendix-A */ +/* Grace period TLV */ +#define GRACE_PERIOD_TYPE 1 +#define GRACE_PERIOD_LENGTH 4 +struct grace_tlv_graceperiod { + struct tlv_header header; + uint32_t interval; +}; +#define GRACE_PERIOD_TLV_SIZE sizeof(struct grace_tlv_graceperiod) + +/* Restart reason TLV */ +#define RESTART_REASON_TYPE 2 +#define RESTART_REASON_LENGTH 1 +struct grace_tlv_restart_reason { + struct tlv_header header; + uint8_t reason; + uint8_t reserved[3]; +}; +#define GRACE_RESTART_REASON_TLV_SIZE sizeof(struct grace_tlv_restart_reason) + +#define OSPF6_GRACE_LSA_MIN_SIZE \ + GRACE_PERIOD_TLV_SIZE + GRACE_RESTART_REASON_TLV_SIZE + +struct advRtr { + in_addr_t advRtrAddr; +}; + +#define OSPF6_HELPER_ENABLE_RTR_COUNT(ospf) \ + (ospf6->ospf6_helper_cfg.enable_rtr_list->count) + +/* Check , it is a planned restart */ +#define OSPF6_GR_IS_PLANNED_RESTART(reason) \ + ((reason == OSPF6_GR_SW_RESTART) || (reason == OSPF6_GR_SW_UPGRADE)) + +/* Check the router is HELPER for current neighbour */ +#define OSPF6_GR_IS_ACTIVE_HELPER(N) \ + ((N)->gr_helper_info.gr_helper_status == OSPF6_GR_ACTIVE_HELPER) + +/* Check the LSA is GRACE LSA */ +#define IS_GRACE_LSA(lsa) (ntohs(lsa->header->type) == OSPF6_LSTYPE_GRACE_LSA) + +/* Check neighbour is in FULL state */ +#define IS_NBR_STATE_FULL(nbr) (nbr->state == OSPF6_NEIGHBOR_FULL) + +extern const char *ospf6_exit_reason_desc[]; +extern const char *ospf6_restart_reason_desc[]; +extern const char *ospf6_rejected_reason_desc[]; + +extern void ospf6_gr_helper_config_init(void); +extern void ospf6_gr_helper_init(struct ospf6 *ospf6); +extern void ospf6_gr_helper_deinit(struct ospf6 *ospf6); +extern void ospf6_gr_helper_exit(struct ospf6_neighbor *nbr, + enum ospf6_helper_exit_reason reason); +extern int ospf6_process_grace_lsa(struct ospf6 *ospf6, struct ospf6_lsa *lsa, + struct ospf6_neighbor *nbr); +extern void ospf6_process_maxage_grace_lsa(struct ospf6 *ospf, + struct ospf6_lsa *lsa, + struct ospf6_neighbor *nbr); +extern void ospf6_helper_handle_topo_chg(struct ospf6 *ospf6, + struct ospf6_lsa *lsa); +extern int config_write_ospf6_gr_helper(struct vty *vty, struct ospf6 *ospf6); +extern int config_write_ospf6_debug_gr_helper(struct vty *vty); +#endif /* OSPF6_GR_H */ diff --git a/ospf6d/ospf6_gr_helper.c b/ospf6d/ospf6_gr_helper.c new file mode 100644 index 0000000000..d0536087c3 --- /dev/null +++ b/ospf6d/ospf6_gr_helper.c @@ -0,0 +1,1378 @@ +/* + * OSPF6 Graceful Restart helper functions. + * + * Copyright (C) 2021-22 Vmware, Inc. + * Rajesh Kumar Girada + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <zebra.h> + +#include "log.h" +#include "vty.h" +#include "command.h" +#include "prefix.h" +#include "stream.h" +#include "zclient.h" +#include "memory.h" +#include "table.h" +#include "lib/bfd.h" +#include "lib_errors.h" +#include "jhash.h" + +#include "ospf6_proto.h" +#include "ospf6_lsa.h" +#include "ospf6_lsdb.h" +#include "ospf6_route.h" +#include "ospf6_message.h" + +#include "ospf6_top.h" +#include "ospf6_area.h" +#include "ospf6_interface.h" +#include "ospf6_neighbor.h" +#include "ospf6_intra.h" +#include "ospf6d.h" +#include "ospf6_gr.h" +#include "lib/json.h" +#ifndef VTYSH_EXTRACT_PL +#include "ospf6d/ospf6_gr_helper_clippy.c" +#endif + +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_GR_HELPER, "OSPF6 Graceful restart helper"); + +unsigned char conf_debug_ospf6_gr; + +static int ospf6_grace_lsa_show_info(struct vty *vty, struct ospf6_lsa *lsa, + json_object *json, bool use_json); + +struct ospf6_lsa_handler grace_lsa_handler = {.lh_type = OSPF6_LSTYPE_GRACE_LSA, + .lh_name = "Grace", + .lh_short_name = "GR", + .lh_show = + ospf6_grace_lsa_show_info, + .lh_get_prefix_str = NULL, + .lh_debug = 0}; + +const char *ospf6_exit_reason_desc[] = { + "Unknown reason", + "Helper in progress", + "Topology Change", + "Grace timer expiry", + "Successful graceful restart", +}; + +const char *ospf6_restart_reason_desc[] = { + "Unknown restart", + "Software restart", + "Software reload/upgrade", + "Switch to redundant control processor", +}; + +const char *ospf6_rejected_reason_desc[] = { + "Unknown reason", + "Helper support disabled", + "Neighbour is not in FULL state", + "Supports only planned restart but received for unplanned", + "Topo change due to change in lsa rxmt list", + "LSA age is more than Grace interval", +}; + +static unsigned int ospf6_enable_rtr_hash_key(const void *data) +{ + const struct advRtr *rtr = data; + + return jhash_1word(rtr->advRtrAddr, 0); +} + +static bool ospf6_enable_rtr_hash_cmp(const void *d1, const void *d2) +{ + const struct advRtr *rtr1 = d1; + const struct advRtr *rtr2 = d2; + + return (rtr1->advRtrAddr == rtr2->advRtrAddr); +} + +static void *ospf6_enable_rtr_hash_alloc(void *p) +{ + struct advRtr *rid; + + rid = XCALLOC(MTYPE_OSPF6_GR_HELPER, sizeof(struct advRtr)); + rid->advRtrAddr = ((struct advRtr *)p)->advRtrAddr; + + return rid; +} + +static void ospf6_disable_rtr_hash_free(void *rtr) +{ + XFREE(MTYPE_OSPF6_GR_HELPER, rtr); +} + +static void ospf6_enable_rtr_hash_destroy(struct ospf6 *ospf6) +{ + if (ospf6->ospf6_helper_cfg.enable_rtr_list == NULL) + return; + + hash_clean(ospf6->ospf6_helper_cfg.enable_rtr_list, + ospf6_disable_rtr_hash_free); + hash_free(ospf6->ospf6_helper_cfg.enable_rtr_list); + ospf6->ospf6_helper_cfg.enable_rtr_list = NULL; +} + +/* + * Extracting tlv info from GRACE LSA. + * + * lsa + * ospf6 grace lsa + * + * Returns: + * interval : grace interval. + * reason : Restarting reason. + */ +static int ospf6_extract_grace_lsa_fields(struct ospf6_lsa *lsa, + uint32_t *interval, uint8_t *reason) +{ + struct ospf6_lsa_header *lsah = NULL; + struct tlv_header *tlvh = NULL; + struct grace_tlv_graceperiod *gracePeriod; + struct grace_tlv_restart_reason *grReason; + uint16_t length = 0; + int sum = 0; + + lsah = (struct ospf6_lsa_header *)lsa->header; + + length = ntohs(lsah->length) - OSPF6_LSA_HEADER_SIZE; + + for (tlvh = TLV_HDR_TOP(lsah); sum < length; + tlvh = TLV_HDR_NEXT(tlvh)) { + switch (ntohs(tlvh->type)) { + case GRACE_PERIOD_TYPE: + gracePeriod = (struct grace_tlv_graceperiod *)tlvh; + *interval = ntohl(gracePeriod->interval); + sum += TLV_SIZE(tlvh); + + /* Check if grace interval is valid */ + if (*interval > OSPF6_MAX_GRACE_INTERVAL + || *interval < OSPF6_MIN_GRACE_INTERVAL) + return OSPF6_FAILURE; + break; + case RESTART_REASON_TYPE: + grReason = (struct grace_tlv_restart_reason *)tlvh; + *reason = grReason->reason; + sum += TLV_SIZE(tlvh); + + if (*reason >= OSPF6_GR_INVALID_REASON_CODE) + return OSPF6_FAILURE; + break; + default: + if (IS_DEBUG_OSPF6_GR) + zlog_debug("%s, Ignoring unknown TLV type:%d", + __func__, ntohs(tlvh->type)); + } + } + + return OSPF6_SUCCESS; +} + +/* + * Grace timer expiry handler. + * HELPER aborts its role at grace timer expiry. + * + * thread + * thread pointer + * + * Returns: + * Nothing + */ +static int ospf6_handle_grace_timer_expiry(struct thread *thread) +{ + struct ospf6_neighbor *nbr = THREAD_ARG(thread); + + nbr->gr_helper_info.t_grace_timer = NULL; + + ospf6_gr_helper_exit(nbr, OSPF6_GR_HELPER_GRACE_TIMEOUT); + return OSPF6_SUCCESS; +} + +/* + * API to check any change in the neighbor's + * retransmission list. + * + * nbr + * ospf6 neighbor + * + * Returns: + * TRUE - if any change in the lsa. + * FALSE - no change in the lsas. + */ +static bool ospf6_check_chg_in_rxmt_list(struct ospf6_neighbor *nbr) +{ + struct ospf6_lsa *lsa, *lsanext; + + for (ALL_LSDB(nbr->retrans_list, lsa, lsanext)) { + struct ospf6_lsa *lsa_in_db = NULL; + + /* Fetching the same copy of LSA form LSDB to validate the + * topochange. + */ + lsa_in_db = + ospf6_lsdb_lookup(lsa->header->type, lsa->header->id, + lsa->header->adv_router, lsa->lsdb); + + if (lsa_in_db && lsa_in_db->tobe_acknowledged) { + ospf6_lsa_unlock(lsa); + if (lsanext) + ospf6_lsa_unlock(lsanext); + + return OSPF6_TRUE; + } + } + + return OSPF6_FALSE; +} + +/* + * Process Grace LSA.If it is eligible move to HELPER role. + * Ref rfc3623 section 3.1 and rfc5187 + * + * ospf + * Ospf6 pointer. + * + * lsa + * Grace LSA received from RESTARTER. + * + * restarter + * ospf6 neighbour which requests the router to act as + * HELPER. + * + * Returns: + * status. + * If supported as HELPER : OSPF_GR_HELPER_INPROGRESS + * If Not supported as HELPER : OSPF_GR_HELPER_NONE + */ +int ospf6_process_grace_lsa(struct ospf6 *ospf6, struct ospf6_lsa *lsa, + struct ospf6_neighbor *restarter) +{ + uint8_t restart_reason = 0; + uint32_t grace_interval = 0; + uint32_t actual_grace_interval = 0; + struct advRtr lookup; + int ret; + + /* Extract the grace lsa packet fields */ + ret = ospf6_extract_grace_lsa_fields(lsa, &grace_interval, + &restart_reason); + if (ret != OSPF6_SUCCESS) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug("%s, Wrong Grace LSA packet.", __func__); + return OSPF6_GR_NOT_HELPER; + } + + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "%s, Grace LSA received from %pI4, grace interval:%u, restart reason :%s", + __func__, &restarter->router_id, grace_interval, + ospf6_restart_reason_desc[restart_reason]); + + /* Verify Helper enabled globally */ + if (!ospf6->ospf6_helper_cfg.is_helper_supported) { + /* Verify Helper support is enabled for the + * current neighbour router-id. + */ + lookup.advRtrAddr = restarter->router_id; + + if (!hash_lookup(ospf6->ospf6_helper_cfg.enable_rtr_list, + &lookup)) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "%s, HELPER support is disabled, So not a HELPER", + __func__); + restarter->gr_helper_info.rejected_reason = + OSPF6_HELPER_SUPPORT_DISABLED; + return OSPF6_GR_NOT_HELPER; + } + } + + /* Check neighbour is in FULL state and + * became a adjacency. + */ + if (!IS_NBR_STATE_FULL(restarter)) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "%s, This Neighbour %pI6 is not in FULL state.", + __func__, &restarter->linklocal_addr); + restarter->gr_helper_info.rejected_reason = + OSPF6_HELPER_NOT_A_VALID_NEIGHBOUR; + return OSPF6_GR_NOT_HELPER; + } + + /* Based on the restart reason from grace lsa + * check the current router is supporting or not + */ + if (ospf6->ospf6_helper_cfg.only_planned_restart + && !OSPF6_GR_IS_PLANNED_RESTART(restart_reason)) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "%s, Router supports only planned restarts but received the GRACE LSA due a unplanned restart", + __func__); + restarter->gr_helper_info.rejected_reason = + OSPF6_HELPER_PLANNED_ONLY_RESTART; + return OSPF6_GR_NOT_HELPER; + } + + /* Check the retransmission list of this + * neighbour, check any change in lsas. + */ + if (ospf6->ospf6_helper_cfg.strict_lsa_check + && restarter->retrans_list->count + && ospf6_check_chg_in_rxmt_list(restarter)) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "%s, Changed LSA in Rxmt list.So not Helper.", + __func__); + restarter->gr_helper_info.rejected_reason = + OSPF6_HELPER_TOPO_CHANGE_RTXMT_LIST; + return OSPF6_GR_NOT_HELPER; + } + + /* LSA age must be less than the grace period */ + if (ntohs(lsa->header->age) >= grace_interval) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "%s, Grace LSA age(%d) is more than the grace interval(%d)", + __func__, lsa->header->age, grace_interval); + restarter->gr_helper_info.rejected_reason = + OSPF6_HELPER_LSA_AGE_MORE; + return OSPF6_GR_NOT_HELPER; + } + + /* check supported grace period configured + * if configured, use this to start the grace + * timer otherwise use the interval received + * in grace LSA packet. + */ + actual_grace_interval = grace_interval; + if (grace_interval > ospf6->ospf6_helper_cfg.supported_grace_time) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "%s, Received grace period %d is larger than supported grace %d", + __func__, grace_interval, + ospf6->ospf6_helper_cfg.supported_grace_time); + actual_grace_interval = + ospf6->ospf6_helper_cfg.supported_grace_time; + } + + if (OSPF6_GR_IS_ACTIVE_HELPER(restarter)) { + if (restarter->gr_helper_info.t_grace_timer) + THREAD_OFF(restarter->gr_helper_info.t_grace_timer); + + if (ospf6->ospf6_helper_cfg.active_restarter_cnt > 0) + ospf6->ospf6_helper_cfg.active_restarter_cnt--; + + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "%s, Router is already acting as a HELPER for this nbr,so restart the grace timer", + __func__); + } else { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "%s, This Router becomes a HELPER for the neighbour %pI6", + __func__, &restarter->linklocal_addr); + } + + /* Became a Helper to the RESTART neighbour. + * change the helper status. + */ + restarter->gr_helper_info.gr_helper_status = OSPF6_GR_ACTIVE_HELPER; + restarter->gr_helper_info.recvd_grace_period = grace_interval; + restarter->gr_helper_info.actual_grace_period = actual_grace_interval; + restarter->gr_helper_info.gr_restart_reason = restart_reason; + restarter->gr_helper_info.rejected_reason = OSPF6_HELPER_REJECTED_NONE; + + /* Increment the active restart nbr count */ + ospf6->ospf6_helper_cfg.active_restarter_cnt++; + + if (IS_DEBUG_OSPF6_GR) + zlog_debug("%s, Grace timer started.interval:%u", __func__, + actual_grace_interval); + + /* Start the grace timer */ + thread_add_timer(master, ospf6_handle_grace_timer_expiry, restarter, + actual_grace_interval, + &restarter->gr_helper_info.t_grace_timer); + + return OSPF6_GR_ACTIVE_HELPER; +} + +/* + * Api to exit from HELPER role to take all actions + * required at exit. + * Ref rfc3623 section 3. and rfc51872 + * + * ospf6 + * Ospf6 pointer. + * + * nbr + * Ospf6 neighbour for which it is acting as HELPER. + * + * reason + * The reason for exiting from HELPER. + * + * Returns: + * Nothing. + */ +void ospf6_gr_helper_exit(struct ospf6_neighbor *nbr, + enum ospf6_helper_exit_reason reason) +{ + struct ospf6_interface *oi = nbr->ospf6_if; + struct ospf6 *ospf6; + + if (!oi) + return; + + ospf6 = oi->area->ospf6; + + if (!OSPF6_GR_IS_ACTIVE_HELPER(nbr)) + return; + + if (IS_DEBUG_OSPF6_GR) + zlog_debug("%s, Exiting from HELPER support to %pI6, due to %s", + __func__, &nbr->linklocal_addr, + ospf6_exit_reason_desc[reason]); + + /* Reset helper status*/ + nbr->gr_helper_info.gr_helper_status = OSPF6_GR_NOT_HELPER; + nbr->gr_helper_info.helper_exit_reason = reason; + nbr->gr_helper_info.actual_grace_period = 0; + nbr->gr_helper_info.recvd_grace_period = 0; + nbr->gr_helper_info.gr_restart_reason = 0; + ospf6->ospf6_helper_cfg.last_exit_reason = reason; + + /* If the exit not triggered due to grace timer + * expiry, stop the grace timer. + */ + if (reason != OSPF6_GR_HELPER_GRACE_TIMEOUT) + THREAD_OFF(nbr->gr_helper_info.t_grace_timer); + + if (ospf6->ospf6_helper_cfg.active_restarter_cnt <= 0) { + zlog_err( + "OSPF6 GR-Helper: Number of active Restarters should be greater than zero."); + return; + } + /* Decrement active restarter count */ + ospf6->ospf6_helper_cfg.active_restarter_cnt--; + + /* check exit triggered due to successful completion + * of graceful restart. + */ + if (reason != OSPF6_GR_HELPER_COMPLETED) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug("%s, Unsuccessful GR exit. RESTARTER : %pI6", + __func__, &nbr->linklocal_addr); + } + + /*Recalculate the DR for the network segment */ + dr_election(oi); + + /* Originate a router LSA */ + OSPF6_ROUTER_LSA_SCHEDULE(nbr->ospf6_if->area); + + /* Originate network lsa if it is an DR in the LAN */ + if (nbr->ospf6_if->state == OSPF6_INTERFACE_DR) + OSPF6_NETWORK_LSA_SCHEDULE(nbr->ospf6_if); +} + +/* + * Process max age Grace LSA. + * It is a indication for successful completion of GR. + * If router acting as HELPER, It exits from helper role. + * + * ospf6 + * Ospf6 pointer. + * + * lsa + * Grace LSA received from RESTARTER. + * + * nbr + * ospf6 neighbour which request the router to act as + * HELPER. + * + * Returns: + * Nothing. + */ +void ospf6_process_maxage_grace_lsa(struct ospf6 *ospf6, struct ospf6_lsa *lsa, + struct ospf6_neighbor *restarter) +{ + uint8_t restart_reason = 0; + uint32_t grace_interval = 0; + int ret; + + /* Extract the grace lsa packet fields */ + ret = ospf6_extract_grace_lsa_fields(lsa, &grace_interval, + &restart_reason); + if (ret != OSPF6_SUCCESS) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug("%s, Wrong Grace LSA packet.", __func__); + return; + } + + if (IS_DEBUG_OSPF6_GR) + zlog_debug("%s, GraceLSA received for neighbour %pI4.", + __func__, &restarter->router_id); + + ospf6_gr_helper_exit(restarter, OSPF6_GR_HELPER_COMPLETED); +} + +/* + * Actions to be taken when topo change detected + * HELPER will be exited upon a topo change. + * + * ospf6 + * ospf6 pointer + * lsa + * topo change occurred due to this lsa(type (1-5 and 7) + * + * Returns: + * Nothing + */ +void ospf6_helper_handle_topo_chg(struct ospf6 *ospf6, struct ospf6_lsa *lsa) +{ + struct listnode *i, *j, *k; + struct ospf6_neighbor *nbr = NULL; + struct ospf6_area *oa = NULL; + struct ospf6_interface *oi = NULL; + + if (!ospf6->ospf6_helper_cfg.active_restarter_cnt) + return; + + /* Topo change not required to be handled if strict + * LSA check is disabled for this router. + */ + if (!ospf6->ospf6_helper_cfg.strict_lsa_check) + return; + + if (IS_DEBUG_OSPF6_GR) + zlog_debug("%s, Topo change detected due to lsa details : %s", + __func__, lsa->name); + + lsa->tobe_acknowledged = OSPF6_TRUE; + + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, i, oa)) + for (ALL_LIST_ELEMENTS_RO(oa->if_list, j, oi)) { + + /* Ref rfc3623 section 3.2.3.b and rfc5187 + * If change due to external LSA and if the area is + * stub, then it is not a topo change. Since Type-5 + * lsas will not be flooded in stub area. + */ + if (IS_AREA_STUB(oi->area) + && ((lsa->header->type == OSPF6_LSTYPE_AS_EXTERNAL) + || (lsa->header->type == OSPF6_LSTYPE_TYPE_7) + || (lsa->header->type + == OSPF6_LSTYPE_INTER_ROUTER))) { + continue; + } + + for (ALL_LIST_ELEMENTS_RO(oi->neighbor_list, k, nbr)) { + + ospf6_gr_helper_exit(nbr, + OSPF6_GR_HELPER_TOPO_CHG); + } + } +} + +/* Configuration handlers */ +/* + * Disable/Enable HELPER support on router level. + * + * ospf6 + * Ospf6 pointer. + * + * status + * TRUE/FALSE + * + * Returns: + * Nothing. + */ +static void ospf6_gr_helper_support_set(struct ospf6 *ospf6, bool support) +{ + struct ospf6_interface *oi; + struct advRtr lookup; + struct listnode *i, *j, *k; + struct ospf6_neighbor *nbr = NULL; + struct ospf6_area *oa = NULL; + + if (ospf6->ospf6_helper_cfg.is_helper_supported == support) + return; + + ospf6->ospf6_helper_cfg.is_helper_supported = support; + + /* If helper support disabled, cease HELPER role for all + * supporting neighbors. + */ + if (support == OSPF6_FALSE) { + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, i, oa)) + for (ALL_LIST_ELEMENTS_RO(oa->if_list, j, oi)) { + + for (ALL_LIST_ELEMENTS_RO(oi->neighbor_list, k, + nbr)) { + + lookup.advRtrAddr = nbr->router_id; + /* check if helper support enabled for + * the corresponding routerid. + * If enabled, + * dont exit from helper role. + */ + if (hash_lookup( + ospf6->ospf6_helper_cfg + .enable_rtr_list, + &lookup)) + continue; + + ospf6_gr_helper_exit( + nbr, OSPF6_GR_HELPER_TOPO_CHG); + } + } + } +} + +/* + * Api to enable/disable strict lsa check on the HELPER. + * + * ospf6 + * Ospf6 pointer. + * + * enabled + * True - disable the lsa check. + * False - enable the strict lsa check. + * + * Returns: + * Nothing. + */ +static void ospf6_gr_helper_lsacheck_set(struct ospf6 *ospf6, bool enabled) +{ + if (ospf6->ospf6_helper_cfg.strict_lsa_check == enabled) + return; + + ospf6->ospf6_helper_cfg.strict_lsa_check = enabled; +} + +/* + * Api to set the supported restart reason. + * + * ospf6 + * Ospf6 pointer. + * + * only_planned + * True: support only planned restart. + * False: support for planned/unplanned restarts. + * + * Returns: + * Nothing. + */ + +static void +ospf6_gr_helper_set_supported_onlyPlanned_restart(struct ospf6 *ospf6, + bool only_planned) +{ + ospf6->ospf6_helper_cfg.only_planned_restart = only_planned; +} + +/* + * Api to set the supported grace interval in this router. + * + * ospf6 + * Ospf6 pointer. + * + * interval + * The supported grace interval.. + * + * Returns: + * Nothing. + */ +static void ospf6_gr_helper_supported_gracetime_set(struct ospf6 *ospf6, + uint32_t interval) +{ + ospf6->ospf6_helper_cfg.supported_grace_time = interval; +} + +/* API to walk and print all the Helper supported router ids */ +static int ospf6_print_vty_helper_dis_rtr_walkcb(struct hash_bucket *bucket, + void *arg) +{ + struct advRtr *rtr = bucket->data; + struct vty *vty = (struct vty *)arg; + static unsigned int count; + + vty_out(vty, "%-6pI4,", &rtr->advRtrAddr); + count++; + + if (count % 5 == 0) + vty_out(vty, "\n"); + + return HASHWALK_CONTINUE; +} + +/* API to walk and print all the Helper supported router ids.*/ +static int ospf6_print_json_helper_dis_rtr_walkcb(struct hash_bucket *bucket, + void *arg) +{ + struct advRtr *rtr = bucket->data; + struct json_object *json_rid_array = (struct json_object *)arg; + struct json_object *json_rid; + char router_id[16]; + + inet_ntop(AF_INET, &rtr->advRtrAddr, router_id, sizeof(router_id)); + + json_rid = json_object_new_object(); + + json_object_string_add(json_rid, "routerId", router_id); + json_object_array_add(json_rid_array, json_rid); + + return HASHWALK_CONTINUE; +} + +/* + * Enable/Disable HELPER support on a specified advertisement + * router. + * + * ospf6 + * Ospf6 pointer. + * + * advRtr + * HELPER support for given Advertisement Router. + * + * support + * True - Enable Helper Support. + * False - Disable Helper Support. + * + * Returns: + * Nothing. + */ +static void ospf6_gr_helper_support_set_per_routerid(struct ospf6 *ospf6, + struct in_addr router_id, + bool support) +{ + struct advRtr temp; + struct advRtr *rtr; + struct listnode *i, *j, *k; + struct ospf6_interface *oi; + struct ospf6_neighbor *nbr; + struct ospf6_area *oa; + + temp.advRtrAddr = router_id.s_addr; + + if (support == OSPF6_FALSE) { + /*Delete the routerid from the enable router hash table */ + rtr = hash_lookup(ospf6->ospf6_helper_cfg.enable_rtr_list, + &temp); + + if (rtr) { + hash_release(ospf6->ospf6_helper_cfg.enable_rtr_list, + rtr); + ospf6_disable_rtr_hash_free(rtr); + } + + /* If helper support is enabled globally + * no action is required. + */ + if (ospf6->ospf6_helper_cfg.is_helper_supported) + return; + + /* Cease the HELPER role fore neighbours from the + * specified advertisement router. + */ + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, i, oa)) + for (ALL_LIST_ELEMENTS_RO(oa->if_list, j, oi)) { + + for (ALL_LIST_ELEMENTS_RO(oi->neighbor_list, k, + nbr)) { + + if (nbr->router_id != router_id.s_addr) + continue; + + if (OSPF6_GR_IS_ACTIVE_HELPER(nbr)) + ospf6_gr_helper_exit( + nbr, + OSPF6_GR_HELPER_TOPO_CHG); + } + } + + } else { + /* Add the routerid to the enable router hash table */ + hash_get(ospf6->ospf6_helper_cfg.enable_rtr_list, &temp, + ospf6_enable_rtr_hash_alloc); + } +} + +static void show_ospfv6_gr_helper_per_nbr(struct vty *vty, json_object *json, + bool uj, struct ospf6_neighbor *nbr) +{ + if (!uj) { + vty_out(vty, " Routerid : %pI4\n", &nbr->router_id); + vty_out(vty, " Received Grace period : %d(in seconds).\n", + nbr->gr_helper_info.recvd_grace_period); + vty_out(vty, " Actual Grace period : %d(in seconds)\n", + nbr->gr_helper_info.actual_grace_period); + vty_out(vty, " Remaining GraceTime:%ld(in seconds).\n", + thread_timer_remain_second( + nbr->gr_helper_info.t_grace_timer)); + vty_out(vty, " Graceful Restart reason: %s.\n\n", + ospf6_restart_reason_desc[nbr->gr_helper_info + .gr_restart_reason]); + } else { + char nbrid[16]; + json_object *json_neigh = NULL; + + inet_ntop(AF_INET, &nbr->router_id, nbrid, sizeof(nbrid)); + json_neigh = json_object_new_object(); + json_object_string_add(json_neigh, "routerid", nbrid); + json_object_int_add(json_neigh, "recvdGraceInterval", + nbr->gr_helper_info.recvd_grace_period); + json_object_int_add(json_neigh, "actualGraceInterval", + nbr->gr_helper_info.actual_grace_period); + json_object_int_add(json_neigh, "remainGracetime", + thread_timer_remain_second( + nbr->gr_helper_info.t_grace_timer)); + json_object_string_add(json_neigh, "restartReason", + ospf6_restart_reason_desc[ + nbr->gr_helper_info.gr_restart_reason]); + json_object_object_add(json, nbr->name, json_neigh); + } +} + +static void show_ospf6_gr_helper_details(struct vty *vty, struct ospf6 *ospf6, + json_object *json, bool uj, bool detail) +{ + struct ospf6_interface *oi; + + /* Show Router ID. */ + if (uj) { + char router_id[16]; + + inet_ntop(AF_INET, &ospf6->router_id, router_id, + sizeof(router_id)); + json_object_string_add(json, "routerId", router_id); + } else + vty_out(vty, + " OSPFv3 Routing Process (0) with Router-ID %pI4\n", + &ospf6->router_id); + + if (!uj) { + + if (ospf6->ospf6_helper_cfg.is_helper_supported) + vty_out(vty, + " Graceful restart helper support enabled.\n"); + else + vty_out(vty, + " Graceful restart helper support disabled.\n"); + + if (ospf6->ospf6_helper_cfg.strict_lsa_check) + vty_out(vty, " Strict LSA check is enabled.\n"); + else + vty_out(vty, " Strict LSA check is disabled.\n"); + + if (ospf6->ospf6_helper_cfg.only_planned_restart) + vty_out(vty, + " Helper supported for planned restarts only.\n"); + else + vty_out(vty, + " Helper supported for Planned and Unplanned Restarts.\n"); + + vty_out(vty, + " Supported Graceful restart interval: %d(in seconds).\n", + ospf6->ospf6_helper_cfg.supported_grace_time); + + if (OSPF6_HELPER_ENABLE_RTR_COUNT(ospf)) { + vty_out(vty, " Enable Router list:\n"); + vty_out(vty, " "); + hash_walk(ospf6->ospf6_helper_cfg.enable_rtr_list, + ospf6_print_vty_helper_dis_rtr_walkcb, vty); + vty_out(vty, "\n\n"); + } + + if (ospf6->ospf6_helper_cfg.last_exit_reason + != OSPF6_GR_HELPER_EXIT_NONE) { + vty_out(vty, " Last Helper exit Reason :%s\n", + ospf6_exit_reason_desc + [ospf6->ospf6_helper_cfg + .last_exit_reason]); + + if (ospf6->ospf6_helper_cfg.active_restarter_cnt) + vty_out(vty, + " Number of Active neighbours in graceful restart: %d\n", + ospf6->ospf6_helper_cfg + .active_restarter_cnt); + else + vty_out(vty, "\n"); + } + + + } else { + json_object_string_add( + json, "helperSupport", + (ospf6->ospf6_helper_cfg.is_helper_supported) + ? "Enabled" + : "Disabled"); + json_object_string_add( + json, "strictLsaCheck", + (ospf6->ospf6_helper_cfg.strict_lsa_check) + ? "Enabled" + : "Disabled"); + json_object_string_add( + json, "restartSupoort", + (ospf6->ospf6_helper_cfg.only_planned_restart) + ? "Planned Restart only" + : "Planned and Unplanned Restarts"); + + json_object_int_add( + json, "supportedGracePeriod", + ospf6->ospf6_helper_cfg.supported_grace_time); + + if (ospf6->ospf6_helper_cfg.last_exit_reason + != OSPF6_GR_HELPER_EXIT_NONE) + json_object_string_add( + json, "LastExitReason", + ospf6_exit_reason_desc + [ospf6->ospf6_helper_cfg + .last_exit_reason]); + + if (OSPF6_HELPER_ENABLE_RTR_COUNT(ospf6)) { + struct json_object *json_rid_array = + json_object_new_array(); + + json_object_object_add(json, "enabledRouterIds", + json_rid_array); + + hash_walk(ospf6->ospf6_helper_cfg.enable_rtr_list, + ospf6_print_json_helper_dis_rtr_walkcb, + json_rid_array); + } + } + + if (detail) { + int cnt = 1; + struct listnode *i, *j, *k; + struct ospf6_area *oa; + json_object *json_neighbors = NULL; + + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, i, oa)) + for (ALL_LIST_ELEMENTS_RO(oa->if_list, j, oi)) { + struct ospf6_neighbor *nbr; + + if (uj) { + json_object_object_get_ex( + json, "Neighbors", + &json_neighbors); + if (!json_neighbors) { + json_neighbors = + json_object_new_object(); + json_object_object_add( + json, "Neighbors", + json_neighbors); + } + } + + for (ALL_LIST_ELEMENTS_RO(oi->neighbor_list, k, + nbr)) { + + if (!OSPF6_GR_IS_ACTIVE_HELPER(nbr)) + continue; + + if (!uj) + vty_out(vty, + " Neighbour %d :\n", + cnt++); + + show_ospfv6_gr_helper_per_nbr( + vty, json_neighbors, uj, nbr); + + } + } + } +} + +/* Graceful Restart HELPER config Commands */ +DEFPY(ospf6_gr_helper_enable, + ospf6_gr_helper_enable_cmd, + "graceful-restart helper-only [A.B.C.D$rtr_id]", + "ospf6 graceful restart\n" + "Enable Helper support\n" + "Advertisement RouterId\n") +{ + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + if (rtr_id_str != NULL) { + + ospf6_gr_helper_support_set_per_routerid(ospf6, rtr_id, + OSPF6_TRUE); + + return CMD_SUCCESS; + } + + ospf6_gr_helper_support_set(ospf6, OSPF6_TRUE); + + return CMD_SUCCESS; +} + +DEFPY(ospf6_gr_helper_disable, + ospf6_gr_helper_disable_cmd, + "no graceful-restart helper-only [A.B.C.D$rtr_id]", + NO_STR + "ospf6 graceful restart\n" + "Disable Helper support\n" + "Advertisement RouterId\n") +{ + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + if (rtr_id_str != NULL) { + + ospf6_gr_helper_support_set_per_routerid(ospf6, rtr_id, + OSPF6_FALSE); + + return CMD_SUCCESS; + } + + ospf6_gr_helper_support_set(ospf6, OSPF6_FALSE); + + return CMD_SUCCESS; +} + +DEFPY(ospf6_gr_helper_disable_lsacheck, + ospf6_gr_helper_disable_lsacheck_cmd, + "graceful-restart helper lsa-check-disable", + "ospf6 graceful restart\n" + "ospf6 GR Helper\n" + "disable strict LSA check\n") +{ + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + ospf6_gr_helper_lsacheck_set(ospf6, OSPF6_FALSE); + return CMD_SUCCESS; +} + +DEFPY(no_ospf6_gr_helper_disable_lsacheck, + no_ospf6_gr_helper_disable_lsacheck_cmd, + "no graceful-restart helper lsa-check-disable", + NO_STR + "ospf6 graceful restart\n" + "ospf6 GR Helper\n" + "diasble strict LSA check\n") +{ + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + ospf6_gr_helper_lsacheck_set(ospf6, OSPF6_TRUE); + return CMD_SUCCESS; +} + +DEFPY(ospf6_gr_helper_planned_only, + ospf6_gr_helper_planned_only_cmd, + "graceful-restart helper planned-only", + "ospf6 graceful restart\n" + "ospf6 GR Helper\n" + "supported only planned restart\n") +{ + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + ospf6_gr_helper_set_supported_onlyPlanned_restart(ospf6, OSPF6_TRUE); + + return CMD_SUCCESS; +} + +DEFPY(no_ospf6_gr_helper_planned_only, no_ospf6_gr_helper_planned_only_cmd, + "no graceful-restart helper planned-only", + NO_STR + "ospf6 graceful restart\n" + "ospf6 GR Helper\n" + "supported only for planned restart\n") +{ + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + ospf6_gr_helper_set_supported_onlyPlanned_restart(ospf6, OSPF6_FALSE); + + return CMD_SUCCESS; +} + +DEFPY(ospf6_gr_helper_supported_grace_time, + ospf6_gr_helper_supported_grace_time_cmd, + "graceful-restart helper supported-grace-time (10-1800)$interval", + "ospf6 graceful restart\n" + "ospf6 GR Helper\n" + "supported grace timer\n" + "grace interval(in seconds)\n") +{ + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + ospf6_gr_helper_supported_gracetime_set(ospf6, interval); + return CMD_SUCCESS; +} + +DEFPY(no_ospf6_gr_helper_supported_grace_time, + no_ospf6_gr_helper_supported_grace_time_cmd, + "no graceful-restart helper supported-grace-time (10-1800)$interval", + NO_STR + "ospf6 graceful restart\n" + "ospf6 GR Helper\n" + "supported grace timer\n" + "grace interval(in seconds)\n") +{ + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + ospf6_gr_helper_supported_gracetime_set(ospf6, + OSPF6_MAX_GRACE_INTERVAL); + return CMD_SUCCESS; +} + +/* Show commands */ +DEFPY(show_ipv6_ospf6_gr_helper, + show_ipv6_ospf6_gr_helper_cmd, + "show ipv6 ospf6 graceful-restart helper [detail] [json]", + SHOW_STR + "Ipv6 Information\n" + "OSPF6 information\n" + "ospf6 graceful restart\n" + "helper details in the router\n" + "detailed information\n" JSON_STR) +{ + int idx = 0; + bool uj = use_json(argc, argv); + struct ospf6 *ospf6 = NULL; + json_object *json = NULL; + bool detail = false; + + ospf6 = ospf6_lookup_by_vrf_name(VRF_DEFAULT_NAME); + OSPF6_CMD_CHECK_RUNNING(); + + if (argv_find(argv, argc, "detail", &idx)) + detail = true; + + if (uj) + json = json_object_new_object(); + + show_ospf6_gr_helper_details(vty, ospf6, json, uj, detail); + + if (uj) { + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } + + return CMD_SUCCESS; +} + +/* Debug commands */ +DEFPY(debug_ospf6_gr, debug_ospf6_gr_cmd, + "[no$no] debug ospf6 graceful-restart", + NO_STR DEBUG_STR OSPF6_STR "Graceful restart\n") +{ + if (!no) + OSPF6_DEBUG_GR_ON(); + else + OSPF6_DEBUG_GR_OFF(); + + return CMD_SUCCESS; +} + +/* + * Api to display the grace LSA information. + * + * vty + * vty pointer. + * lsa + * Grace LSA. + * json + * json object + * + * Returns: + * Nothing. + */ +static int ospf6_grace_lsa_show_info(struct vty *vty, struct ospf6_lsa *lsa, + json_object *json, bool use_json) +{ + struct ospf6_lsa_header *lsah = NULL; + struct tlv_header *tlvh = NULL; + struct grace_tlv_graceperiod *gracePeriod; + struct grace_tlv_restart_reason *grReason; + uint16_t length = 0; + int sum = 0; + + lsah = (struct ospf6_lsa_header *)lsa->header; + + length = ntohs(lsah->length) - OSPF6_LSA_HEADER_SIZE; + + if (vty) { + if (!use_json) + vty_out(vty, "TLV info:\n"); + } else { + zlog_debug(" TLV info:"); + } + + for (tlvh = TLV_HDR_TOP(lsah); sum < length; + tlvh = TLV_HDR_NEXT(tlvh)) { + switch (ntohs(tlvh->type)) { + case GRACE_PERIOD_TYPE: + gracePeriod = (struct grace_tlv_graceperiod *)tlvh; + sum += TLV_SIZE(tlvh); + + if (vty) { + if (use_json) + json_object_int_add( + json, "gracePeriod", + ntohl(gracePeriod->interval)); + else + vty_out(vty, " Grace period:%d\n", + ntohl(gracePeriod->interval)); + } else { + zlog_debug(" Grace period:%d", + ntohl(gracePeriod->interval)); + } + break; + case RESTART_REASON_TYPE: + grReason = (struct grace_tlv_restart_reason *)tlvh; + sum += TLV_SIZE(tlvh); + if (vty) { + if (use_json) + json_object_string_add( + json, "restartReason", + ospf6_restart_reason_desc + [grReason->reason]); + else + vty_out(vty, " Restart reason:%s\n", + ospf6_restart_reason_desc + [grReason->reason]); + } else { + zlog_debug(" Restart reason:%s", + ospf6_restart_reason_desc + [grReason->reason]); + } + break; + default: + break; + } + } + + return 0; +} + +void ospf6_gr_helper_config_init(void) +{ + + ospf6_install_lsa_handler(&grace_lsa_handler); + + install_element(OSPF6_NODE, &ospf6_gr_helper_enable_cmd); + install_element(OSPF6_NODE, &ospf6_gr_helper_disable_cmd); + install_element(OSPF6_NODE, &ospf6_gr_helper_disable_lsacheck_cmd); + install_element(OSPF6_NODE, &no_ospf6_gr_helper_disable_lsacheck_cmd); + install_element(OSPF6_NODE, &ospf6_gr_helper_planned_only_cmd); + install_element(OSPF6_NODE, &no_ospf6_gr_helper_planned_only_cmd); + install_element(OSPF6_NODE, &ospf6_gr_helper_supported_grace_time_cmd); + install_element(OSPF6_NODE, + &no_ospf6_gr_helper_supported_grace_time_cmd); + + install_element(VIEW_NODE, &show_ipv6_ospf6_gr_helper_cmd); + + install_element(CONFIG_NODE, &debug_ospf6_gr_cmd); + install_element(ENABLE_NODE, &debug_ospf6_gr_cmd); +} + + +/* + * Initialize GR helper config data structure. + * + * ospf6 + * ospf6 pointer + * + * Returns: + * Nothing + */ +void ospf6_gr_helper_init(struct ospf6 *ospf6) +{ + if (IS_DEBUG_OSPF6_GR) + zlog_debug("%s, GR Helper init.", __func__); + + ospf6->ospf6_helper_cfg.is_helper_supported = OSPF6_FALSE; + ospf6->ospf6_helper_cfg.strict_lsa_check = OSPF6_TRUE; + ospf6->ospf6_helper_cfg.only_planned_restart = OSPF6_FALSE; + ospf6->ospf6_helper_cfg.supported_grace_time = OSPF6_MAX_GRACE_INTERVAL; + ospf6->ospf6_helper_cfg.last_exit_reason = OSPF6_GR_HELPER_EXIT_NONE; + ospf6->ospf6_helper_cfg.active_restarter_cnt = 0; + + ospf6->ospf6_helper_cfg.enable_rtr_list = hash_create( + ospf6_enable_rtr_hash_key, ospf6_enable_rtr_hash_cmp, + "Ospf6 enable router hash"); +} + +/* + * De-initialize GR helper config data structure. + * + * ospf6 + * ospf6 pointer + * + * Returns: + * Nothing + */ +void ospf6_gr_helper_deinit(struct ospf6 *ospf6) +{ + + if (IS_DEBUG_OSPF6_GR) + zlog_debug("%s, GR helper deinit.", __func__); + + ospf6_enable_rtr_hash_destroy(ospf6); +} + +static int ospf6_cfg_write_helper_enable_rtr_walkcb(struct hash_bucket *backet, + void *arg) +{ + struct advRtr *rtr = backet->data; + struct vty *vty = (struct vty *)arg; + + vty_out(vty, " graceful-restart helper-only %pI4\n", &rtr->advRtrAddr); + return HASHWALK_CONTINUE; +} + +int config_write_ospf6_gr_helper(struct vty *vty, struct ospf6 *ospf6) +{ + if (ospf6->ospf6_helper_cfg.is_helper_supported) + vty_out(vty, " graceful-restart helper-only\n"); + + if (!ospf6->ospf6_helper_cfg.strict_lsa_check) + vty_out(vty, " graceful-restart helper lsa-check-disable\n"); + + if (ospf6->ospf6_helper_cfg.only_planned_restart) + vty_out(vty, " graceful-restart helper planned-only\n"); + + if (ospf6->ospf6_helper_cfg.supported_grace_time + != OSPF6_MAX_GRACE_INTERVAL) + vty_out(vty, + " graceful-restart helper supported-grace-time %d\n", + ospf6->ospf6_helper_cfg.supported_grace_time); + + if (OSPF6_HELPER_ENABLE_RTR_COUNT(ospf6)) { + hash_walk(ospf6->ospf6_helper_cfg.enable_rtr_list, + ospf6_cfg_write_helper_enable_rtr_walkcb, vty); + } + + return 0; +} + +int config_write_ospf6_debug_gr_helper(struct vty *vty) +{ + if (IS_DEBUG_OSPF6_GR) + vty_out(vty, "debug ospf6 gr helper\n"); + return 0; +} diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c index a169b9c60e..b427a0c9bd 100644 --- a/ospf6d/ospf6_interface.c +++ b/ospf6d/ospf6_interface.c @@ -44,9 +44,10 @@ #include "ospf6d.h" #include "ospf6_bfd.h" #include "ospf6_zebra.h" +#include "ospf6_gr.h" #include "lib/json.h" -DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_IF, "OSPF6 interface"); +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_IF, "OSPF6 interface"); DEFINE_MTYPE_STATIC(OSPF6D, CFG_PLIST_NAME, "configured prefix list names"); DEFINE_QOBJ_TYPE(ospf6_interface); DEFINE_HOOK(ospf6_interface_change, @@ -59,6 +60,22 @@ const char *const ospf6_interface_state_str[] = { "None", "Down", "Loopback", "Waiting", "PointToPoint", "DROther", "BDR", "DR", NULL}; +int ospf6_interface_neighbor_count(struct ospf6_interface *oi) +{ + int count = 0; + struct ospf6_neighbor *nbr = NULL; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(oi->neighbor_list, node, nbr)) { + /* Down state is not shown. */ + if (nbr->state == OSPF6_NEIGHBOR_DOWN) + continue; + count++; + } + + return count; +} + struct ospf6_interface *ospf6_interface_lookup_by_ifindex(ifindex_t ifindex, vrf_id_t vrf_id) { @@ -579,7 +596,7 @@ static struct ospf6_neighbor *better_drouter(struct ospf6_neighbor *a, return a; } -static uint8_t dr_election(struct ospf6_interface *oi) +uint8_t dr_election(struct ospf6_interface *oi) { struct listnode *node, *nnode; struct ospf6_neighbor *on, *drouter, *bdrouter, myself; @@ -896,6 +913,17 @@ int interface_down(struct thread *thread) /* Stop trying to set socket options. */ THREAD_OFF(oi->thread_sso); + /* Cease the HELPER role for all the neighbours + * of this interface. + */ + if (ospf6_interface_neighbor_count(oi)) { + struct listnode *ln; + struct ospf6_neighbor *nbr = NULL; + + for (ALL_LIST_ELEMENTS_RO(oi->neighbor_list, ln, nbr)) + ospf6_gr_helper_exit(nbr, OSPF6_GR_HELPER_TOPO_CHG); + } + for (ALL_LIST_ELEMENTS(oi->neighbor_list, node, nnode, on)) ospf6_neighbor_delete(on); @@ -2565,7 +2593,7 @@ static int config_write_ospf6_interface(struct vty *vty, struct vrf *vrf) ospf6_bfd_write_config(vty, oi); - vty_endframe(vty, "!\n"); + vty_endframe(vty, "exit\n!\n"); } return 0; } diff --git a/ospf6d/ospf6_interface.h b/ospf6d/ospf6_interface.h index b5efca743e..ccdf8b1c8f 100644 --- a/ospf6d/ospf6_interface.h +++ b/ospf6d/ospf6_interface.h @@ -218,6 +218,8 @@ extern void install_element_ospf6_clear_interface(void); extern int config_write_ospf6_debug_interface(struct vty *vty); extern void install_element_ospf6_debug_interface(void); +extern int ospf6_interface_neighbor_count(struct ospf6_interface *oi); +extern uint8_t dr_election(struct ospf6_interface *oi); DECLARE_HOOK(ospf6_interface_change, (struct ospf6_interface * oi, int state, int old_state), diff --git a/ospf6d/ospf6_lsa.c b/ospf6d/ospf6_lsa.c index 9c03ce21ed..1bc1ce9cdf 100644 --- a/ospf6d/ospf6_lsa.c +++ b/ospf6d/ospf6_lsa.c @@ -29,6 +29,7 @@ #include "memory.h" #include "thread.h" #include "checksum.h" +#include "frrstr.h" #include "ospf6_proto.h" #include "ospf6_lsa.h" @@ -80,7 +81,6 @@ static int ospf6_unknown_lsa_show(struct vty *vty, struct ospf6_lsa *lsa, json_object *json_obj, bool use_json) { uint8_t *start, *end, *current; - char byte[4]; start = (uint8_t *)lsa->header + sizeof(struct ospf6_lsa_header); end = (uint8_t *)lsa->header + ntohs(lsa->header->length); @@ -95,8 +95,7 @@ static int ospf6_unknown_lsa_show(struct vty *vty, struct ospf6_lsa *lsa, else if ((current - start) % 4 == 0) vty_out(vty, " "); - snprintf(byte, sizeof(byte), "%02x", *current); - vty_out(vty, "%s", byte); + vty_out(vty, "%02x", *current); } vty_out(vty, "\n\n"); @@ -553,29 +552,51 @@ void ospf6_lsa_show_summary(struct vty *vty, struct ospf6_lsa *lsa, void ospf6_lsa_show_dump(struct vty *vty, struct ospf6_lsa *lsa, json_object *json_array, bool use_json) { - uint8_t *start, *end, *current; + uint8_t *start = NULL; + uint8_t *end = NULL; + uint8_t *current = NULL; char byte[4]; + char *header_str = NULL; + char adv_router[INET6_ADDRSTRLEN]; + char id[INET6_ADDRSTRLEN]; + json_object *json = NULL; start = (uint8_t *)lsa->header; end = (uint8_t *)lsa->header + ntohs(lsa->header->length); - if (use_json) - return; + if (use_json) { + json = json_object_new_object(); + size_t header_str_sz = (2 * (end - start)) + 1; - vty_out(vty, "\n"); - vty_out(vty, "%s:\n", lsa->name); + header_str = XMALLOC(MTYPE_TMP, header_str_sz); - for (current = start; current < end; current++) { - if ((current - start) % 16 == 0) - vty_out(vty, "\n "); - else if ((current - start) % 4 == 0) - vty_out(vty, " "); + inet_ntop(AF_INET, &lsa->header->id, id, sizeof(id)); + inet_ntop(AF_INET, &lsa->header->adv_router, adv_router, + sizeof(adv_router)); - snprintf(byte, sizeof(byte), "%02x", *current); - vty_out(vty, "%s", byte); - } + frrstr_hex(header_str, header_str_sz, start, end - start); + + json_object_string_add(json, "linkStateId", id); + json_object_string_add(json, "advertisingRouter", adv_router); + json_object_string_add(json, "header", header_str); + json_object_array_add(json_array, json); + + XFREE(MTYPE_TMP, header_str); + } else { + vty_out(vty, "\n%s:\n", lsa->name); + + for (current = start; current < end; current++) { + if ((current - start) % 16 == 0) + vty_out(vty, "\n "); + else if ((current - start) % 4 == 0) + vty_out(vty, " "); - vty_out(vty, "\n\n"); + snprintf(byte, sizeof(byte), "%02x", *current); + vty_out(vty, "%s", byte); + } + + vty_out(vty, "\n\n"); + } return; } diff --git a/ospf6d/ospf6_lsa.h b/ospf6d/ospf6_lsa.h index 4c95ee69bd..a8ed9132dd 100644 --- a/ospf6d/ospf6_lsa.h +++ b/ospf6d/ospf6_lsa.h @@ -70,7 +70,8 @@ #define OSPF6_LSTYPE_TYPE_7 0x2007 #define OSPF6_LSTYPE_LINK 0x0008 #define OSPF6_LSTYPE_INTRA_PREFIX 0x2009 -#define OSPF6_LSTYPE_SIZE 0x000a +#define OSPF6_LSTYPE_GRACE_LSA 0x000b +#define OSPF6_LSTYPE_SIZE 0x000c /* Masks for LS Type : RFC 2740 A.4.2.1 "LS type" */ #define OSPF6_LSTYPE_UBIT_MASK 0x8000 @@ -146,6 +147,9 @@ struct ospf6_lsa { /* lsa instance */ struct ospf6_lsa_header *header; + + /*For topo chg detection in HELPER role*/ + bool tobe_acknowledged; }; #define OSPF6_LSA_HEADERONLY 0x01 @@ -210,6 +214,14 @@ extern vector ospf6_lsa_handler_vector; continue; \ } +#define CHECK_LSA_TOPO_CHG_ELIGIBLE(type) \ + ((type == OSPF6_LSTYPE_ROUTER) \ + || (type == OSPF6_LSTYPE_NETWORK) \ + || (type == OSPF6_LSTYPE_INTER_PREFIX) \ + || (type == OSPF6_LSTYPE_INTER_ROUTER) \ + || (type == OSPF6_LSTYPE_AS_EXTERNAL) \ + || (type == OSPF6_LSTYPE_TYPE_7) \ + || (type == OSPF6_LSTYPE_INTRA_PREFIX)) /* Function Prototypes */ extern const char *ospf6_lstype_name(uint16_t type); diff --git a/ospf6d/ospf6_lsdb.h b/ospf6d/ospf6_lsdb.h index 7a62c46b02..9789e8c4e0 100644 --- a/ospf6d/ospf6_lsdb.h +++ b/ospf6d/ospf6_lsdb.h @@ -68,7 +68,7 @@ extern struct ospf6_lsa *ospf6_lsdb_next(const struct route_node *iterend, /* * Since we are locking the lsa in ospf6_lsdb_head - * and then unlocking it in lspf6_lsa_lock, when + * and then unlocking it in ospf6_lsa_unlock, when * we cache the next pointer we need to increment * the lock for the lsa so we don't accidently free * it really early. @@ -76,7 +76,7 @@ extern struct ospf6_lsa *ospf6_lsdb_next(const struct route_node *iterend, #define ALL_LSDB(lsdb, lsa, lsanext) \ const struct route_node *iterend = \ ospf6_lsdb_head(lsdb, 0, 0, 0, &lsa); \ - (lsa) != NULL &&ospf6_lsa_lock(lsa) \ + (lsa) != NULL && ospf6_lsa_lock(lsa) \ && ((lsanext) = ospf6_lsdb_next(iterend, (lsa)), 1); \ ospf6_lsa_unlock(lsa), (lsa) = (lsanext) diff --git a/ospf6d/ospf6_main.c b/ospf6d/ospf6_main.c index e233611690..54cf142ba8 100644 --- a/ospf6d/ospf6_main.c +++ b/ospf6d/ospf6_main.c @@ -208,7 +208,6 @@ int main(int argc, char *argv[], char *envp[]) break; default: frr_help_exit(1); - break; } } diff --git a/ospf6d/ospf6_message.c b/ospf6d/ospf6_message.c index 549f5668b9..cd73e3d406 100644 --- a/ospf6d/ospf6_message.c +++ b/ospf6d/ospf6_message.c @@ -46,7 +46,7 @@ #include "ospf6_flood.h" #include "ospf6d.h" - +#include "ospf6_gr.h" #include <netinet/ip6.h> DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_MESSAGE, "OSPF6 message"); @@ -84,7 +84,9 @@ const uint16_t ospf6_lsa_minlen[OSPF6_LSTYPE_SIZE] = { /* 0x2006 */ 0, /* 0x2007 */ OSPF6_AS_EXTERNAL_LSA_MIN_SIZE, /* 0x0008 */ OSPF6_LINK_LSA_MIN_SIZE, - /* 0x2009 */ OSPF6_INTRA_PREFIX_LSA_MIN_SIZE}; + /* 0x2009 */ OSPF6_INTRA_PREFIX_LSA_MIN_SIZE, + /* 0x200a */ 0, + /* 0x000b */ OSPF6_GRACE_LSA_MIN_SIZE}; /* print functions */ @@ -512,8 +514,44 @@ static void ospf6_hello_recv(struct in6_addr *src, struct in6_addr *dst, thread_execute(master, hello_received, on, 0); if (twoway) thread_execute(master, twoway_received, on, 0); - else - thread_execute(master, oneway_received, on, 0); + else { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "%s, Received oneway hello from RESTARTER so ignore here.", + __PRETTY_FUNCTION__); + + if (!OSPF6_GR_IS_ACTIVE_HELPER(on)) { + /* If the router is DR_OTHER, RESTARTER will not wait + * until it receives the hello from it if it receives + * from DR and BDR. + * So, helper might receives ONE_WAY hello from + * RESTARTER. So not allowing to change the state if it + * receives one_way hellow when it acts as HELPER for + * that specific neighbor. + */ + thread_execute(master, oneway_received, on, 0); + } + } + + if (OSPF6_GR_IS_ACTIVE_HELPER(on)) { + /* As per the GR Conformance Test Case 7.2. Section 3 + * "Also, if X was the Designated Router on network segment S + * when the helping relationship began, Y maintains X as the + * Designated Router until the helping relationship is + * terminated." + * When it is a helper for this neighbor, It should not trigger + * the ISM Events. Also Intentionally not setting the priority + * and other fields so that when the neighbor exits the Grace + * period, it can handle if there is any change before GR and + * after GR. + */ + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "%s, Neighbor is under GR Restart, hence ignoring the ISM Events", + __PRETTY_FUNCTION__); + + return; + } /* Schedule interface events */ if (backupseen) @@ -1260,7 +1298,15 @@ static unsigned ospf6_lsa_examin(struct ospf6_lsa_header *lsah, lsalen - OSPF6_LSA_HEADER_SIZE - OSPF6_INTRA_PREFIX_LSA_MIN_SIZE, ntohs(intra_prefix_lsa->prefix_num) /* 16 bits */ - ); + ); + case OSPF6_LSTYPE_GRACE_LSA: + if (lsalen < OSPF6_LSA_HEADER_SIZE + GRACE_PERIOD_TLV_SIZE + + GRACE_RESTART_REASON_TLV_SIZE) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug("%s: Undersized GraceLSA.", + __func__); + return MSG_NG; + } } /* No additional validation is possible for unknown LSA types, which are themselves valid in OPSFv3, hence the default decision is to accept. diff --git a/ospf6d/ospf6_neighbor.c b/ospf6d/ospf6_neighbor.c index 8cf05183e1..4ea615f32b 100644 --- a/ospf6d/ospf6_neighbor.c +++ b/ospf6d/ospf6_neighbor.c @@ -45,6 +45,7 @@ #include "ospf6_lsa.h" #include "ospf6_spf.h" #include "ospf6_zebra.h" +#include "ospf6_gr.h" #include "lib/json.h" DEFINE_MTYPE(OSPF6D, OSPF6_NEIGHBOR, "OSPF6 neighbor"); @@ -151,6 +152,7 @@ void ospf6_neighbor_delete(struct ospf6_neighbor *on) THREAD_OFF(on->thread_send_lsreq); THREAD_OFF(on->thread_send_lsupdate); THREAD_OFF(on->thread_send_lsack); + THREAD_OFF(on->gr_helper_info.t_grace_timer); bfd_sess_free(&on->bfd_session); XFREE(MTYPE_OSPF6_NEIGHBOR, on); @@ -192,19 +194,24 @@ static void ospf6_neighbor_state_change(uint8_t next_state, if (prev_state == OSPF6_NEIGHBOR_FULL || next_state == OSPF6_NEIGHBOR_FULL) { - OSPF6_ROUTER_LSA_SCHEDULE(on->ospf6_if->area); - if (on->ospf6_if->state == OSPF6_INTERFACE_DR) { - OSPF6_NETWORK_LSA_SCHEDULE(on->ospf6_if); - OSPF6_INTRA_PREFIX_LSA_SCHEDULE_TRANSIT(on->ospf6_if); + if (!OSPF6_GR_IS_ACTIVE_HELPER(on)) { + OSPF6_ROUTER_LSA_SCHEDULE(on->ospf6_if->area); + if (on->ospf6_if->state == OSPF6_INTERFACE_DR) { + OSPF6_NETWORK_LSA_SCHEDULE(on->ospf6_if); + OSPF6_INTRA_PREFIX_LSA_SCHEDULE_TRANSIT( + on->ospf6_if); + } } if (next_state == OSPF6_NEIGHBOR_FULL) on->ospf6_if->area->intra_prefix_originate = 1; - OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB(on->ospf6_if->area); + if (!OSPF6_GR_IS_ACTIVE_HELPER(on)) + OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB( + on->ospf6_if->area); - if ((prev_state == OSPF6_NEIGHBOR_LOADING || - prev_state == OSPF6_NEIGHBOR_EXCHANGE) && - next_state == OSPF6_NEIGHBOR_FULL) { + if ((prev_state == OSPF6_NEIGHBOR_LOADING + || prev_state == OSPF6_NEIGHBOR_EXCHANGE) + && next_state == OSPF6_NEIGHBOR_FULL) { OSPF6_AS_EXTERN_LSA_SCHEDULE(on->ospf6_if); on->ospf6_if->area->full_nbrs++; } @@ -601,12 +608,29 @@ int inactivity_timer(struct thread *thread) on->drouter = on->prev_drouter = 0; on->bdrouter = on->prev_bdrouter = 0; - ospf6_neighbor_state_change(OSPF6_NEIGHBOR_DOWN, on, - OSPF6_NEIGHBOR_EVENT_INACTIVITY_TIMER); - thread_add_event(master, neighbor_change, on->ospf6_if, 0, NULL); + if (!OSPF6_GR_IS_ACTIVE_HELPER(on)) { + on->drouter = on->prev_drouter = 0; + on->bdrouter = on->prev_bdrouter = 0; + + ospf6_neighbor_state_change( + OSPF6_NEIGHBOR_DOWN, on, + OSPF6_NEIGHBOR_EVENT_INACTIVITY_TIMER); + thread_add_event(master, neighbor_change, on->ospf6_if, 0, + NULL); + + listnode_delete(on->ospf6_if->neighbor_list, on); + ospf6_neighbor_delete(on); - listnode_delete(on->ospf6_if->neighbor_list, on); - ospf6_neighbor_delete(on); + } else { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "%s, Acting as HELPER for this neighbour, So restart the dead timer.", + __PRETTY_FUNCTION__); + + thread_add_timer(master, inactivity_timer, on, + on->ospf6_if->dead_interval, + &on->inactivity_timer); + } return 0; } diff --git a/ospf6d/ospf6_neighbor.h b/ospf6d/ospf6_neighbor.h index 729b1d2e85..a229897226 100644 --- a/ospf6d/ospf6_neighbor.h +++ b/ospf6d/ospf6_neighbor.h @@ -32,6 +32,38 @@ extern unsigned char conf_debug_ospf6_neighbor; #define IS_OSPF6_DEBUG_NEIGHBOR(level) \ (conf_debug_ospf6_neighbor & OSPF6_DEBUG_NEIGHBOR_##level) +struct ospf6_helper_info { + + /* Grace interval received from + * Restarting Router. + */ + uint32_t recvd_grace_period; + + /* Grace interval used for grace + * gracetimer. + */ + uint32_t actual_grace_period; + + /* Grace timer,This Router acts as + * helper until this timer until + * this timer expires. + */ + struct thread *t_grace_timer; + + /* Helper status */ + uint32_t gr_helper_status; + + /* Helper exit reason*/ + uint32_t helper_exit_reason; + + /* Planned/Unplanned restart*/ + uint32_t gr_restart_reason; + + + /* Helper rejected reason */ + uint32_t rejected_reason; +}; + /* Neighbor structure */ struct ospf6_neighbor { /* Neighbor Router ID String */ @@ -104,6 +136,9 @@ struct ospf6_neighbor { /* BFD information */ struct bfd_session_params *bfd_session; + + /* ospf6 graceful restart HELPER info */ + struct ospf6_helper_info gr_helper_info; }; /* Neighbor state */ diff --git a/ospf6d/ospf6_nssa.c b/ospf6d/ospf6_nssa.c index 470a5b1338..10b7d2d9f6 100644 --- a/ospf6d/ospf6_nssa.c +++ b/ospf6d/ospf6_nssa.c @@ -1130,18 +1130,17 @@ static void ospf6_nssa_flush_area(struct ospf6_area *area) uint16_t type; struct ospf6_lsa *lsa = NULL, *type5 = NULL; struct ospf6 *ospf6 = area->ospf6; - const struct route_node *rt = NULL; if (IS_OSPF6_DEBUG_NSSA) zlog_debug("%s: area %s", __func__, area->name); /* Flush the NSSA LSA */ type = htons(OSPF6_LSTYPE_TYPE_7); - rt = ospf6_lsdb_head(area->lsdb_self, 0, type, ospf6->router_id, &lsa); - while (lsa) { + for (ALL_LSDB_TYPED_ADVRTR(area->lsdb, type, ospf6->router_id, lsa)) { lsa->header->age = htons(OSPF_LSA_MAXAGE); SET_FLAG(lsa->flag, OSPF6_LSA_FLUSH); ospf6_flood(NULL, lsa); + /* Flush the translated LSA */ if (ospf6_check_and_set_router_abr(ospf6)) { type = htons(OSPF6_LSTYPE_AS_EXTERNAL); @@ -1155,7 +1154,6 @@ static void ospf6_nssa_flush_area(struct ospf6_area *area) ospf6_flood(NULL, type5); } } - lsa = ospf6_lsdb_next(rt, lsa); } } @@ -1200,11 +1198,10 @@ static void ospf6_check_and_originate_type7_lsa(struct ospf6_area *area) } -static void ospf6_area_nssa_update(struct ospf6_area *area) +void ospf6_area_nssa_update(struct ospf6_area *area) { if (IS_AREA_NSSA(area)) { - if (!ospf6_check_and_set_router_abr(area->ospf6)) - OSPF6_OPT_CLEAR(area->options, OSPF6_OPT_E); + OSPF6_OPT_CLEAR(area->options, OSPF6_OPT_E); area->ospf6->anyNSSA++; OSPF6_OPT_SET(area->options, OSPF6_OPT_N); area->NSSATranslatorRole = OSPF6_NSSA_ROLE_CANDIDATE; @@ -1212,8 +1209,7 @@ static void ospf6_area_nssa_update(struct ospf6_area *area) if (IS_OSPF6_DEBUG_ORIGINATE(ROUTER)) zlog_debug("Normal area for if %s", area->name); OSPF6_OPT_CLEAR(area->options, OSPF6_OPT_N); - if (ospf6_check_and_set_router_abr(area->ospf6)) - OSPF6_OPT_SET(area->options, OSPF6_OPT_E); + OSPF6_OPT_SET(area->options, OSPF6_OPT_E); area->ospf6->anyNSSA--; area->NSSATranslatorState = OSPF6_NSSA_TRANSLATE_DISABLED; } @@ -1222,6 +1218,9 @@ static void ospf6_area_nssa_update(struct ospf6_area *area) if (IS_AREA_NSSA(area)) { OSPF6_ROUTER_LSA_SCHEDULE(area); + /* Flush external LSAs. */ + ospf6_asbr_remove_externals_from_area(area); + /* Check if router is ABR */ if (ospf6_check_and_set_router_abr(area->ospf6)) { if (IS_OSPF6_DEBUG_NSSA) @@ -1240,8 +1239,6 @@ static void ospf6_area_nssa_update(struct ospf6_area *area) if (IS_OSPF6_DEBUG_NSSA) zlog_debug("Normal area %s", area->name); ospf6_nssa_flush_area(area); - ospf6_area_disable(area); - ospf6_area_delete(area); } } @@ -1249,6 +1246,9 @@ int ospf6_area_nssa_set(struct ospf6 *ospf6, struct ospf6_area *area) { if (!IS_AREA_NSSA(area)) { + /* Disable stub first. */ + ospf6_area_stub_unset(ospf6, area); + SET_FLAG(area->flag, OSPF6_AREA_NSSA); if (IS_OSPF6_DEBUG_NSSA) zlog_debug("area %s nssa set", area->name); diff --git a/ospf6d/ospf6_nssa.h b/ospf6d/ospf6_nssa.h index a171d76d44..454bdd7fe2 100644 --- a/ospf6d/ospf6_nssa.h +++ b/ospf6d/ospf6_nssa.h @@ -61,6 +61,7 @@ extern struct ospf6_lsa *ospf6_translated_nssa_originate(struct ospf6_area *, extern void ospf6_asbr_nssa_redist_task(struct ospf6 *ospf6); extern void ospf6_schedule_abr_task(struct ospf6 *ospf6); +extern void ospf6_area_nssa_update(struct ospf6_area *area); void ospf6_asbr_prefix_readvertise(struct ospf6 *ospf6); extern void ospf6_nssa_lsa_originate(struct ospf6_route *route, struct ospf6_area *area); diff --git a/ospf6d/ospf6_route.c b/ospf6d/ospf6_route.c index cd3139d28a..13003b4151 100644 --- a/ospf6d/ospf6_route.c +++ b/ospf6d/ospf6_route.c @@ -667,6 +667,9 @@ struct ospf6_route *ospf6_route_add(struct ospf6_route *route, if (route->type == OSPF6_DEST_TYPE_LINKSTATE) ospf6_linkstate_prefix2str(&route->prefix, buf, sizeof(buf)); + else if (route->type == OSPF6_DEST_TYPE_ROUTER) + inet_ntop(AF_INET, &ADV_ROUTER_IN_PREFIX(&route->prefix), buf, + sizeof(buf)); else prefix2str(&route->prefix, buf, sizeof(buf)); @@ -899,6 +902,9 @@ void ospf6_route_remove(struct ospf6_route *route, if (route->type == OSPF6_DEST_TYPE_LINKSTATE) ospf6_linkstate_prefix2str(&route->prefix, buf, sizeof(buf)); + else if (route->type == OSPF6_DEST_TYPE_ROUTER) + inet_ntop(AF_INET, &ADV_ROUTER_IN_PREFIX(&route->prefix), buf, + sizeof(buf)); else prefix2str(&route->prefix, buf, sizeof(buf)); diff --git a/ospf6d/ospf6_spf.c b/ospf6d/ospf6_spf.c index 4e7a7146eb..1412298802 100644 --- a/ospf6d/ospf6_spf.c +++ b/ospf6d/ospf6_spf.c @@ -1250,12 +1250,9 @@ static int ospf6_ase_calculate_timer(struct thread *t) zlog_debug("%s : looking at area %s", __func__, area->name); - if (IS_OSPF6_DEBUG_SPF(PROCESS)) { - type = htons(OSPF6_LSTYPE_TYPE_7); - for (ALL_LSDB_TYPED(area->lsdb, type, lsa)) - ospf6_ase_calculate_route(ospf6, lsa, - area); - } + type = htons(OSPF6_LSTYPE_TYPE_7); + for (ALL_LSDB_TYPED(area->lsdb, type, lsa)) + ospf6_ase_calculate_route(ospf6, lsa, area); } } return 0; diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c index 6105e2c24b..6ff3789a80 100644 --- a/ospf6d/ospf6_top.c +++ b/ospf6d/ospf6_top.c @@ -51,6 +51,7 @@ #include "ospf6_intra.h" #include "ospf6_spf.h" #include "ospf6d.h" +#include "ospf6_gr.h" #include "lib/json.h" #include "ospf6_nssa.h" @@ -237,7 +238,7 @@ void ospf6_vrf_init(void) vrf_init(ospf6_vrf_new, ospf6_vrf_enable, ospf6_vrf_disable, ospf6_vrf_delete, ospf6_vrf_enable); - vrf_cmd_init(NULL, &ospf6d_privs); + vrf_cmd_init(NULL); } static void ospf6_top_lsdb_hook_add(struct ospf6_lsa *lsa) @@ -440,6 +441,7 @@ static struct ospf6 *ospf6_create(const char *name) o->oi_write_q = list_new(); + ospf6_gr_helper_init(o); QOBJ_REG(o, ospf6); /* Make ospf protocol socket. */ @@ -485,6 +487,7 @@ void ospf6_delete(struct ospf6 *o) QOBJ_UNREG(o); + ospf6_gr_helper_deinit(o); ospf6_flush_self_originated_lsas_now(o); ospf6_disable(o); ospf6_del(o); @@ -2233,7 +2236,9 @@ static int config_write_ospf6(struct vty *vty) ospf6_distance_config_write(vty, ospf6); ospf6_distribute_config_write(vty, ospf6); ospf6_asbr_summary_config_write(vty, ospf6); + config_write_ospf6_gr_helper(vty, ospf6); + vty_out(vty, "exit\n"); vty_out(vty, "!\n"); } return 0; diff --git a/ospf6d/ospf6_top.h b/ospf6d/ospf6_top.h index fe02cd3f84..58ecf08495 100644 --- a/ospf6d/ospf6_top.h +++ b/ospf6d/ospf6_top.h @@ -60,6 +60,43 @@ struct ospf6_redist { #define ROUTEMAP(R) (R->route_map.map) }; +struct ospf6_gr_helper { + /* Gracefull restart Helper supported configs*/ + /* Supported grace interval*/ + uint32_t supported_grace_time; + + /* Helper support + * Supported : True + * Not Supported : False. + */ + bool is_helper_supported; + + /* Support for strict LSA check. + * if it is set,Helper aborted + * upon a TOPO change. + */ + bool strict_lsa_check; + + /* Support as HELPER only for + * planned restarts. + */ + bool only_planned_restart; + + /* This list contains the advertisement + * routerids for which Helper support is + * enabled. + */ + struct hash *enable_rtr_list; + + /* HELPER for number of active + * RESTARTERs. + */ + int active_restarter_cnt; + + /* last HELPER exit reason */ + uint32_t last_exit_reason; +}; + /* OSPFv3 top level data structure */ struct ospf6 { /* The relevant vrf_id */ @@ -154,6 +191,10 @@ struct ospf6 { * to support ECMP. */ uint16_t max_multipath; + + /*ospf6 Graceful restart helper info */ + struct ospf6_gr_helper ospf6_helper_cfg; + /* Count of NSSA areas */ uint8_t anyNSSA; struct thread *t_abr_task; /* ABR task timer. */ diff --git a/ospf6d/ospf6d.c b/ospf6d/ospf6d.c index fb6ac4402a..5dfd986e2a 100644 --- a/ospf6d/ospf6d.c +++ b/ospf6d/ospf6d.c @@ -45,6 +45,7 @@ #include "ospf6_flood.h" #include "ospf6d.h" #include "ospf6_bfd.h" +#include "ospf6_gr.h" #include "lib/json.h" #include "ospf6_nssa.h" @@ -96,6 +97,7 @@ static int config_write_ospf6_debug(struct vty *vty) config_write_ospf6_debug_abr(vty); config_write_ospf6_debug_flood(vty); config_write_ospf6_debug_nssa(vty); + config_write_ospf6_debug_gr_helper(vty); return 0; } @@ -1402,6 +1404,7 @@ void ospf6_init(struct thread_master *master) ospf6_intra_init(); ospf6_asbr_init(); ospf6_abr_init(); + ospf6_gr_helper_config_init(); /* initialize hooks for modifying filter rules */ prefix_list_add_hook(ospf6_plist_add); diff --git a/ospf6d/ospf6d.h b/ospf6d/ospf6d.h index 5afece9b0a..d5170be7cc 100644 --- a/ospf6d/ospf6d.h +++ b/ospf6d/ospf6d.h @@ -108,6 +108,12 @@ extern struct thread_master *master; vrf_name = VRF_DEFAULT_NAME; \ } +#define OSPF6_FALSE false +#define OSPF6_TRUE true +#define OSPF6_SUCCESS 1 +#define OSPF6_FAILURE 0 +#define OSPF6_INVALID -1 + extern struct zebra_privs_t ospf6d_privs; /* Function Prototypes */ diff --git a/ospf6d/subdir.am b/ospf6d/subdir.am index 78fb26b00e..ac99e90b26 100644 --- a/ospf6d/subdir.am +++ b/ospf6d/subdir.am @@ -12,6 +12,7 @@ vtysh_scan += \ ospf6d/ospf6_area.c \ ospf6d/ospf6_bfd.c \ ospf6d/ospf6_flood.c \ + ospf6d/ospf6_gr_helper.c \ ospf6d/ospf6_interface.c \ ospf6d/ospf6_intra.c \ ospf6d/ospf6_lsa.c \ @@ -39,6 +40,7 @@ ospf6d_libospf6_a_SOURCES = \ ospf6d/ospf6_routemap_nb_config.c \ ospf6d/ospf6_bfd.c \ ospf6d/ospf6_flood.c \ + ospf6d/ospf6_gr_helper.c \ ospf6d/ospf6_interface.c \ ospf6d/ospf6_intra.c \ ospf6d/ospf6_lsa.c \ @@ -61,6 +63,7 @@ noinst_HEADERS += \ ospf6d/ospf6_asbr.h \ ospf6d/ospf6_bfd.h \ ospf6d/ospf6_flood.h \ + ospf6d/ospf6_gr.h \ ospf6d/ospf6_interface.h \ ospf6d/ospf6_intra.h \ ospf6d/ospf6_lsa.h \ @@ -91,6 +94,7 @@ clippy_scan += \ ospf6d/ospf6_top.c \ ospf6d/ospf6_asbr.c \ ospf6d/ospf6_lsa.c \ + ospf6d/ospf6_gr_helper.c \ # end nodist_ospf6d_ospf6d_SOURCES = \ diff --git a/ospfd/ospf_flood.c b/ospfd/ospf_flood.c index 7fddb65a86..8f9153d766 100644 --- a/ospfd/ospf_flood.c +++ b/ospfd/ospf_flood.c @@ -267,6 +267,8 @@ static void ospf_process_self_originated_lsa(struct ospf *ospf, ospf_external_lsa_refresh(ospf, new, &ei_aggr, LSA_REFRESH_FORCE, true); + SET_FLAG(aggr->flags, + OSPF_EXTERNAL_AGGRT_ORIGINATED); } else ospf_lsa_flush_as(ospf, new); } diff --git a/ospfd/ospf_interface.h b/ospfd/ospf_interface.h index 7ba6a2b6d7..e441016406 100644 --- a/ospfd/ospf_interface.h +++ b/ospfd/ospf_interface.h @@ -273,74 +273,78 @@ struct ospf_interface { DECLARE_QOBJ_TYPE(ospf_interface); /* Prototypes. */ -extern char *ospf_if_name(struct ospf_interface *); -extern struct ospf_interface *ospf_if_new(struct ospf *, struct interface *, - struct prefix *); -extern void ospf_if_cleanup(struct ospf_interface *); -extern void ospf_if_free(struct ospf_interface *); -extern int ospf_if_up(struct ospf_interface *); -extern int ospf_if_down(struct ospf_interface *); - -extern int ospf_if_is_up(struct ospf_interface *); -extern struct ospf_interface *ospf_if_exists(struct ospf_interface *); -extern struct ospf_interface *ospf_if_lookup_by_lsa_pos(struct ospf_area *, - int); +extern char *ospf_if_name(struct ospf_interface *oi); extern struct ospf_interface * -ospf_if_lookup_by_local_addr(struct ospf *, struct interface *, struct in_addr); -extern struct ospf_interface *ospf_if_lookup_by_prefix(struct ospf *, - struct prefix_ipv4 *); -extern struct ospf_interface *ospf_if_table_lookup(struct interface *, - struct prefix *); -extern struct ospf_interface *ospf_if_addr_local(struct in_addr); +ospf_if_new(struct ospf *ospf, struct interface *ifp, struct prefix *p); +extern void ospf_if_cleanup(struct ospf_interface *oi); +extern void ospf_if_free(struct ospf_interface *oi); +extern int ospf_if_up(struct ospf_interface *oi); +extern int ospf_if_down(struct ospf_interface *oi); + +extern int ospf_if_is_up(struct ospf_interface *oi); +extern struct ospf_interface *ospf_if_exists(struct ospf_interface *oi); +extern struct ospf_interface *ospf_if_lookup_by_lsa_pos(struct ospf_area *area, + int lsa_pos); extern struct ospf_interface * -ospf_if_lookup_recv_if(struct ospf *, struct in_addr, struct interface *); -extern struct ospf_interface *ospf_if_is_configured(struct ospf *, - struct in_addr *); - -extern struct ospf_if_params *ospf_lookup_if_params(struct interface *, - struct in_addr); -extern struct ospf_if_params *ospf_get_if_params(struct interface *, - struct in_addr); -extern void ospf_free_if_params(struct interface *, struct in_addr); -extern void ospf_if_update_params(struct interface *, struct in_addr); - -extern int ospf_if_new_hook(struct interface *); +ospf_if_lookup_by_local_addr(struct ospf *ospf, struct interface *ifp, + struct in_addr addr); +extern struct ospf_interface *ospf_if_lookup_by_prefix(struct ospf *ospf, + struct prefix_ipv4 *p); +extern struct ospf_interface *ospf_if_table_lookup(struct interface *ifp, + struct prefix *p); +extern struct ospf_interface *ospf_if_addr_local(struct in_addr addr); +extern struct ospf_interface *ospf_if_lookup_recv_if(struct ospf *ospf, + struct in_addr addr, + struct interface *ifp); +extern struct ospf_interface *ospf_if_is_configured(struct ospf *ospf, + struct in_addr *addr); + +extern struct ospf_if_params *ospf_lookup_if_params(struct interface *ifp, + struct in_addr addr); +extern struct ospf_if_params *ospf_get_if_params(struct interface *ifp, + struct in_addr addr); +extern void ospf_free_if_params(struct interface *ifp, struct in_addr addr); +extern void ospf_if_update_params(struct interface *ifp, struct in_addr addr); + +extern int ospf_if_new_hook(struct interface *ifp); extern void ospf_if_init(void); -extern void ospf_if_stream_unset(struct ospf_interface *); -extern void ospf_if_reset_variables(struct ospf_interface *); -extern int ospf_if_is_enable(struct ospf_interface *); -extern int ospf_if_get_output_cost(struct ospf_interface *); -extern void ospf_if_recalculate_output_cost(struct interface *); +extern void ospf_if_stream_unset(struct ospf_interface *oi); +extern void ospf_if_reset_variables(struct ospf_interface *oi); +extern int ospf_if_is_enable(struct ospf_interface *oi); +extern int ospf_if_get_output_cost(struct ospf_interface *oi); +extern void ospf_if_recalculate_output_cost(struct interface *ifp); /* Simulate down/up on the interface. */ -extern void ospf_if_reset(struct interface *); - -extern struct ospf_interface *ospf_vl_new(struct ospf *, struct ospf_vl_data *); -extern struct ospf_vl_data *ospf_vl_data_new(struct ospf_area *, - struct in_addr); -extern struct ospf_vl_data *ospf_vl_lookup(struct ospf *, struct ospf_area *, - struct in_addr); +extern void ospf_if_reset(struct interface *ifp); + +extern struct ospf_interface *ospf_vl_new(struct ospf *ospf, + struct ospf_vl_data *vl_data); +extern struct ospf_vl_data *ospf_vl_data_new(struct ospf_area *area, + struct in_addr addr); +extern struct ospf_vl_data * +ospf_vl_lookup(struct ospf *ospf, struct ospf_area *area, struct in_addr addr); extern int ospf_vl_count(struct ospf *ospf, struct ospf_area *area); -extern void ospf_vl_data_free(struct ospf_vl_data *); -extern void ospf_vl_add(struct ospf *, struct ospf_vl_data *); -extern void ospf_vl_delete(struct ospf *, struct ospf_vl_data *); -extern void ospf_vl_up_check(struct ospf_area *, struct in_addr, - struct vertex *); -extern void ospf_vl_unapprove(struct ospf *); -extern void ospf_vl_shut_unapproved(struct ospf *); -extern int ospf_full_virtual_nbrs(struct ospf_area *); -extern int ospf_vls_in_area(struct ospf_area *); - -extern struct crypt_key *ospf_crypt_key_lookup(struct list *, uint8_t); +extern void ospf_vl_data_free(struct ospf_vl_data *vl_data); +extern void ospf_vl_add(struct ospf *ospf, struct ospf_vl_data *vl_data); +extern void ospf_vl_delete(struct ospf *ospf, struct ospf_vl_data *vl_data); +extern void ospf_vl_up_check(struct ospf_area *area, struct in_addr addr, + struct vertex *vertex); +extern void ospf_vl_unapprove(struct ospf *ospf); +extern void ospf_vl_shut_unapproved(struct ospf *ospf); +extern int ospf_full_virtual_nbrs(struct ospf_area *area); +extern int ospf_vls_in_area(struct ospf_area *area); + +extern struct crypt_key *ospf_crypt_key_lookup(struct list *list, + uint8_t key_id); extern struct crypt_key *ospf_crypt_key_new(void); -extern void ospf_crypt_key_add(struct list *, struct crypt_key *); -extern int ospf_crypt_key_delete(struct list *, uint8_t); +extern void ospf_crypt_key_add(struct list *list, struct crypt_key *key); +extern int ospf_crypt_key_delete(struct list *list, uint8_t key_id); extern uint8_t ospf_default_iftype(struct interface *ifp); extern int ospf_interface_neighbor_count(struct ospf_interface *oi); /* Set all multicast memberships appropriately based on the type and state of the interface. */ -extern void ospf_if_set_multicast(struct ospf_interface *); +extern void ospf_if_set_multicast(struct ospf_interface *oi); extern void ospf_if_interface(struct interface *ifp); diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c index 9ef2a6520a..d209ae053c 100644 --- a/ospfd/ospf_lsa.c +++ b/ospfd/ospf_lsa.c @@ -3631,6 +3631,8 @@ struct ospf_lsa *ospf_lsa_refresh(struct ospf *ospf, struct ospf_lsa *lsa) ospf_external_lsa_refresh(ospf, lsa, &ei_aggr, LSA_REFRESH_FORCE, true); + SET_FLAG(aggr->flags, + OSPF_EXTERNAL_AGGRT_ORIGINATED); } else ospf_lsa_flush_as(ospf, lsa); } diff --git a/ospfd/ospf_main.c b/ospfd/ospf_main.c index 73d596b030..257429ebe8 100644 --- a/ospfd/ospf_main.c +++ b/ospfd/ospf_main.c @@ -185,7 +185,6 @@ int main(int argc, char **argv) #endif /* SUPPORT_OSPF_API */ default: frr_help_exit(1); - break; } } diff --git a/ospfd/ospf_nsm.c b/ospfd/ospf_nsm.c index 892d264a2d..268fb81e52 100644 --- a/ospfd/ospf_nsm.c +++ b/ospfd/ospf_nsm.c @@ -298,8 +298,6 @@ static int nsm_negotiation_done(struct ospf_neighbor *nbr) ospf_db_summary_add(nbr, lsa); LSDB_LOOP (SUMMARY_LSDB(area), rn, lsa) ospf_db_summary_add(nbr, lsa); - LSDB_LOOP (ASBR_SUMMARY_LSDB(area), rn, lsa) - ospf_db_summary_add(nbr, lsa); /* Process only if the neighbor is opaque capable. */ if (CHECK_FLAG(nbr->options, OSPF_OPTION_O)) { @@ -314,10 +312,14 @@ static int nsm_negotiation_done(struct ospf_neighbor *nbr) ospf_db_summary_add(nbr, lsa); } + /* For Stub/NSSA area, we should not send Type-4 and Type-5 LSAs */ if (nbr->oi->type != OSPF_IFTYPE_VIRTUALLINK - && area->external_routing == OSPF_AREA_DEFAULT) + && area->external_routing == OSPF_AREA_DEFAULT) { + LSDB_LOOP (ASBR_SUMMARY_LSDB(area), rn, lsa) + ospf_db_summary_add(nbr, lsa); LSDB_LOOP (EXTERNAL_LSDB(nbr->oi->ospf), rn, lsa) ospf_db_summary_add(nbr, lsa); + } if (CHECK_FLAG(nbr->options, OSPF_OPTION_O) && (nbr->oi->type != OSPF_IFTYPE_VIRTUALLINK diff --git a/ospfd/ospf_spf.c b/ospfd/ospf_spf.c index 6a51440266..8b4d55984c 100644 --- a/ospfd/ospf_spf.c +++ b/ospfd/ospf_spf.c @@ -1781,6 +1781,9 @@ void ospf_spf_calculate_area(struct ospf *ospf, struct ospf_area *area, ospf->ti_lfa_protection_type); ospf_spf_cleanup(area->spf, area->spf_vertex_list); + + area->spf = NULL; + area->spf_vertex_list = NULL; } void ospf_spf_calculate_areas(struct ospf *ospf, struct route_table *new_table, diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index 4c248c0df3..515ec28b1b 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -11845,9 +11845,7 @@ static int config_write_interface_one(struct vty *vty, struct vrf *vrf) /* Router Dead Interval print. */ if (OSPF_IF_PARAM_CONFIGURED(params, v_wait) - && params->is_v_wait_set - && params->v_wait - != OSPF_ROUTER_DEAD_INTERVAL_DEFAULT) { + && params->is_v_wait_set) { vty_out(vty, " ip ospf dead-interval "); /* fast hello ? */ @@ -11970,7 +11968,7 @@ static int config_write_interface_one(struct vty *vty, struct vrf *vrf) ospf_opaque_config_write_if(vty, ifp); - vty_endframe(vty, NULL); + vty_endframe(vty, "exit\n!\n"); } return write; @@ -12546,6 +12544,8 @@ static int ospf_config_write_one(struct vty *vty, struct ospf *ospf) /* LDP-Sync print */ ospf_ldp_sync_write_config(vty, ospf); + vty_out(vty, "exit\n"); + write++; return write; } @@ -12984,7 +12984,7 @@ void ospf_vty_init(void) install_element(OSPF_NODE, &ospf_max_multipath_cmd); install_element(OSPF_NODE, &no_ospf_max_multipath_cmd); - vrf_cmd_init(NULL, &ospfd_privs); + vrf_cmd_init(NULL); /* Init interface related vty commands. */ ospf_vty_if_init(); diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c index 3226d66444..9a421de017 100644 --- a/ospfd/ospfd.c +++ b/ospfd/ospfd.c @@ -291,6 +291,16 @@ static int ospf_area_id_cmp(struct ospf_area *a1, struct ospf_area *a2) return 0; } +static void ospf_add(struct ospf *ospf) +{ + listnode_add(om->ospf, ospf); +} + +static void ospf_delete(struct ospf *ospf) +{ + listnode_delete(om->ospf, ospf); +} + struct ospf *ospf_new_alloc(unsigned short instance, const char *name) { int i; @@ -366,6 +376,8 @@ struct ospf *ospf_new_alloc(unsigned short instance, const char *name) new->maxage_delay = OSPF_LSA_MAXAGE_REMOVE_DELAY_DEFAULT; new->maxage_lsa = route_table_init(); new->t_maxage_walker = NULL; + thread_add_timer(master, ospf_lsa_maxage_walker, new, + OSPF_LSA_MAXAGE_CHECK_INTERVAL, &new->t_maxage_walker); /* Max paths initialization */ new->max_multipath = MULTIPATH_NUM; @@ -376,6 +388,8 @@ struct ospf *ospf_new_alloc(unsigned short instance, const char *name) new->lsa_refresh_queue.index = 0; new->lsa_refresh_interval = OSPF_LSA_REFRESH_INTERVAL_DEFAULT; new->t_lsa_refresher = NULL; + thread_add_timer(master, ospf_lsa_refresh_walker, new, + new->lsa_refresh_interval, &new->t_lsa_refresher); new->lsa_refresher_started = monotime(NULL); new->ibuf = stream_new(OSPF_MAX_PACKET_SIZE + 1); @@ -390,6 +404,8 @@ struct ospf *ospf_new_alloc(unsigned short instance, const char *name) ospf_asbr_external_aggregator_init(new); + ospf_opaque_type11_lsa_init(new); + QOBJ_REG(new, ospf); new->fd = -1; @@ -403,23 +419,23 @@ static struct ospf *ospf_new(unsigned short instance, const char *name) struct ospf *new; new = ospf_new_alloc(instance, name); + ospf_add(new); + + if (new->vrf_id == VRF_UNKNOWN) + return new; if ((ospf_sock_init(new)) < 0) { - if (new->vrf_id != VRF_UNKNOWN) - flog_err( - EC_LIB_SOCKET, - "%s: ospf_sock_init is unable to open a socket", - __func__); + flog_err(EC_LIB_SOCKET, + "%s: ospf_sock_init is unable to open a socket", + __func__); return new; } - thread_add_timer(master, ospf_lsa_maxage_walker, new, - OSPF_LSA_MAXAGE_CHECK_INTERVAL, &new->t_maxage_walker); - thread_add_timer(master, ospf_lsa_refresh_walker, new, - new->lsa_refresh_interval, &new->t_lsa_refresher); - thread_add_read(master, ospf_read, new, new->fd, &new->t_read); + new->oi_running = 1; + ospf_router_id_update(new); + /* * Read from non-volatile memory whether this instance is performing a * graceful restart or not. @@ -455,16 +471,6 @@ static int ospf_is_ready(struct ospf *ospf) return 1; } -static void ospf_add(struct ospf *ospf) -{ - listnode_add(om->ospf, ospf); -} - -static void ospf_delete(struct ospf *ospf) -{ - listnode_delete(om->ospf, ospf); -} - struct ospf *ospf_lookup_by_inst_name(unsigned short instance, const char *name) { struct ospf *ospf = NULL; @@ -483,16 +489,6 @@ struct ospf *ospf_lookup_by_inst_name(unsigned short instance, const char *name) return NULL; } -static void ospf_init(struct ospf *ospf) -{ - ospf_opaque_type11_lsa_init(ospf); - - if (ospf->vrf_id != VRF_UNKNOWN) - ospf->oi_running = 1; - - ospf_router_id_update(ospf); -} - struct ospf *ospf_lookup(unsigned short instance, const char *name) { struct ospf *ospf; @@ -513,12 +509,8 @@ struct ospf *ospf_get(unsigned short instance, const char *name, bool *created) ospf = ospf_lookup(instance, name); *created = (ospf == NULL); - if (ospf == NULL) { + if (ospf == NULL) ospf = ospf_new(instance, name); - ospf_add(ospf); - - ospf_init(ospf); - } return ospf; } @@ -1130,8 +1122,8 @@ static void update_redistributed(struct ospf *ospf, int add_to_ospf) if (add_to_ospf) { if (ospf_external_info_find_lsa(ospf, &ei->p)) - if (!ospf_distribute_check_connected( - ospf, ei)) + if (!ospf_redistribute_check( + ospf, ei, NULL)) ospf_external_lsa_flush( ospf, ei->type, &ei->p, @@ -1139,8 +1131,8 @@ static void update_redistributed(struct ospf *ospf, int add_to_ospf) } else { if (!ospf_external_info_find_lsa( ospf, &ei->p)) - if (ospf_distribute_check_connected( - ospf, ei)) + if (ospf_redistribute_check( + ospf, ei, NULL)) ospf_external_lsa_originate( ospf, ei); } diff --git a/pathd/path_cli.c b/pathd/path_cli.c index da41c96414..bd629a2b70 100644 --- a/pathd/path_cli.c +++ b/pathd/path_cli.c @@ -45,9 +45,6 @@ static int config_write_segment_routing(struct vty *vty); -static int config_write_traffic_eng(struct vty *vty); -static int config_write_segment_lists(struct vty *vty); -static int config_write_sr_policies(struct vty *vty); static int segment_list_has_src_dst( struct vty *vty, char *xpath, long index, const char *index_str, struct in_addr adj_src_ipv4, struct in_addr adj_dst_ipv4, @@ -63,6 +60,8 @@ static int segment_list_has_prefix( DEFINE_MTYPE_STATIC(PATHD, PATH_CLI, "Client"); +DEFINE_HOOK(pathd_srte_config_write, (struct vty *vty), (vty)); + /* Vty node structures. */ static struct cmd_node segment_routing_node = { .name = "segment-routing", @@ -77,7 +76,6 @@ static struct cmd_node sr_traffic_eng_node = { .node = SR_TRAFFIC_ENG_NODE, .parent_node = SEGMENT_ROUTING_NODE, .prompt = "%s(config-sr-te)# ", - .config_write = config_write_traffic_eng, }; static struct cmd_node srte_segment_list_node = { @@ -85,7 +83,6 @@ static struct cmd_node srte_segment_list_node = { .node = SR_SEGMENT_LIST_NODE, .parent_node = SR_TRAFFIC_ENG_NODE, .prompt = "%s(config-sr-te-segment-list)# ", - .config_write = config_write_segment_lists, }; static struct cmd_node srte_policy_node = { @@ -93,7 +90,6 @@ static struct cmd_node srte_policy_node = { .node = SR_POLICY_NODE, .parent_node = SR_TRAFFIC_ENG_NODE, .prompt = "%s(config-sr-te-policy)# ", - .config_write = config_write_sr_policies, }; static struct cmd_node srte_candidate_dyn_node = { @@ -309,6 +305,11 @@ void cli_show_srte_segment_list(struct vty *vty, struct lyd_node *dnode, yang_dnode_get_string(dnode, "./name")); } +void cli_show_srte_segment_list_end(struct vty *vty, struct lyd_node *dnode) +{ + vty_out(vty, " exit\n"); +} + static int segment_list_has_src_dst( struct vty *vty, char *xpath, long index, const char *index_str, struct in_addr adj_src_ipv4, struct in_addr adj_dst_ipv4, @@ -550,7 +551,7 @@ void cli_show_srte_segment_list_segment(struct vty *vty, struct lyd_node *dnode, bool show_defaults) { - vty_out(vty, " index %s ", yang_dnode_get_string(dnode, "./index")); + vty_out(vty, " index %s", yang_dnode_get_string(dnode, "./index")); if (yang_dnode_exists(dnode, "./sid-value")) { vty_out(vty, " mpls label %s", yang_dnode_get_string(dnode, "./sid-value")); @@ -666,6 +667,11 @@ void cli_show_srte_policy(struct vty *vty, struct lyd_node *dnode, yang_dnode_get_string(dnode, "./endpoint")); } +void cli_show_srte_policy_end(struct vty *vty, struct lyd_node *dnode) +{ + vty_out(vty, " exit\n"); +} + /* * XPath: /frr-pathd:pathd/srte/policy/name */ @@ -1237,6 +1243,15 @@ void cli_show_srte_policy_candidate_path(struct vty *vty, } } +void cli_show_srte_policy_candidate_path_end(struct vty *vty, + struct lyd_node *dnode) +{ + const char *type = yang_dnode_get_string(dnode, "./type"); + + if (strmatch(type, "dynamic")) + vty_out(vty, " exit\n"); +} + static int config_write_dnode(const struct lyd_node *dnode, void *arg) { struct vty *vty = arg; @@ -1249,29 +1264,20 @@ static int config_write_dnode(const struct lyd_node *dnode, void *arg) int config_write_segment_routing(struct vty *vty) { vty_out(vty, "segment-routing\n"); - return 1; -} - -int config_write_traffic_eng(struct vty *vty) -{ vty_out(vty, " traffic-eng\n"); + path_ted_config_write(vty); - return 1; -} -int config_write_segment_lists(struct vty *vty) -{ yang_dnode_iterate(config_write_dnode, vty, running_config->dnode, "/frr-pathd:pathd/srte/segment-list"); - - return 1; -} - -int config_write_sr_policies(struct vty *vty) -{ yang_dnode_iterate(config_write_dnode, vty, running_config->dnode, "/frr-pathd:pathd/srte/policy"); + hook_call(pathd_srte_config_write, vty); + + vty_out(vty, " exit\n"); + vty_out(vty, "exit\n"); + return 1; } diff --git a/pathd/path_main.c b/pathd/path_main.c index 8d88475206..7b702cca31 100644 --- a/pathd/path_main.c +++ b/pathd/path_main.c @@ -138,7 +138,6 @@ int main(int argc, char **argv, char **envp) break; default: frr_help_exit(1); - break; } } diff --git a/pathd/path_nb.c b/pathd/path_nb.c index 9c622883bc..1ab8b7f39b 100644 --- a/pathd/path_nb.c +++ b/pathd/path_nb.c @@ -56,6 +56,7 @@ const struct frr_yang_module_info frr_pathd_info = { .cbs = { .create = pathd_srte_segment_list_create, .cli_show = cli_show_srte_segment_list, + .cli_show_end = cli_show_srte_segment_list_end, .destroy = pathd_srte_segment_list_destroy, .get_next = pathd_srte_segment_list_get_next, .get_keys = pathd_srte_segment_list_get_keys, @@ -136,6 +137,7 @@ const struct frr_yang_module_info frr_pathd_info = { .cbs = { .create = pathd_srte_policy_create, .cli_show = cli_show_srte_policy, + .cli_show_end = cli_show_srte_policy_end, .destroy = pathd_srte_policy_destroy, .get_next = pathd_srte_policy_get_next, .get_keys = pathd_srte_policy_get_keys, @@ -169,6 +171,7 @@ const struct frr_yang_module_info frr_pathd_info = { .cbs = { .create = pathd_srte_policy_candidate_path_create, .cli_show = cli_show_srte_policy_candidate_path, + .cli_show_end = cli_show_srte_policy_candidate_path_end, .destroy = pathd_srte_policy_candidate_path_destroy, .get_next = pathd_srte_policy_candidate_path_get_next, .get_keys = pathd_srte_policy_candidate_path_get_keys, diff --git a/pathd/path_nb.h b/pathd/path_nb.h index caeadd9ccc..6a918b8b82 100644 --- a/pathd/path_nb.h +++ b/pathd/path_nb.h @@ -112,10 +112,12 @@ void pathd_apply_finish(struct nb_cb_apply_finish_args *args); /* Optional 'cli_show' callbacks. */ void cli_show_srte_segment_list(struct vty *vty, struct lyd_node *dnode, bool show_defaults); +void cli_show_srte_segment_list_end(struct vty *vty, struct lyd_node *dnode); void cli_show_srte_segment_list_segment(struct vty *vty, struct lyd_node *dnode, bool show_defaults); void cli_show_srte_policy(struct vty *vty, struct lyd_node *dnode, bool show_defaults); +void cli_show_srte_policy_end(struct vty *vty, struct lyd_node *dnode); void cli_show_srte_policy_name(struct vty *vty, struct lyd_node *dnode, bool show_defaults); void cli_show_srte_policy_binding_sid(struct vty *vty, struct lyd_node *dnode, @@ -123,6 +125,8 @@ void cli_show_srte_policy_binding_sid(struct vty *vty, struct lyd_node *dnode, void cli_show_srte_policy_candidate_path(struct vty *vty, struct lyd_node *dnode, bool show_defaults); +void cli_show_srte_policy_candidate_path_end(struct vty *vty, + struct lyd_node *dnode); /* Utility functions */ typedef void (*of_pref_cp_t)(enum objfun_type type, void *arg); diff --git a/pathd/path_pcep_cli.c b/pathd/path_pcep_cli.c index a911210ab4..829df3179c 100644 --- a/pathd/path_pcep_cli.c +++ b/pathd/path_pcep_cli.c @@ -175,7 +175,6 @@ static struct cmd_node pcep_node = { .name = "srte pcep", .node = PCEP_NODE, .parent_node = SR_TRAFFIC_ENG_NODE, - .config_write = pcep_cli_pcep_config_write, .prompt = "%s(config-sr-te-pcep)# " }; @@ -183,7 +182,6 @@ static struct cmd_node pcep_pcc_node = { .name = "srte pcep pcc", .node = PCEP_PCC_NODE, .parent_node = PCEP_NODE, - .config_write = pcep_cli_pcc_config_write, .prompt = "%s(config-sr-te-pcep-pcc)# " }; @@ -191,7 +189,6 @@ static struct cmd_node pcep_pce_node = { .name = "srte pcep pce", .node = PCEP_PCE_NODE, .parent_node = PCEP_NODE, - .config_write = pcep_cli_pce_config_write, .prompt = "%s(config-sr-te-pcep-pce)# " }; @@ -199,7 +196,6 @@ static struct cmd_node pcep_pce_config_node = { .name = "srte pcep pce-config", .node = PCEP_PCE_CONFIG_NODE, .parent_node = PCEP_NODE, - .config_write = pcep_cli_pcep_pce_config_write, .prompt = "%s(pce-sr-te-pcep-pce-config)# " }; @@ -1444,6 +1440,10 @@ int pcep_cli_debug_set_all(uint32_t flags, bool set) int pcep_cli_pcep_config_write(struct vty *vty) { vty_out(vty, " pcep\n"); + pcep_cli_pcep_pce_config_write(vty); + pcep_cli_pce_config_write(vty); + pcep_cli_pcc_config_write(vty); + vty_out(vty, " exit\n"); return 1; } @@ -1468,7 +1468,7 @@ int pcep_cli_pcc_config_write(struct vty *vty) } if (pce_connections_g.num_connections == 0) { - return lines; + goto exit; } buf[0] = 0; @@ -1495,6 +1495,8 @@ int pcep_cli_pcc_config_write(struct vty *vty) lines++; buf[0] = 0; } +exit: + vty_out(vty, " exit\n"); return lines; } @@ -1655,6 +1657,8 @@ int pcep_cli_pce_config_write(struct vty *vty) vty_out(vty, "%s", buf); buf[0] = '\0'; + + vty_out(vty, " exit\n"); } return lines; @@ -1679,6 +1683,8 @@ int pcep_cli_pcep_pce_config_write(struct vty *vty) pcep_cli_print_pce_config(group_opts, buf, sizeof(buf)); vty_out(vty, "%s", buf); buf[0] = 0; + + vty_out(vty, " exit\n"); } return lines; @@ -1755,13 +1761,20 @@ DEFPY_NOSH( DEFPY_NOSH( pcep_cli_pcep_pce_config, pcep_cli_pcep_pce_config_cmd, - "[no] pce-config WORD$name", + "pce-config WORD$name", + "Shared configuration\n" + "Shared configuration name\n") +{ + return path_pcep_cli_pcep_pce_config(vty, name); +} + +DEFPY(pcep_cli_pcep_no_pce_config, + pcep_cli_pcep_no_pce_config_cmd, + "no pce-config WORD$name", NO_STR "Shared configuration\n" "Shared configuration name\n") { - if (no == NULL) - return path_pcep_cli_pcep_pce_config(vty, name); return path_pcep_cli_pcep_pce_config_delete(vty, name); } @@ -1781,13 +1794,20 @@ DEFPY(pcep_cli_show_srte_pcep_pce_config, DEFPY_NOSH( pcep_cli_pce, pcep_cli_pce_cmd, - "[no] pce WORD$name", + "pce WORD$name", + "PCE configuration, address sub-config is mandatory\n" + "PCE name\n") +{ + return path_pcep_cli_pce(vty, name); +} + +DEFPY(pcep_cli_no_pce, + pcep_cli_no_pce_cmd, + "no pce WORD$name", NO_STR "PCE configuration, address sub-config is mandatory\n" "PCE name\n") { - if (no == NULL) - return path_pcep_cli_pce(vty, name); return path_pcep_cli_pce_delete(vty, name); } @@ -1906,15 +1926,19 @@ DEFPY(pcep_cli_peer_timers, DEFPY_NOSH( pcep_cli_pcc, pcep_cli_pcc_cmd, - "[no] pcc", + "pcc", + "PCC configuration\n") +{ + return path_pcep_cli_pcc(vty); +} + +DEFPY(pcep_cli_no_pcc, + pcep_cli_no_pcc_cmd, + "no pcc", NO_STR "PCC configuration\n") { - if (no != NULL) { - return path_pcep_cli_pcc_delete(vty); - } else { - return path_pcep_cli_pcc(vty); - } + return path_pcep_cli_pcc_delete(vty); } DEFPY(pcep_cli_pcc_pcc_msd, @@ -1981,6 +2005,7 @@ DEFPY(pcep_cli_clear_srte_pcep_session, void pcep_cli_init(void) { + hook_register(pathd_srte_config_write, pcep_cli_pcep_config_write); hook_register(nb_client_debug_config_write, pcep_cli_debug_config_write); hook_register(nb_client_debug_set_all, pcep_cli_debug_set_all); @@ -2001,6 +2026,7 @@ void pcep_cli_init(void) /* PCEP configuration group related configuration commands */ install_element(PCEP_NODE, &pcep_cli_pcep_pce_config_cmd); + install_element(PCEP_NODE, &pcep_cli_pcep_no_pce_config_cmd); install_element(PCEP_PCE_CONFIG_NODE, &pcep_cli_peer_source_address_cmd); install_element(PCEP_PCE_CONFIG_NODE, &pcep_cli_peer_timers_cmd); @@ -2010,6 +2036,7 @@ void pcep_cli_init(void) /* PCE peer related configuration commands */ install_element(PCEP_NODE, &pcep_cli_pce_cmd); + install_element(PCEP_NODE, &pcep_cli_no_pce_cmd); install_element(PCEP_PCE_NODE, &pcep_cli_peer_address_cmd); install_element(PCEP_PCE_NODE, &pcep_cli_peer_source_address_cmd); install_element(PCEP_PCE_NODE, &pcep_cli_peer_pcep_pce_config_ref_cmd); @@ -2021,6 +2048,7 @@ void pcep_cli_init(void) /* PCC related configuration commands */ install_element(ENABLE_NODE, &pcep_cli_show_srte_pcc_cmd); install_element(PCEP_NODE, &pcep_cli_pcc_cmd); + install_element(PCEP_NODE, &pcep_cli_no_pcc_cmd); install_element(PCEP_PCC_NODE, &pcep_cli_pcc_pcc_peer_cmd); install_element(PCEP_PCC_NODE, &pcep_cli_pcc_pcc_msd_cmd); diff --git a/pathd/path_ted.c b/pathd/path_ted.c index 01ada92258..d17b5a0aab 100644 --- a/pathd/path_ted.c +++ b/pathd/path_ted.c @@ -516,16 +516,16 @@ uint32_t path_ted_config_write(struct vty *vty) { if (ted_state_g.enabled) { - vty_out(vty, " mpls-te on\n"); + vty_out(vty, " mpls-te on\n"); switch (ted_state_g.import) { case IMPORT_ISIS: - vty_out(vty, " mpls-te import isis\n"); + vty_out(vty, " mpls-te import isis\n"); break; case IMPORT_OSPFv2: - vty_out(vty, " mpls-te import ospfv2\n"); + vty_out(vty, " mpls-te import ospfv2\n"); break; case IMPORT_OSPFv3: - vty_out(vty, " mpls-te import ospfv3\n"); + vty_out(vty, " mpls-te import ospfv3\n"); break; default: break; diff --git a/pathd/pathd.h b/pathd/pathd.h index f790a0e3c9..81d7aa9105 100644 --- a/pathd/pathd.h +++ b/pathd/pathd.h @@ -34,6 +34,8 @@ DECLARE_MGROUP(PATHD); +DECLARE_HOOK(pathd_srte_config_write, (struct vty *vty), (vty)); + enum srte_protocol_origin { SRTE_ORIGIN_UNDEFINED = 0, SRTE_ORIGIN_PCEP = 1, diff --git a/pbrd/pbr_main.c b/pbrd/pbr_main.c index 7861559034..c7cbbb4462 100644 --- a/pbrd/pbr_main.c +++ b/pbrd/pbr_main.c @@ -153,7 +153,6 @@ int main(int argc, char **argv, char **envp) break; default: frr_help_exit(1); - break; } } diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c index 2936d1e346..d083b9d2b0 100644 --- a/pbrd/pbr_vty.c +++ b/pbrd/pbr_vty.c @@ -1118,7 +1118,7 @@ static int pbr_interface_config_write(struct vty *vty) pbr_map_write_interfaces(vty, ifp); - vty_endframe(vty, "!\n"); + vty_endframe(vty, "exit\n!\n"); } } @@ -1184,6 +1184,7 @@ static int pbr_vty_map_config_write_sequence(struct vty *vty, pbrms_nexthop_group_write_individual_nexthop(vty, pbrms); } + vty_out(vty, "exit\n"); vty_out(vty, "!\n"); return 1; } @@ -1229,7 +1230,7 @@ void pbr_vty_init(void) { cmd_variable_handler_register(pbr_map_name); - vrf_cmd_init(NULL, &pbr_privs); + vrf_cmd_init(NULL); if_cmd_init(pbr_interface_config_write); diff --git a/pimd/pim_bsm.c b/pimd/pim_bsm.c index 0181e885fd..f2845ee6e1 100644 --- a/pimd/pim_bsm.c +++ b/pimd/pim_bsm.c @@ -419,9 +419,12 @@ static void pim_instate_pend_list(struct bsgrp_node *bsgrp_node) active = bsm_rpinfos_first(bsgrp_node->bsrp_list); /* Remove nodes with hold time 0 & check if list still has a head */ - frr_each_safe (bsm_rpinfos, bsgrp_node->partial_bsrp_list, pend) - if (is_hold_time_zero(pend)) + frr_each_safe (bsm_rpinfos, bsgrp_node->partial_bsrp_list, pend) { + if (is_hold_time_zero(pend)) { bsm_rpinfos_del(bsgrp_node->partial_bsrp_list, pend); + pim_bsm_rpinfo_free(pend); + } + } pend = bsm_rpinfos_first(bsgrp_node->partial_bsrp_list); diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index 273100c492..1238e03a5b 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -7179,7 +7179,7 @@ DEFPY (pim_register_accept_list, DEFUN (ip_pim_joinprune_time, ip_pim_joinprune_time_cmd, - "ip pim join-prune-interval (5-600)", + "ip pim join-prune-interval (1-65535)", IP_STR "pim multicast routing\n" "Join Prune Send Interval\n" @@ -7193,27 +7193,22 @@ DEFUN (ip_pim_joinprune_time, DEFUN (no_ip_pim_joinprune_time, no_ip_pim_joinprune_time_cmd, - "no ip pim join-prune-interval (5-600)", + "no ip pim join-prune-interval [(1-65535)]", NO_STR IP_STR "pim multicast routing\n" "Join Prune Send Interval\n" - "Seconds\n") + IGNORED_IN_NO_STR) { - char jp_default_timer[5]; - - snprintf(jp_default_timer, sizeof(jp_default_timer), "%d", - PIM_DEFAULT_T_PERIODIC); - nb_cli_enqueue_change(vty, "/frr-pim:pim/join-prune-interval", - NB_OP_MODIFY, jp_default_timer); + NB_OP_DESTROY, NULL); return nb_cli_apply_changes(vty, NULL); } DEFUN (ip_pim_register_suppress, ip_pim_register_suppress_cmd, - "ip pim register-suppress-time (5-60000)", + "ip pim register-suppress-time (1-65535)", IP_STR "pim multicast routing\n" "Register Suppress Timer\n" @@ -7227,27 +7222,22 @@ DEFUN (ip_pim_register_suppress, DEFUN (no_ip_pim_register_suppress, no_ip_pim_register_suppress_cmd, - "no ip pim register-suppress-time (5-60000)", + "no ip pim register-suppress-time [(1-65535)]", NO_STR IP_STR "pim multicast routing\n" "Register Suppress Timer\n" - "Seconds\n") + IGNORED_IN_NO_STR) { - char rs_default_timer[5]; - - snprintf(rs_default_timer, sizeof(rs_default_timer), "%d", - PIM_REGISTER_SUPPRESSION_TIME_DEFAULT); - nb_cli_enqueue_change(vty, "/frr-pim:pim/register-suppress-time", - NB_OP_MODIFY, rs_default_timer); + NB_OP_DESTROY, NULL); return nb_cli_apply_changes(vty, NULL); } DEFUN (ip_pim_rp_keep_alive, ip_pim_rp_keep_alive_cmd, - "ip pim rp keep-alive-timer (31-60000)", + "ip pim rp keep-alive-timer (1-65535)", IP_STR "pim multicast routing\n" "Rendevous Point\n" @@ -7274,20 +7264,26 @@ DEFUN (ip_pim_rp_keep_alive, DEFUN (no_ip_pim_rp_keep_alive, no_ip_pim_rp_keep_alive_cmd, - "no ip pim rp keep-alive-timer (31-60000)", + "no ip pim rp keep-alive-timer [(1-65535)]", NO_STR IP_STR "pim multicast routing\n" "Rendevous Point\n" "Keep alive Timer\n" - "Seconds\n") + IGNORED_IN_NO_STR) { const char *vrfname; - char rp_ka_timer[5]; + char rp_ka_timer[6]; char rp_ka_timer_xpath[XPATH_MAXLEN]; + uint v; - snprintf(rp_ka_timer, sizeof(rp_ka_timer), "%d", - PIM_RP_KEEPALIVE_PERIOD); + /* RFC4601 */ + v = yang_dnode_get_uint16(vty->candidate_config->dnode, + "/frr-pim:pim/register-suppress-time"); + v = 3 * v + PIM_REGISTER_PROBE_TIME_DEFAULT; + if (v > UINT16_MAX) + v = UINT16_MAX; + snprintf(rp_ka_timer, sizeof(rp_ka_timer), "%u", v); vrfname = pim_cli_get_vrf_name(vty); if (vrfname == NULL) @@ -7306,7 +7302,7 @@ DEFUN (no_ip_pim_rp_keep_alive, DEFUN (ip_pim_keep_alive, ip_pim_keep_alive_cmd, - "ip pim keep-alive-timer (31-60000)", + "ip pim keep-alive-timer (1-65535)", IP_STR "pim multicast routing\n" "Keep alive Timer\n" @@ -7331,19 +7327,16 @@ DEFUN (ip_pim_keep_alive, DEFUN (no_ip_pim_keep_alive, no_ip_pim_keep_alive_cmd, - "no ip pim keep-alive-timer (31-60000)", + "no ip pim keep-alive-timer [(1-65535)]", NO_STR IP_STR "pim multicast routing\n" "Keep alive Timer\n" - "Seconds\n") + IGNORED_IN_NO_STR) { const char *vrfname; - char ka_timer[5]; char ka_timer_xpath[XPATH_MAXLEN]; - snprintf(ka_timer, sizeof(ka_timer), "%d", PIM_KEEPALIVE_PERIOD); - vrfname = pim_cli_get_vrf_name(vty); if (vrfname == NULL) return CMD_WARNING_CONFIG_FAILED; @@ -7352,15 +7345,14 @@ DEFUN (no_ip_pim_keep_alive, "frr-pim:pimd", "pim", vrfname); strlcat(ka_timer_xpath, "/keep-alive-timer", sizeof(ka_timer_xpath)); - nb_cli_enqueue_change(vty, ka_timer_xpath, NB_OP_MODIFY, - ka_timer); + nb_cli_enqueue_change(vty, ka_timer_xpath, NB_OP_DESTROY, NULL); return nb_cli_apply_changes(vty, NULL); } DEFUN (ip_pim_packets, ip_pim_packets_cmd, - "ip pim packets (1-100)", + "ip pim packets (1-255)", IP_STR "pim multicast routing\n" "packets to process at one time per fd\n" @@ -7374,27 +7366,21 @@ DEFUN (ip_pim_packets, DEFUN (no_ip_pim_packets, no_ip_pim_packets_cmd, - "no ip pim packets (1-100)", + "no ip pim packets [(1-255)]", NO_STR IP_STR "pim multicast routing\n" "packets to process at one time per fd\n" - "Number of packets\n") + IGNORED_IN_NO_STR) { - char default_packet[3]; - - snprintf(default_packet, sizeof(default_packet), "%d", - PIM_DEFAULT_PACKET_PROCESS); - - nb_cli_enqueue_change(vty, "/frr-pim:pim/packets", NB_OP_MODIFY, - default_packet); + nb_cli_enqueue_change(vty, "/frr-pim:pim/packets", NB_OP_DESTROY, NULL); return nb_cli_apply_changes(vty, NULL); } DEFPY (igmp_group_watermark, igmp_group_watermark_cmd, - "ip igmp watermark-warn (10-60000)$limit", + "ip igmp watermark-warn (1-65535)$limit", IP_STR IGMP_STR "Configure group limit for watermark warning\n" @@ -7408,12 +7394,12 @@ DEFPY (igmp_group_watermark, DEFPY (no_igmp_group_watermark, no_igmp_group_watermark_cmd, - "no ip igmp watermark-warn [(10-60000)$limit]", + "no ip igmp watermark-warn [(1-65535)$limit]", NO_STR IP_STR IGMP_STR "Unconfigure group limit for watermark warning\n" - "Group count to generate watermark warning\n") + IGNORED_IN_NO_STR) { PIM_DECLVAR_CONTEXT(vrf, pim); pim->igmp_watermark_limit = 0; @@ -8146,7 +8132,7 @@ DEFUN (interface_no_ip_igmp_join, DEFUN (interface_ip_igmp_query_interval, interface_ip_igmp_query_interval_cmd, - "ip igmp query-interval (1-1800)", + "ip igmp query-interval (1-65535)", IP_STR IFACE_IGMP_STR IFACE_IGMP_QUERY_INTERVAL_STR @@ -8174,19 +8160,14 @@ DEFUN (interface_ip_igmp_query_interval, DEFUN (interface_no_ip_igmp_query_interval, interface_no_ip_igmp_query_interval_cmd, - "no ip igmp query-interval", + "no ip igmp query-interval [(1-65535)]", NO_STR IP_STR IFACE_IGMP_STR - IFACE_IGMP_QUERY_INTERVAL_STR) + IFACE_IGMP_QUERY_INTERVAL_STR + IGNORED_IN_NO_STR) { - char default_query_interval[5]; - - snprintf(default_query_interval, sizeof(default_query_interval), "%d", - IGMP_GENERAL_QUERY_INTERVAL); - - nb_cli_enqueue_change(vty, "./query-interval", NB_OP_MODIFY, - default_query_interval); + nb_cli_enqueue_change(vty, "./query-interval", NB_OP_DESTROY, NULL); return nb_cli_apply_changes(vty, "./frr-igmp:igmp"); } @@ -8222,7 +8203,7 @@ DEFUN (interface_no_ip_igmp_version, DEFUN (interface_ip_igmp_query_max_response_time, interface_ip_igmp_query_max_response_time_cmd, - "ip igmp query-max-response-time (10-250)", + "ip igmp query-max-response-time (1-65535)", IP_STR IFACE_IGMP_STR IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_STR @@ -8251,27 +8232,21 @@ DEFUN (interface_ip_igmp_query_max_response_time, DEFUN (interface_no_ip_igmp_query_max_response_time, interface_no_ip_igmp_query_max_response_time_cmd, - "no ip igmp query-max-response-time (10-250)", + "no ip igmp query-max-response-time [(1-65535)]", NO_STR IP_STR IFACE_IGMP_STR IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_STR - "Time for response in deci-seconds\n") + IGNORED_IN_NO_STR) { - char default_query_max_response_time[4]; - - snprintf(default_query_max_response_time, - sizeof(default_query_max_response_time), - "%d", IGMP_QUERY_MAX_RESPONSE_TIME_DSEC); - - nb_cli_enqueue_change(vty, "./query-max-response-time", NB_OP_MODIFY, - default_query_max_response_time); + nb_cli_enqueue_change(vty, "./query-max-response-time", NB_OP_DESTROY, + NULL); return nb_cli_apply_changes(vty, "./frr-igmp:igmp"); } DEFUN_HIDDEN (interface_ip_igmp_query_max_response_time_dsec, interface_ip_igmp_query_max_response_time_dsec_cmd, - "ip igmp query-max-response-time-dsec (10-250)", + "ip igmp query-max-response-time-dsec (1-65535)", IP_STR IFACE_IGMP_STR IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC_STR @@ -8299,27 +8274,22 @@ DEFUN_HIDDEN (interface_ip_igmp_query_max_response_time_dsec, DEFUN_HIDDEN (interface_no_ip_igmp_query_max_response_time_dsec, interface_no_ip_igmp_query_max_response_time_dsec_cmd, - "no ip igmp query-max-response-time-dsec", + "no ip igmp query-max-response-time-dsec [(1-65535)]", NO_STR IP_STR IFACE_IGMP_STR - IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC_STR) + IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC_STR + IGNORED_IN_NO_STR) { - char default_query_max_response_time[4]; - - snprintf(default_query_max_response_time, - sizeof(default_query_max_response_time), - "%d", IGMP_QUERY_MAX_RESPONSE_TIME_DSEC); - - nb_cli_enqueue_change(vty, "./query-max-response-time", NB_OP_MODIFY, - default_query_max_response_time); + nb_cli_enqueue_change(vty, "./query-max-response-time", NB_OP_DESTROY, + NULL); return nb_cli_apply_changes(vty, "./frr-igmp:igmp"); } DEFUN (interface_ip_igmp_last_member_query_count, interface_ip_igmp_last_member_query_count_cmd, - "ip igmp last-member-query-count (1-7)", + "ip igmp last-member-query-count (1-255)", IP_STR IFACE_IGMP_STR IFACE_IGMP_LAST_MEMBER_QUERY_COUNT_STR @@ -8347,26 +8317,22 @@ DEFUN (interface_ip_igmp_last_member_query_count, DEFUN (interface_no_ip_igmp_last_member_query_count, interface_no_ip_igmp_last_member_query_count_cmd, - "no ip igmp last-member-query-count", + "no ip igmp last-member-query-count [(1-255)]", NO_STR IP_STR IFACE_IGMP_STR - IFACE_IGMP_LAST_MEMBER_QUERY_COUNT_STR) + IFACE_IGMP_LAST_MEMBER_QUERY_COUNT_STR + IGNORED_IN_NO_STR) { - char default_robustness[2]; - - snprintf(default_robustness, sizeof(default_robustness), "%d", - IGMP_DEFAULT_ROBUSTNESS_VARIABLE); - - nb_cli_enqueue_change(vty, "./robustness-variable", NB_OP_MODIFY, - default_robustness); + nb_cli_enqueue_change(vty, "./robustness-variable", NB_OP_DESTROY, + NULL); return nb_cli_apply_changes(vty, "./frr-igmp:igmp"); } DEFUN (interface_ip_igmp_last_member_query_interval, interface_ip_igmp_last_member_query_interval_cmd, - "ip igmp last-member-query-interval (1-255)", + "ip igmp last-member-query-interval (1-65535)", IP_STR IFACE_IGMP_STR IFACE_IGMP_LAST_MEMBER_QUERY_INTERVAL_STR @@ -8394,20 +8360,15 @@ DEFUN (interface_ip_igmp_last_member_query_interval, DEFUN (interface_no_ip_igmp_last_member_query_interval, interface_no_ip_igmp_last_member_query_interval_cmd, - "no ip igmp last-member-query-interval", + "no ip igmp last-member-query-interval [(1-65535)]", NO_STR IP_STR IFACE_IGMP_STR - IFACE_IGMP_LAST_MEMBER_QUERY_INTERVAL_STR) + IFACE_IGMP_LAST_MEMBER_QUERY_INTERVAL_STR + IGNORED_IN_NO_STR) { - char default_last_member_query_count[4]; - - snprintf(default_last_member_query_count, - sizeof(default_last_member_query_count), - "%d", IGMP_SPECIFIC_QUERY_MAX_RESPONSE_TIME_DSEC); - - nb_cli_enqueue_change(vty, "./last-member-query-interval", NB_OP_MODIFY, - default_last_member_query_count); + nb_cli_enqueue_change(vty, "./last-member-query-interval", + NB_OP_DESTROY, NULL); return nb_cli_apply_changes(vty, "./frr-igmp:igmp"); } @@ -8437,13 +8398,7 @@ DEFUN (interface_no_ip_pim_drprio, "Revert the Designated Router Priority to default\n" "Old Value of the Priority\n") { - char default_priority[10]; - - snprintf(default_priority, sizeof(default_priority), "%d", - PIM_DEFAULT_DR_PRIORITY); - - nb_cli_enqueue_change(vty, "./dr-priority", NB_OP_MODIFY, - default_priority); + nb_cli_enqueue_change(vty, "./dr-priority", NB_OP_DESTROY, NULL); return nb_cli_apply_changes(vty, "./frr-pim:pim"); } @@ -8778,7 +8733,7 @@ DEFUN (interface_no_ip_mroute, DEFUN (interface_ip_pim_hello, interface_ip_pim_hello_cmd, - "ip pim hello (1-180) [(1-630)]", + "ip pim hello (1-65535) [(1-65535)]", IP_STR PIM_STR IFACE_PIM_HELLO_STR @@ -8813,21 +8768,15 @@ DEFUN (interface_ip_pim_hello, DEFUN (interface_no_ip_pim_hello, interface_no_ip_pim_hello_cmd, - "no ip pim hello [(1-180) [(1-630)]]", + "no ip pim hello [(1-65535) [(1-65535)]]", NO_STR IP_STR PIM_STR IFACE_PIM_HELLO_STR - IFACE_PIM_HELLO_TIME_STR - IFACE_PIM_HELLO_HOLD_STR) + IGNORED_IN_NO_STR + IGNORED_IN_NO_STR) { - char hello_default_timer[3]; - - snprintf(hello_default_timer, sizeof(hello_default_timer), "%d", - PIM_DEFAULT_HELLO_PERIOD); - - nb_cli_enqueue_change(vty, "./hello-interval", NB_OP_MODIFY, - hello_default_timer); + nb_cli_enqueue_change(vty, "./hello-interval", NB_OP_DESTROY, NULL); nb_cli_enqueue_change(vty, "./hello-holdtime", NB_OP_DESTROY, NULL); return nb_cli_apply_changes(vty, "./frr-pim:pim"); @@ -9634,10 +9583,10 @@ DEFUN (no_ip_pim_ucast_bsm, } #if HAVE_BFDD > 0 -DEFUN_HIDDEN( +DEFUN_HIDDEN ( ip_pim_bfd_param, ip_pim_bfd_param_cmd, - "ip pim bfd (2-255) (50-60000) (50-60000)", + "ip pim bfd (2-255) (1-65535) (1-65535)", IP_STR PIM_STR "Enables BFD support\n" @@ -9648,7 +9597,7 @@ DEFUN_HIDDEN( DEFUN( ip_pim_bfd_param, ip_pim_bfd_param_cmd, - "ip pim bfd (2-255) (50-60000) (50-60000)", + "ip pim bfd (2-255) (1-65535) (1-65535)", IP_STR PIM_STR "Enables BFD support\n" @@ -9687,7 +9636,10 @@ DEFUN_HIDDEN( #if HAVE_BFDD == 0 ALIAS(no_ip_pim_bfd, no_ip_pim_bfd_param_cmd, - "no ip pim bfd (2-255) (50-60000) (50-60000)", NO_STR IP_STR PIM_STR + "no ip pim bfd (2-255) (1-65535) (1-65535)", + NO_STR + IP_STR + PIM_STR "Enables BFD support\n" "Detect Multiplier\n" "Required min receive interval\n" @@ -9726,7 +9678,7 @@ DEFPY(ip_msdp_peer, ip_msdp_peer_cmd, } DEFPY(ip_msdp_timers, ip_msdp_timers_cmd, - "ip msdp timers (2-600)$keepalive (3-600)$holdtime [(1-600)$connretry]", + "ip msdp timers (1-65535)$keepalive (1-65535)$holdtime [(1-65535)$connretry]", IP_STR CFG_MSDP_STR "MSDP timers configuration\n" @@ -9757,6 +9709,35 @@ DEFPY(ip_msdp_timers, ip_msdp_timers_cmd, return CMD_SUCCESS; } +DEFPY(no_ip_msdp_timers, no_ip_msdp_timers_cmd, + "no ip msdp timers [(1-65535) (1-65535) [(1-65535)]]", + NO_STR + IP_STR + CFG_MSDP_STR + "MSDP timers configuration\n" + IGNORED_IN_NO_STR + IGNORED_IN_NO_STR + IGNORED_IN_NO_STR) +{ + const char *vrfname; + char xpath[XPATH_MAXLEN]; + + vrfname = pim_cli_get_vrf_name(vty); + if (vrfname == NULL) + return CMD_WARNING_CONFIG_FAILED; + + snprintf(xpath, sizeof(xpath), FRR_PIM_MSDP_XPATH, "frr-pim:pimd", + "pim", vrfname, "frr-routing:ipv4"); + + nb_cli_enqueue_change(vty, "./hold-time", NB_OP_DESTROY, NULL); + nb_cli_enqueue_change(vty, "./keep-alive", NB_OP_DESTROY, NULL); + nb_cli_enqueue_change(vty, "./connection-retry", NB_OP_DESTROY, NULL); + + nb_cli_apply_changes(vty, xpath); + + return CMD_SUCCESS; +} + DEFUN (no_ip_msdp_peer, no_ip_msdp_peer_cmd, "no ip msdp peer A.B.C.D", @@ -11381,6 +11362,8 @@ void pim_cmd_init(void) install_element(CONFIG_NODE, &ip_msdp_timers_cmd); install_element(VRF_NODE, &ip_msdp_timers_cmd); + install_element(CONFIG_NODE, &no_ip_msdp_timers_cmd); + install_element(VRF_NODE, &no_ip_msdp_timers_cmd); install_element(CONFIG_NODE, &ip_msdp_mesh_group_member_cmd); install_element(VRF_NODE, &ip_msdp_mesh_group_member_cmd); install_element(CONFIG_NODE, &no_ip_msdp_mesh_group_member_cmd); diff --git a/pimd/pim_igmpv3.c b/pimd/pim_igmpv3.c index afa2db5f15..bc67a1dd1d 100644 --- a/pimd/pim_igmpv3.c +++ b/pimd/pim_igmpv3.c @@ -499,6 +499,35 @@ static void allow(struct igmp_sock *igmp, struct in_addr from, struct igmp_group *group; int i; + if (num_sources == 0) { + /* + RFC 3376: 3.1. Socket-State + If the requested filter mode is INCLUDE *and* the requested + source list is empty, then the entry corresponding to the + requested interface and multicast address is deleted if + present. If no such entry is present, the request is ignored. + So, deleting the group present. + */ + group = find_group_by_addr(igmp, group_addr); + if (!group) { + return; + } + if (group->group_filtermode_isexcl) { + if (listcount(group->group_source_list) == 1) { + struct in_addr star = {.s_addr = INADDR_ANY}; + + source = igmp_find_source_by_addr(group, star); + if (source) + igmp_source_reset_gmi(igmp, group, + source); + } + } else { + igmp_group_delete(group); + } + + return; + } + /* non-existant group is created as INCLUDE {empty} */ group = igmp_add_group_by_addr(igmp, group_addr); if (!group) { @@ -529,15 +558,6 @@ static void allow(struct igmp_sock *igmp, struct in_addr from, igmp_source_reset_gmi(igmp, group, source); } /* scan received sources */ - - if ((num_sources == 0) && (group->group_filtermode_isexcl) - && (listcount(group->group_source_list) == 1)) { - struct in_addr star = {.s_addr = INADDR_ANY}; - - source = igmp_find_source_by_addr(group, star); - if (source) - igmp_source_reset_gmi(igmp, group, source); - } } void igmpv3_report_isin(struct igmp_sock *igmp, struct in_addr from, diff --git a/pimd/pim_instance.c b/pimd/pim_instance.c index 6dda66b79a..5322c48f67 100644 --- a/pimd/pim_instance.c +++ b/pimd/pim_instance.c @@ -220,7 +220,7 @@ static int pim_vrf_config_write(struct vty *vty) pim_global_config_write_worker(pim, vty); if (vrf->vrf_id != VRF_DEFAULT) - vty_endframe(vty, " exit-vrf\n!\n"); + vty_endframe(vty, "exit-vrf\n!\n"); } return 0; @@ -231,7 +231,7 @@ void pim_vrf_init(void) vrf_init(pim_vrf_new, pim_vrf_enable, pim_vrf_disable, pim_vrf_delete, NULL); - vrf_cmd_init(pim_vrf_config_write, &pimd_privs); + vrf_cmd_init(pim_vrf_config_write); } void pim_vrf_terminate(void) diff --git a/pimd/pim_instance.h b/pimd/pim_instance.h index 72c726690c..52ded08ae3 100644 --- a/pimd/pim_instance.h +++ b/pimd/pim_instance.h @@ -96,9 +96,9 @@ struct pim_router { int t_periodic; struct pim_assert_metric infinite_assert_metric; long rpf_cache_refresh_delay_msec; - int32_t register_suppress_time; + uint32_t register_suppress_time; int packet_process; - int32_t register_probe_time; + uint32_t register_probe_time; /* * What is the default vrf that we work in diff --git a/pimd/pim_main.c b/pimd/pim_main.c index 96132c4425..780595ca11 100644 --- a/pimd/pim_main.c +++ b/pimd/pim_main.c @@ -115,7 +115,6 @@ int main(int argc, char **argv, char **envp) break; default: frr_help_exit(1); - break; } } diff --git a/pimd/pim_mroute.c b/pimd/pim_mroute.c index ab6d8c17df..7743bcc510 100644 --- a/pimd/pim_mroute.c +++ b/pimd/pim_mroute.c @@ -668,19 +668,15 @@ static int pim_mroute_msg(struct pim_instance *pim, const char *buf, case IGMPMSG_WRONGVIF: return pim_mroute_msg_wrongvif(pim->mroute_socket, ifp, msg); - break; case IGMPMSG_NOCACHE: return pim_mroute_msg_nocache(pim->mroute_socket, ifp, msg); - break; case IGMPMSG_WHOLEPKT: return pim_mroute_msg_wholepkt(pim->mroute_socket, ifp, (const char *)msg); - break; case IGMPMSG_WRVIFWHOLE: return pim_mroute_msg_wrvifwhole( pim->mroute_socket, ifp, (const char *)msg); - break; default: break; } diff --git a/pimd/pim_nb_config.c b/pimd/pim_nb_config.c index bd5e215027..4b4c1ec7db 100644 --- a/pimd/pim_nb_config.c +++ b/pimd/pim_nb_config.c @@ -459,6 +459,10 @@ static void change_query_max_response_time(struct pim_interface *pim_ifp, struct listnode *sock_node; struct igmp_sock *igmp; + if (pim_ifp->igmp_query_max_response_time_dsec + == query_max_response_time_dsec) + return; + pim_ifp->igmp_query_max_response_time_dsec = query_max_response_time_dsec; @@ -556,8 +560,27 @@ int pim_join_prune_interval_modify(struct nb_cb_modify_args *args) */ int pim_register_suppress_time_modify(struct nb_cb_modify_args *args) { + uint16_t value; switch (args->event) { case NB_EV_VALIDATE: + value = yang_dnode_get_uint16(args->dnode, NULL); + /* + * As soon as this is non-constant it needs to be replaced with + * a yang_dnode_get to lookup the candidate value, *not* the + * operational value. Since the code has a field assigned and + * used for this value it should have YANG/CLI to set it too, + * otherwise just use the #define! + */ + /* RFC7761: 4.11. Timer Values */ + if (value <= router->register_probe_time * 2) { + snprintf( + args->errmsg, args->errmsg_len, + "Register suppress time (%u) must be more than " + "twice the register probe time (%u).", + value, router->register_probe_time); + return NB_ERR_VALIDATION; + } + break; case NB_EV_PREPARE: case NB_EV_ABORT: break; @@ -956,21 +979,13 @@ int pim_msdp_hold_time_modify(struct nb_cb_modify_args *args) switch (args->event) { case NB_EV_VALIDATE: - if (yang_dnode_get_uint32(args->dnode, NULL) - <= yang_dnode_get_uint32(args->dnode, "../keep-alive")) { - snprintf( - args->errmsg, args->errmsg_len, - "Hold time must be greater than keep alive interval"); - return NB_ERR_VALIDATION; - } - break; case NB_EV_PREPARE: case NB_EV_ABORT: break; case NB_EV_APPLY: vrf = nb_running_get_entry(args->dnode, NULL, true); pim = vrf->info; - pim->msdp.hold_time = yang_dnode_get_uint32(args->dnode, NULL); + pim->msdp.hold_time = yang_dnode_get_uint16(args->dnode, NULL); break; } @@ -988,21 +1003,13 @@ int pim_msdp_keep_alive_modify(struct nb_cb_modify_args *args) switch (args->event) { case NB_EV_VALIDATE: - if (yang_dnode_get_uint32(args->dnode, NULL) - >= yang_dnode_get_uint32(args->dnode, "../hold-time")) { - snprintf( - args->errmsg, args->errmsg_len, - "Keep alive must be less than hold time interval"); - return NB_ERR_VALIDATION; - } - break; case NB_EV_PREPARE: case NB_EV_ABORT: break; case NB_EV_APPLY: vrf = nb_running_get_entry(args->dnode, NULL, true); pim = vrf->info; - pim->msdp.keep_alive = yang_dnode_get_uint32(args->dnode, NULL); + pim->msdp.keep_alive = yang_dnode_get_uint16(args->dnode, NULL); break; } @@ -1027,7 +1034,7 @@ int pim_msdp_connection_retry_modify(struct nb_cb_modify_args *args) vrf = nb_running_get_entry(args->dnode, NULL, true); pim = vrf->info; pim->msdp.connection_retry = - yang_dnode_get_uint32(args->dnode, NULL); + yang_dnode_get_uint16(args->dnode, NULL); break; } @@ -2636,9 +2643,7 @@ int lib_interface_igmp_version_destroy(struct nb_cb_destroy_args *args) int lib_interface_igmp_query_interval_modify(struct nb_cb_modify_args *args) { struct interface *ifp; - struct pim_interface *pim_ifp; int query_interval; - int query_interval_dsec; switch (args->event) { case NB_EV_VALIDATE: @@ -2647,18 +2652,8 @@ int lib_interface_igmp_query_interval_modify(struct nb_cb_modify_args *args) break; case NB_EV_APPLY: ifp = nb_running_get_entry(args->dnode, NULL, true); - pim_ifp = ifp->info; query_interval = yang_dnode_get_uint16(args->dnode, NULL); - query_interval_dsec = 10 * query_interval; - if (query_interval_dsec <= - pim_ifp->igmp_query_max_response_time_dsec) { - snprintf(args->errmsg, args->errmsg_len, - "Can't set general query interval %d dsec <= query max response time %d dsec.", - query_interval_dsec, - pim_ifp->igmp_query_max_response_time_dsec); - return NB_ERR_INCONSISTENCY; - } - change_query_interval(pim_ifp, query_interval); + change_query_interval(ifp->info, query_interval); } return NB_OK; @@ -2671,9 +2666,7 @@ int lib_interface_igmp_query_max_response_time_modify( struct nb_cb_modify_args *args) { struct interface *ifp; - struct pim_interface *pim_ifp; int query_max_response_time_dsec; - int default_query_interval_dsec; switch (args->event) { case NB_EV_VALIDATE: @@ -2682,22 +2675,9 @@ int lib_interface_igmp_query_max_response_time_modify( break; case NB_EV_APPLY: ifp = nb_running_get_entry(args->dnode, NULL, true); - pim_ifp = ifp->info; query_max_response_time_dsec = - yang_dnode_get_uint8(args->dnode, NULL); - default_query_interval_dsec = - 10 * pim_ifp->igmp_default_query_interval; - - if (query_max_response_time_dsec - >= default_query_interval_dsec) { - snprintf(args->errmsg, args->errmsg_len, - "Can't set query max response time %d sec >= general query interval %d sec", - query_max_response_time_dsec, - pim_ifp->igmp_default_query_interval); - return NB_ERR_INCONSISTENCY; - } - - change_query_max_response_time(pim_ifp, + yang_dnode_get_uint16(args->dnode, NULL); + change_query_max_response_time(ifp->info, query_max_response_time_dsec); } @@ -2722,8 +2702,8 @@ int lib_interface_igmp_last_member_query_interval_modify( case NB_EV_APPLY: ifp = nb_running_get_entry(args->dnode, NULL, true); pim_ifp = ifp->info; - last_member_query_interval = yang_dnode_get_uint8(args->dnode, - NULL); + last_member_query_interval = + yang_dnode_get_uint16(args->dnode, NULL); pim_ifp->igmp_specific_query_max_response_time_dsec = last_member_query_interval; diff --git a/pimd/pim_pim.c b/pimd/pim_pim.c index e7ac0d4e5b..8c38cf6c4c 100644 --- a/pimd/pim_pim.c +++ b/pimd/pim_pim.c @@ -322,7 +322,6 @@ int pim_pim_packet(struct interface *ifp, uint8_t *buf, size_t len) } return -1; } - return -1; } static void pim_sock_read_on(struct interface *ifp); @@ -514,7 +513,7 @@ static int pim_msg_send_frame(int fd, char *buf, size_t len, { struct ip *ip = (struct ip *)buf; - while (sendto(fd, buf, len, MSG_DONTWAIT, dst, salen) < 0) { + if (sendto(fd, buf, len, MSG_DONTWAIT, dst, salen) < 0) { char dst_str[INET_ADDRSTRLEN]; switch (errno) { diff --git a/pimd/pim_rpf.c b/pimd/pim_rpf.c index 98944e8fed..66c6df65ad 100644 --- a/pimd/pim_rpf.c +++ b/pimd/pim_rpf.c @@ -419,8 +419,6 @@ int pim_rpf_addr_is_inaddr_none(struct pim_rpf *rpf) default: return 0; } - - return 0; } int pim_rpf_addr_is_inaddr_any(struct pim_rpf *rpf) @@ -434,8 +432,6 @@ int pim_rpf_addr_is_inaddr_any(struct pim_rpf *rpf) default: return 0; } - - return 0; } int pim_rpf_is_same(struct pim_rpf *rpf1, struct pim_rpf *rpf2) diff --git a/pimd/pim_upstream.c b/pimd/pim_upstream.c index 2b674b4234..d21c7b4008 100644 --- a/pimd/pim_upstream.c +++ b/pimd/pim_upstream.c @@ -1800,12 +1800,16 @@ void pim_upstream_start_register_stop_timer(struct pim_upstream *up, THREAD_OFF(up->t_rs_timer); if (!null_register) { - uint32_t lower = (0.5 * PIM_REGISTER_SUPPRESSION_PERIOD); - uint32_t upper = (1.5 * PIM_REGISTER_SUPPRESSION_PERIOD); - time = lower + (frr_weak_random() % (upper - lower + 1)) - - PIM_REGISTER_PROBE_PERIOD; + uint32_t lower = (0.5 * router->register_suppress_time); + uint32_t upper = (1.5 * router->register_suppress_time); + time = lower + (frr_weak_random() % (upper - lower + 1)); + /* Make sure we don't wrap around */ + if (time >= router->register_probe_time) + time -= router->register_probe_time; + else + time = 0; } else - time = PIM_REGISTER_PROBE_PERIOD; + time = router->register_probe_time; if (PIM_DEBUG_PIM_TRACE) { zlog_debug( diff --git a/pimd/pim_vty.c b/pimd/pim_vty.c index 95882cf58f..e4dec9ee8e 100644 --- a/pimd/pim_vty.c +++ b/pimd/pim_vty.c @@ -451,7 +451,7 @@ int pim_interface_config_write(struct vty *vty) pim_bfd_write_config(vty, ifp); ++writes; } - vty_endframe(vty, "!\n"); + vty_endframe(vty, "exit\n!\n"); ++writes; } } diff --git a/redhat/frr.spec.in b/redhat/frr.spec.in index fbe4c4a1f1..066c45f55c 100644 --- a/redhat/frr.spec.in +++ b/redhat/frr.spec.in @@ -451,7 +451,7 @@ ln -s %{_sbindir}/frrinit.sh %{buildroot}%{_initddir}/frr %endif install %{zeb_src}/tools/etc/frr/daemons %{buildroot}%{_sysconfdir}/frr -install %{zeb_src}/tools/etc/frr/frr.conf %{buildroot}%{_sysconfdir}/frr +install %{zeb_src}/tools/etc/frr/frr.conf %{buildroot}%{_sysconfdir}/frr/frr.conf.template install -m644 %{zeb_rh_src}/frr.pam %{buildroot}%{_sysconfdir}/pam.d/frr install -m644 %{zeb_rh_src}/frr.logrotate %{buildroot}%{_sysconfdir}/logrotate.d/frr install -d -m750 %{buildroot}%{rundir} @@ -558,24 +558,15 @@ zebra_spec_add_service fabricd 2618/tcp "Fabricd vty" /sbin/install-info %{_infodir}/frr.info.gz %{_infodir}/dir -# Create dummy files if they don't exist so basic functions can be used. +# Create dummy config file if they don't exist so basic functions can be used. if [ ! -e %{configdir}/zebra.conf ]; then - echo "hostname `hostname`" > %{configdir}/zebra.conf + # per daemon configs exist + mv %{configdir}/frr.conf.template %{configdir}/frr.conf %if 0%{?frr_user:1} - chown %{frr_user}:%{frr_user} %{configdir}/zebra.conf* + chown %{frr_user}:%{frr_user} %{configdir}/frr.conf %endif - chmod 640 %{configdir}/zebra.conf* + chmod 640 %{configdir}/frr.conf fi -for daemon in %{all_daemons} ; do - if [ x"${daemon}" != x"" ] ; then - if [ ! -e %{configdir}/${daemon}.conf ]; then - touch %{configdir}/${daemon}.conf - %if 0%{?frr_user:1} - chown %{frr_user}:%{frr_user} %{configdir}/${daemon}.conf* - %endif - fi - fi -done %if 0%{?frr_user:1} chown %{frr_user}:%{frr_user} %{configdir}/daemons %endif diff --git a/ripd/rip_main.c b/ripd/rip_main.c index 2e5eec9844..7b9146b13a 100644 --- a/ripd/rip_main.c +++ b/ripd/rip_main.c @@ -161,7 +161,6 @@ int main(int argc, char **argv) break; default: frr_help_exit(1); - break; } } diff --git a/ripd/ripd.c b/ripd/ripd.c index 3d1427c3b6..37f4b57431 100644 --- a/ripd/ripd.c +++ b/ripd/ripd.c @@ -3281,6 +3281,8 @@ static int config_write_rip(struct vty *vty) /* Interface routemap configuration */ config_write_if_rmap(vty, rip->if_rmap_ctx); + vty_out(vty, "exit\n"); + write = 1; } @@ -3696,7 +3698,7 @@ void rip_vrf_init(void) vrf_init(rip_vrf_new, rip_vrf_enable, rip_vrf_disable, rip_vrf_delete, rip_vrf_enable); - vrf_cmd_init(NULL, &ripd_privs); + vrf_cmd_init(NULL); } void rip_vrf_terminate(void) diff --git a/ripngd/ripng_main.c b/ripngd/ripng_main.c index a5d837aa55..34cd4ab0a7 100644 --- a/ripngd/ripng_main.c +++ b/ripngd/ripng_main.c @@ -163,7 +163,6 @@ int main(int argc, char **argv) break; default: frr_help_exit(1); - break; } } diff --git a/ripngd/ripngd.c b/ripngd/ripngd.c index cbd2c22893..4f5c8e7760 100644 --- a/ripngd/ripngd.c +++ b/ripngd/ripngd.c @@ -2270,6 +2270,8 @@ static int ripng_config_write(struct vty *vty) config_write_distribute(vty, ripng->distribute_ctx); config_write_if_rmap(vty, ripng->if_rmap_ctx); + vty_out(vty, "exit\n"); + write = 1; } @@ -2692,7 +2694,7 @@ void ripng_vrf_init(void) vrf_init(ripng_vrf_new, ripng_vrf_enable, ripng_vrf_disable, ripng_vrf_delete, ripng_vrf_enable); - vrf_cmd_init(NULL, &ripngd_privs); + vrf_cmd_init(NULL); } void ripng_vrf_terminate(void) diff --git a/sharpd/sharp_main.c b/sharpd/sharp_main.c index 75cf145385..a646c313e4 100644 --- a/sharpd/sharp_main.c +++ b/sharpd/sharp_main.c @@ -171,7 +171,6 @@ int main(int argc, char **argv, char **envp) break; default: frr_help_exit(1); - break; } } diff --git a/snapcraft/README.snap_build.md b/snapcraft/README.snap_build.md index e43f63f2d9..f4a38e7f7f 100644 --- a/snapcraft/README.snap_build.md +++ b/snapcraft/README.snap_build.md @@ -1,7 +1,6 @@ Building your own FRRouting Snap ======================================== -(Tested on Ubuntu 16.04 with Snap Version 2, does not work on Ubuntu 15.x -which uses earlier versions of snaps) +(Tested on Ubuntu 18.04) 1. Install snapcraft: @@ -100,6 +99,7 @@ All the commands are prefixed with frr. frr.staticd-debug frr.bfdd-debug frr.fabricd-debug + frr.pathd-debug vtysh can be accessed as frr.vtysh (Make sure you have /snap/bin in your path). If access as `vtysh` instead of `frr.vtysh` is needed, you can enable it diff --git a/snapcraft/README.usage.md b/snapcraft/README.usage.md index 6a0864c8c5..7abc0f6ded 100644 --- a/snapcraft/README.usage.md +++ b/snapcraft/README.usage.md @@ -18,7 +18,8 @@ ie for `ospf6d` (OSPFv3): systemctl enable snap.frr.ospf6d.service The daemons are: `ripd`, `ripngd`, `ospfd`, `ospf6d`, `isisd`, `bgpd`, -`pimd`, `ldpd`, `eigrpd`, `babeld`, `nhrpd`, `bfdd`, `zebra` +`pimd`, `ldpd`, `eigrpd`, `babeld`, `nhrpd`, `bfdd`, `vrrpd`, `pbrd`, +`pathd`, `fabricd`, `staticd`, `zebra` Commands defined by this snap ----------------------------- diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/__init__.py b/snapcraft/defaults/pathd.conf.default index e69de29bb2..e69de29bb2 100644 --- a/tests/topotests/bgp-evpn-overlay-index-gateway/__init__.py +++ b/snapcraft/defaults/pathd.conf.default diff --git a/snapcraft/scripts/Makefile b/snapcraft/scripts/Makefile index 0435b3bc52..5aedddcf69 100644 --- a/snapcraft/scripts/Makefile +++ b/snapcraft/scripts/Makefile @@ -19,6 +19,7 @@ install: install -D -m 0755 bfdd-service $(DESTDIR)/bin/ install -D -m 0755 fabricd-service $(DESTDIR)/bin/ install -D -m 0755 vrrpd-service $(DESTDIR)/bin/ + install -D -m 0755 pathd-service $(DESTDIR)/bin/ install -D -m 0755 set-options $(DESTDIR)/bin/ install -D -m 0755 show_version $(DESTDIR)/bin/ diff --git a/snapcraft/scripts/pathd-service b/snapcraft/scripts/pathd-service new file mode 100644 index 0000000000..6473c59d97 --- /dev/null +++ b/snapcraft/scripts/pathd-service @@ -0,0 +1,13 @@ +#!/bin/sh + +set -e -x + +if ! [ -e $SNAP_DATA/pathd.conf ]; then + cp $SNAP/etc/frr/pathd.conf.default $SNAP_DATA/pathd.conf +fi +exec $SNAP/sbin/pathd \ + -f $SNAP_DATA/pathd.conf \ + --pid_file $SNAP_DATA/pathd.pid \ + --socket $SNAP_DATA/zsock \ + --vty_socket $SNAP_DATA + diff --git a/snapcraft/snapcraft.yaml.in b/snapcraft/snapcraft.yaml.in index 1836f34979..51252ede0c 100644 --- a/snapcraft/snapcraft.yaml.in +++ b/snapcraft/snapcraft.yaml.in @@ -4,11 +4,12 @@ summary: FRRouting BGP/OSPFv2/OSPFv3/ISIS/RIP/RIPng/PIM/LDP/EIGRP/BFD routing da description: BGP/OSPFv2/OSPFv3/ISIS/RIP/RIPng/PIM/LDP/EIGRP/BFD routing daemon FRRouting (FRR) is free software which manages TCP/IP based routing protocols. It supports BGP4, BGP4+, OSPFv2, OSPFv3, IS-IS, RIPv1, RIPv2, - RIPng, PIM, LDP, Babel, EIGRP, PBR (Policy-based routing), BFD and OpenFabric - as well as the IPv6 versions of these. + RIPng, PIM, LDP, Babel, EIGRP, PBR (Policy-based routing), PATHD (Segment + routing), BFD and OpenFabric as well as the IPv6 versions of these. FRRouting (frr) is a fork of Quagga. confinement: strict grade: devel +base: core18 apps: vtysh: @@ -141,6 +142,13 @@ apps: - network - network-bind - network-control + pathd: + command: bin/pathd-service + daemon: simple + plugs: + - network + - network-bind + - network-control set: command: bin/set-options zebra-debug: @@ -245,6 +253,13 @@ apps: - network - network-bind - network-control + pathd-debug: + command: sbin/pathd -f $SNAP_DATA/pathd.conf --pid_file $SNAP_DATA/pathd.pid --socket $SNAP_DATA/zsock --vty_socket $SNAP_DATA + plugs: + - network + - network-bind + - network-control + parts: rtrlib: build-packages: @@ -254,6 +269,7 @@ parts: - libssh-dev stage-packages: - libssh-4 + - zlib1g prime: - lib/librtr.so* - usr/lib/x86_64-linux-gnu/libssh.so* @@ -268,12 +284,12 @@ parts: - cmake - make - gcc - - libpcre3-dev + - libpcre2-dev stage-packages: - - libpcre3 + - libpcre2-8-0 source: https://github.com/CESNET/libyang.git source-type: git - source-tag: v1.0.184 + source-tag: v2.0.7 plugin: cmake configflags: - -DCMAKE_INSTALL_PREFIX:PATH=/usr @@ -298,7 +314,6 @@ parts: - imagemagick - ghostscript - groff - - hardening-wrapper - libpcre3-dev - chrpath - pkg-config @@ -307,6 +322,7 @@ parts: - bison - flex - python3-dev + - libprotobuf-c-dev - protobuf-c-compiler - python3-sphinx stage-packages: @@ -315,12 +331,20 @@ parts: - logrotate - libcap2 - libtinfo5 - - libreadline6 - - libjson-c2 + - libreadline7 + - libjson-c3 - libc-ares2 - libatm1 - libprotobuf-c1 - libdb5.3 + - libacl1 + - libattr1 + - libaudit1 + - libcap-ng0 + - libpam0g + - libpcre3 + - libselinux1 + - libxtables12 plugin: autotools source: ../frr-@PACKAGE_VERSION@.tar.gz configflags: @@ -367,6 +391,7 @@ parts: bfdd.conf.default: etc/frr/bfdd.conf.default fabricd.conf.default: etc/frr/fabricd.conf.default vrrpd.conf.default: etc/frr/vrrpd.conf.default + pathd.conf.default: etc/frr/pathd.conf.default vtysh.conf.default: etc/frr/vtysh.conf.default staticd.conf.default: etc/frr/staticd.conf.default frr-scripts: @@ -376,6 +401,8 @@ parts: stage-packages: - telnet - traceroute + - libgcc1 + - libstdc++6 plugin: make source: helpers prime: @@ -390,8 +417,3 @@ parts: README.snap_build.md: doc/README.snap_build.md extra_version_info.txt: doc/extra_version_info.txt -passthrough: - layout: - /usr/lib/x86_64-linux-gnu/libyang1: - bind: $SNAP/usr/lib/x86_64-linux-gnu/libyang1 - diff --git a/staticd/static_main.c b/staticd/static_main.c index 1561b91efb..f7a15462a0 100644 --- a/staticd/static_main.c +++ b/staticd/static_main.c @@ -149,7 +149,6 @@ int main(int argc, char **argv, char **envp) break; default: frr_help_exit(1); - break; } } diff --git a/staticd/static_nb_config.c b/staticd/static_nb_config.c index 582b838ce4..deeca97b0e 100644 --- a/staticd/static_nb_config.c +++ b/staticd/static_nb_config.c @@ -471,13 +471,6 @@ int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_cr yang_afi_safi_identity2value(afi_safi, &afi, &safi); rn = static_add_route(afi, safi, &prefix, NULL, s_vrf); - if (!rn) { - flog_warn( - EC_LIB_NB_CB_CONFIG_APPLY, - "route node %s creation failed", - yang_dnode_get_string(args->dnode, "./prefix")); - return NB_ERR; - } if (vrf->vrf_id == VRF_UNKNOWN) snprintf( args->errmsg, args->errmsg_len, @@ -752,13 +745,6 @@ int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_sr afi = family2afi(src_prefix.family); src_rn = static_add_route(afi, safi, &rn->p, &src_prefix, s_vrf); - if (!src_rn) { - flog_warn(EC_LIB_NB_CB_CONFIG_APPLY, - "src rn %s creation failed", - yang_dnode_get_string(args->dnode, - "./src-prefix")); - return NB_ERR; - } nb_running_set_entry(args->dnode, src_rn); break; } diff --git a/staticd/static_routes.c b/staticd/static_routes.c index 0ca0011d00..77a10092f8 100644 --- a/staticd/static_routes.c +++ b/staticd/static_routes.c @@ -126,8 +126,7 @@ struct route_node *static_add_route(afi_t afi, safi_t safi, struct prefix *p, struct static_route_info *si; struct route_table *stable = svrf->stable[afi][safi]; - if (!stable) - return NULL; + assert(stable); /* Lookup static route prefix. */ rn = srcdest_rnode_get(stable, p, src_p); diff --git a/staticd/static_vrf.c b/staticd/static_vrf.c index 96e5d37d68..740d904690 100644 --- a/staticd/static_vrf.c +++ b/staticd/static_vrf.c @@ -164,7 +164,7 @@ static int static_vrf_config_write(struct vty *vty) SAFI_UNICAST, "ipv6 route"); if (vrf->vrf_id != VRF_DEFAULT) - vty_endframe(vty, " exit-vrf\n!\n"); + vty_endframe(vty, "exit-vrf\n!\n"); } return 0; @@ -175,7 +175,7 @@ void static_vrf_init(void) vrf_init(static_vrf_new, static_vrf_enable, static_vrf_disable, static_vrf_delete, NULL); - vrf_cmd_init(static_vrf_config_write, &static_privs); + vrf_cmd_init(static_vrf_config_write); } void static_vrf_terminate(void) diff --git a/staticd/static_vty.c b/staticd/static_vty.c index ea09054a23..f16b40a23f 100644 --- a/staticd/static_vty.c +++ b/staticd/static_vty.c @@ -325,7 +325,8 @@ static int static_route_leak(struct vty *vty, const char *svrf, dnode = yang_dnode_get(vty->candidate_config->dnode, ab_xpath); if (!dnode) { - /* Silently return */ + vty_out(vty, + "%% Refusing to remove a non-existent route\n"); return CMD_SUCCESS; } diff --git a/tests/isisd/test_isis_lspdb.c b/tests/isisd/test_isis_lspdb.c index 244922ea4e..cc95c4a132 100644 --- a/tests/isisd/test_isis_lspdb.c +++ b/tests/isisd/test_isis_lspdb.c @@ -23,15 +23,15 @@ static void test_lsp_build_list_nonzero_ht(void) struct lspdb_head _lspdb, *lspdb = &_lspdb; lsp_db_init(&_lspdb); - struct isis_lsp *lsp1 = lsp_new(area, lsp_id1, 6000, 0, 0, 0, NULL, - ISIS_LEVEL2); + struct isis_lsp *lsp1 = + lsp_new(area, lsp_id1, 6000, 1, 0, 0, NULL, ISIS_LEVEL2); - lsp_insert(lspdb, lsp1); + lspdb_add(lspdb, lsp1); - struct isis_lsp *lsp2 = lsp_new(area, lsp_id2, 6000, 0, 0, 0, NULL, - ISIS_LEVEL2); + struct isis_lsp *lsp2 = + lsp_new(area, lsp_id2, 6000, 1, 0, 0, NULL, ISIS_LEVEL2); - lsp_insert(lspdb, lsp2); + lspdb_add(lspdb, lsp2); struct list *list = list_new(); diff --git a/tests/lib/cli/test_cli.refout.in b/tests/lib/cli/test_cli.refout.in index 8f9959cc47..1f38e08b20 100644 --- a/tests/lib/cli/test_cli.refout.in +++ b/tests/lib/cli/test_cli.refout.in @@ -315,7 +315,6 @@ domainname test.domain !
!
!
-line vty
!
end
test# conf t
@@ -332,7 +331,6 @@ domainname test.domain !
!
!
-line vty
!
end
foohost(config)# diff --git a/tests/lib/test_frrscript.c b/tests/lib/test_frrscript.c index 7b23045978..4c5d8d2928 100644 --- a/tests/lib/test_frrscript.c +++ b/tests/lib/test_frrscript.c @@ -62,6 +62,14 @@ int main(int argc, char **argv) long long *ansptr = frrscript_get_result(fs, "fact", "ans", lua_tointegerp); assert(*ansptr == 120); + + /* check consecutive call + get_result without re-loading */ + n = 4; + result = frrscript_call(fs, "fact", ("n", &n)); + assert(result == 0); + ansptr = frrscript_get_result(fs, "fact", "ans", lua_tointegerp); + assert(*ansptr == 24); + XFREE(MTYPE_SCRIPT_RES, ansptr); /* Negative testing */ diff --git a/tests/subdir.am b/tests/subdir.am index 45236287cf..b0be63c695 100644 --- a/tests/subdir.am +++ b/tests/subdir.am @@ -35,8 +35,10 @@ if OSPFD TESTS_OSPFD = \ tests/ospfd/test_ospf_spf \ # end +IGNORE_OSPFD = else TESTS_OSPFD = +IGNORE_OSPFD = --ignore=ospfd/ endif if OSPF6D @@ -486,7 +488,7 @@ endif .PHONY: tests/tests.xml tests/tests.xml: $(check_PROGRAMS) - ( cd tests; $(PYTHON) ../$(srcdir)/tests/runtests.py --junitxml=tests.xml -v ../$(srcdir)/tests $(IGNORE_BGPD) $(IGNORE_ISISD) $(IGNORE_OSPF6D); ) + ( cd tests; $(PYTHON) ../$(srcdir)/tests/runtests.py --junitxml=tests.xml -v ../$(srcdir)/tests $(IGNORE_BGPD) $(IGNORE_ISISD) $(IGNORE_OSPFD) $(IGNORE_OSPF6D); ) check: tests/tests.xml clean-local: clean-tests diff --git a/tests/topotests/bfd_vrf_topo1/r1/bfdd.conf b/tests/topotests/bfd_vrf_topo1/r1/bfdd.conf index 5e736a7fcc..fd57b2c4d5 100644 --- a/tests/topotests/bfd_vrf_topo1/r1/bfdd.conf +++ b/tests/topotests/bfd_vrf_topo1/r1/bfdd.conf @@ -4,7 +4,7 @@ debug bfd peer debug bfd zebra ! bfd - peer 192.168.0.2 vrf r1-cust1 + peer 192.168.0.2 vrf r1-bfd-cust1 echo-mode no shutdown ! diff --git a/tests/topotests/bfd_vrf_topo1/r1/bgpd.conf b/tests/topotests/bfd_vrf_topo1/r1/bgpd.conf index 5bb45b9863..cf72f30d66 100644 --- a/tests/topotests/bfd_vrf_topo1/r1/bgpd.conf +++ b/tests/topotests/bfd_vrf_topo1/r1/bgpd.conf @@ -1,4 +1,4 @@ -router bgp 101 vrf r1-cust1 +router bgp 101 vrf r1-bfd-cust1 no bgp ebgp-requires-policy no bgp network import-check neighbor 192.168.0.2 remote-as 102 diff --git a/tests/topotests/bfd_vrf_topo1/r1/zebra.conf b/tests/topotests/bfd_vrf_topo1/r1/zebra.conf index fcd1e7db17..62ed36fdb8 100644 --- a/tests/topotests/bfd_vrf_topo1/r1/zebra.conf +++ b/tests/topotests/bfd_vrf_topo1/r1/zebra.conf @@ -1,3 +1,3 @@ -interface r1-eth0 vrf r1-cust1 +interface r1-eth0 vrf r1-bfd-cust1 ip address 192.168.0.1/24 ! diff --git a/tests/topotests/bfd_vrf_topo1/r2/bfdd.conf b/tests/topotests/bfd_vrf_topo1/r2/bfdd.conf index 94f502c7d9..e5539f14e5 100644 --- a/tests/topotests/bfd_vrf_topo1/r2/bfdd.conf +++ b/tests/topotests/bfd_vrf_topo1/r2/bfdd.conf @@ -4,13 +4,13 @@ debug bfd peer debug bfd zebra ! bfd - peer 192.168.0.1 vrf r2-cust1 + peer 192.168.0.1 vrf r2-bfd-cust1 receive-interval 1000 transmit-interval 500 echo-mode no shutdown ! - peer 192.168.1.1 vrf r2-cust1 + peer 192.168.1.1 vrf r2-bfd-cust1 echo-mode no shutdown ! diff --git a/tests/topotests/bfd_vrf_topo1/r2/bgpd.conf b/tests/topotests/bfd_vrf_topo1/r2/bgpd.conf index b2aac74685..132011cf86 100644 --- a/tests/topotests/bfd_vrf_topo1/r2/bgpd.conf +++ b/tests/topotests/bfd_vrf_topo1/r2/bgpd.conf @@ -1,4 +1,4 @@ -router bgp 102 vrf r2-cust1 +router bgp 102 vrf r2-bfd-cust1 no bgp ebgp-requires-policy no bgp network import-check neighbor 192.168.0.1 remote-as 101 diff --git a/tests/topotests/bfd_vrf_topo1/r2/zebra.conf b/tests/topotests/bfd_vrf_topo1/r2/zebra.conf index daffd1912e..1e817b19f6 100644 --- a/tests/topotests/bfd_vrf_topo1/r2/zebra.conf +++ b/tests/topotests/bfd_vrf_topo1/r2/zebra.conf @@ -1,9 +1,9 @@ -interface r2-eth0 vrf r2-cust1 +interface r2-eth0 vrf r2-bfd-cust1 ip address 192.168.0.2/24 ! -interface r2-eth1 vrf r2-cust1 +interface r2-eth1 vrf r2-bfd-cust1 ip address 192.168.1.2/24 ! -interface r2-eth2 vrf r2-cust1 +interface r2-eth2 vrf r2-bfd-cust1 ip address 192.168.2.2/24 ! diff --git a/tests/topotests/bfd_vrf_topo1/r3/bfdd.conf b/tests/topotests/bfd_vrf_topo1/r3/bfdd.conf index 76910ac927..e1f53e1abc 100644 --- a/tests/topotests/bfd_vrf_topo1/r3/bfdd.conf +++ b/tests/topotests/bfd_vrf_topo1/r3/bfdd.conf @@ -4,7 +4,7 @@ debug bfd peer debug bfd zebra ! bfd - peer 192.168.1.2 vrf r3-cust1 + peer 192.168.1.2 vrf r3-bfd-cust1 echo-interval 100 echo-mode no shutdown diff --git a/tests/topotests/bfd_vrf_topo1/r3/bgpd.conf b/tests/topotests/bfd_vrf_topo1/r3/bgpd.conf index 1d7c730395..f764647920 100644 --- a/tests/topotests/bfd_vrf_topo1/r3/bgpd.conf +++ b/tests/topotests/bfd_vrf_topo1/r3/bgpd.conf @@ -1,4 +1,4 @@ -router bgp 103 vrf r3-cust1 +router bgp 103 vrf r3-bfd-cust1 no bgp ebgp-requires-policy no bgp network import-check neighbor 192.168.1.2 remote-as 102 diff --git a/tests/topotests/bfd_vrf_topo1/r3/zebra.conf b/tests/topotests/bfd_vrf_topo1/r3/zebra.conf index f727c2d633..e67345948e 100644 --- a/tests/topotests/bfd_vrf_topo1/r3/zebra.conf +++ b/tests/topotests/bfd_vrf_topo1/r3/zebra.conf @@ -1,3 +1,3 @@ -interface r3-eth0 vrf r3-cust1 +interface r3-eth0 vrf r3-bfd-cust1 ip address 192.168.1.1/24 ! diff --git a/tests/topotests/bfd_vrf_topo1/r4/bfdd.conf b/tests/topotests/bfd_vrf_topo1/r4/bfdd.conf index 63d0da7805..9ef2023b21 100644 --- a/tests/topotests/bfd_vrf_topo1/r4/bfdd.conf +++ b/tests/topotests/bfd_vrf_topo1/r4/bfdd.conf @@ -4,7 +4,7 @@ debug bfd peer debug bfd zebra ! bfd - peer 192.168.2.2 vrf r4-cust1 + peer 192.168.2.2 vrf r4-bfd-cust1 transmit-interval 2000 receive-interval 2000 no shutdown diff --git a/tests/topotests/bfd_vrf_topo1/r4/bgpd.conf b/tests/topotests/bfd_vrf_topo1/r4/bgpd.conf index f34035d460..03353e45e3 100644 --- a/tests/topotests/bfd_vrf_topo1/r4/bgpd.conf +++ b/tests/topotests/bfd_vrf_topo1/r4/bgpd.conf @@ -1,4 +1,4 @@ -router bgp 104 vrf r4-cust1 +router bgp 104 vrf r4-bfd-cust1 no bgp ebgp-requires-policy no bgp network import-check neighbor 192.168.2.2 remote-as 102 diff --git a/tests/topotests/bfd_vrf_topo1/r4/zebra.conf b/tests/topotests/bfd_vrf_topo1/r4/zebra.conf index 69770dd2bf..15d3ec1d90 100644 --- a/tests/topotests/bfd_vrf_topo1/r4/zebra.conf +++ b/tests/topotests/bfd_vrf_topo1/r4/zebra.conf @@ -1,3 +1,3 @@ -interface r4-eth0 vrf r4-cust1 +interface r4-eth0 vrf r4-bfd-cust1 ip address 192.168.2.1/24 ! diff --git a/tests/topotests/bfd_vrf_topo1/test_bfd_vrf_topo1.py b/tests/topotests/bfd_vrf_topo1/test_bfd_vrf_topo1.py index 8a1ffe085d..a342997912 100644 --- a/tests/topotests/bfd_vrf_topo1/test_bfd_vrf_topo1.py +++ b/tests/topotests/bfd_vrf_topo1/test_bfd_vrf_topo1.py @@ -95,20 +95,18 @@ def setup_module(mod): logger.info("Testing with VRF Namespace support") cmds = [ - "if [ -e /var/run/netns/{0}-cust1 ] ; then ip netns del {0}-cust1 ; fi", - "ip netns add {0}-cust1", - "ip link set dev {0}-eth0 netns {0}-cust1", - "ip netns exec {0}-cust1 ifconfig {0}-eth0 up", + "if [ -e /var/run/netns/{0}-bfd-cust1 ] ; then ip netns del {0}-bfd-cust1 ; fi", + "ip netns add {0}-bfd-cust1", + "ip link set dev {0}-eth0 netns {0}-bfd-cust1 up", ] cmds2 = [ - "ip link set dev {0}-eth1 netns {0}-cust1", - "ip netns exec {0}-cust1 ifconfig {0}-eth1 up", - "ip link set dev {0}-eth2 netns {0}-cust1", - "ip netns exec {0}-cust1 ifconfig {0}-eth2 up", + "ip link set dev {0}-eth1 netns {0}-bfd-cust1", + "ip netns exec {0}-bfd-cust1 ip link set {0}-eth1 up", + "ip link set dev {0}-eth2 netns {0}-bfd-cust1 up", ] for rname, router in router_list.items(): - # create VRF rx-cust1 and link rx-eth0 to rx-cust1 + # create VRF rx-bfd-cust1 and link rx-eth0 to rx-bfd-cust1 for cmd in cmds: output = tgen.net[rname].cmd(cmd.format(rname)) if rname == "r2": @@ -138,11 +136,11 @@ def teardown_module(_mod): # move back rx-eth0 to default VRF # delete rx-vrf cmds = [ - "ip netns exec {0}-cust1 ip link set {0}-eth0 netns 1", - "ip netns delete {0}-cust1", + "ip netns exec {0}-bfd-cust1 ip link set {0}-eth0 netns 1", + "ip netns delete {0}-bfd-cust1", ] cmds2 = [ - "ip netns exec {0}-cust1 ip link set {0}-eth1 netns 1", + "ip netns exec {0}-bfd-cust1 ip link set {0}-eth1 netns 1", "ip netns exec {0}-cust2 ip link set {0}-eth1 netns 1", ] @@ -189,7 +187,7 @@ def test_bgp_convergence(): test_func = partial( topotest.router_json_cmp, router, - "show ip bgp vrf {}-cust1 summary json".format(router.name), + "show ip bgp vrf {}-bfd-cust1 summary json".format(router.name), expected, ) _, res = topotest.run_and_expect(test_func, None, count=125, wait=1.0) @@ -211,7 +209,7 @@ def test_bgp_fast_convergence(): test_func = partial( topotest.router_json_cmp, router, - "show ip bgp vrf {}-cust1 json".format(router.name), + "show ip bgp vrf {}-bfd-cust1 json".format(router.name), expected, ) _, res = topotest.run_and_expect(test_func, None, count=40, wait=1) @@ -231,7 +229,7 @@ def test_bfd_fast_convergence(): # Disable r2-eth0 link router2 = tgen.gears["r2"] topotest.interface_set_status( - router2, "r2-eth0", ifaceaction=False, vrf_name="r2-cust1" + router2, "r2-eth0", ifaceaction=False, vrf_name="r2-bfd-cust1" ) # Wait the minimum time we can before checking that BGP/BFD @@ -286,7 +284,7 @@ def test_bgp_fast_reconvergence(): test_func = partial( topotest.router_json_cmp, router, - "show ip bgp vrf {}-cust1 json".format(router.name), + "show ip bgp vrf {}-bfd-cust1 json".format(router.name), expected, ) _, res = topotest.run_and_expect(test_func, None, count=16, wait=1) diff --git a/tests/topotests/bgp_basic_functionality_topo1/test_bgp_basic_functionality.py b/tests/topotests/bgp_basic_functionality_topo1/test_bgp_basic_functionality.py index 4753c49397..3623e89dcb 100644 --- a/tests/topotests/bgp_basic_functionality_topo1/test_bgp_basic_functionality.py +++ b/tests/topotests/bgp_basic_functionality_topo1/test_bgp_basic_functionality.py @@ -208,6 +208,9 @@ def test_modify_and_delete_router_id(request): tc_name = request.node.name write_test_header(tc_name) + # Creating configuration from JSON + reset_config_on_routers(tgen) + # Modify router id input_dict = { "r1": {"bgp": {"router_id": "12.12.12.12"}}, @@ -252,6 +255,9 @@ def test_bgp_config_with_4byte_as_number(request): tc_name = request.node.name write_test_header(tc_name) + # Creating configuration from JSON + reset_config_on_routers(tgen) + input_dict = { "r1": {"bgp": {"local_as": 131079}}, "r2": {"bgp": {"local_as": 131079}}, @@ -283,6 +289,9 @@ def test_BGP_config_with_invalid_ASN_p2(request): tc_name = request.node.name write_test_header(tc_name) + # Creating configuration from JSON + reset_config_on_routers(tgen) + # Api call to modify AS number input_dict = { "r1": { @@ -307,11 +316,18 @@ def test_BGP_config_with_invalid_ASN_p2(request): }, } result = modify_as_number(tgen, topo, input_dict) - try: - assert result is True - except AssertionError: - logger.info("Expected behaviour: {}".format(result)) - logger.info("BGP config is not created because of invalid ASNs") + assert result is not True, ( + "Expected BGP config is not created because of invalid ASNs: {}".format( + result + ) + ) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + result = verify_bgp_convergence(tgen, topo) + if result != True: + assert False, "Testcase " + tc_name + " :Failed \n Error: {}".format(result) write_test_footer(tc_name) @@ -331,6 +347,13 @@ def test_BGP_config_with_2byteAS_and_4byteAS_number_p1(request): tc_name = request.node.name write_test_header(tc_name) + # Creating configuration from JSON + reset_config_on_routers(tgen) + + result = verify_bgp_convergence(tgen, topo) + if result != True: + assert False, "Testcase " + tc_name + " :Failed \n Error: {}".format(result) + # Api call to modify AS number input_dict = { "r1": {"bgp": {"local_as": 131079}}, @@ -586,7 +609,8 @@ def test_BGP_attributes_with_vrf_default_keyword_p0(request): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - # reset_config_on_routers(tgen) + # Creating configuration from JSON + reset_config_on_routers(tgen) step("Configure static routes and redistribute in BGP on R3") for addr_type in ADDR_TYPES: diff --git a/tests/topotests/bgp_community_alias/r1/bgpd.conf b/tests/topotests/bgp_community_alias/r1/bgpd.conf index a6366204e8..13b57ad243 100644 --- a/tests/topotests/bgp_community_alias/r1/bgpd.conf +++ b/tests/topotests/bgp_community_alias/r1/bgpd.conf @@ -3,6 +3,8 @@ bgp community alias 65001:1 community-r2-1 bgp community alias 65002:2 community-r2-2 bgp community alias 65001:1:1 large-community-r2-1 ! +bgp large-community-list expanded r2 seq 5 permit _65001:1:1_ +! router bgp 65001 no bgp ebgp-requires-policy neighbor 192.168.1.2 remote-as external diff --git a/tests/topotests/bgp_community_alias/test_bgp-community-alias.py b/tests/topotests/bgp_community_alias/test_bgp-community-alias.py index 6aadff1cfa..26933a7992 100644 --- a/tests/topotests/bgp_community_alias/test_bgp-community-alias.py +++ b/tests/topotests/bgp_community_alias/test_bgp-community-alias.py @@ -138,6 +138,17 @@ def test_bgp_community_alias(): success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) assert result is None, "Cannot see BGP prefixes by community alias at r1" + def _bgp_show_prefixes_by_large_community_list(router): + output = json.loads( + router.vtysh_cmd("show bgp ipv4 unicast large-community-list r2 json") + ) + expected = {"routes": {"172.16.16.1/32": [{"valid": True}]}} + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_show_prefixes_by_large_community_list, router) + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Cannot see BGP prefixes by large community list at r1" + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vni_routes_base.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vni_routes_base.json index 2eeebad4b0..2eeebad4b0 100644 --- a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vni_routes_base.json +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vni_routes_base.json diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vni_routes_no_rt2.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vni_routes_no_rt2.json index 419bcc3dd9..419bcc3dd9 100644 --- a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vni_routes_no_rt2.json +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vni_routes_no_rt2.json diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vni_routes_no_rt5.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vni_routes_no_rt5.json index 2eeebad4b0..2eeebad4b0 100644 --- a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vni_routes_no_rt5.json +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vni_routes_no_rt5.json diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv4_base.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv4_base.json index 833f98657b..833f98657b 100644 --- a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv4_base.json +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv4_base.json diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv4_no_rt2.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv4_no_rt2.json index 833f98657b..833f98657b 100644 --- a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv4_no_rt2.json +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv4_no_rt2.json diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv4_no_rt5.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv4_no_rt5.json index 4a292bddbe..4a292bddbe 100644 --- a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv4_no_rt5.json +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv4_no_rt5.json diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv6_base.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv6_base.json index 3dc3fcf9cb..3dc3fcf9cb 100644 --- a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv6_base.json +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv6_base.json diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv6_no_rt2.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv6_no_rt2.json index 3dc3fcf9cb..3dc3fcf9cb 100644 --- a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv6_no_rt2.json +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv6_no_rt2.json diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv6_no_rt5.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv6_no_rt5.json index 6c11d894eb..6c11d894eb 100644 --- a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv6_no_rt5.json +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv6_no_rt5.json diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgpd.conf b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgpd.conf index 63aa99a832..63aa99a832 100644 --- a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgpd.conf +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgpd.conf diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra.conf b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra.conf index 99a2e89ef3..99a2e89ef3 100644 --- a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra.conf +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra.conf diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv4_base.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv4_base.json index 2dcf35d91b..2dcf35d91b 100644 --- a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv4_base.json +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv4_base.json diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv4_no_rt2.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv4_no_rt2.json index 2dcf35d91b..2dcf35d91b 100644 --- a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv4_no_rt2.json +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv4_no_rt2.json diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv4_no_rt5.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv4_no_rt5.json index 9c3091dc50..9c3091dc50 100644 --- a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv4_no_rt5.json +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv4_no_rt5.json diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv6_base.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv6_base.json index 229c927656..229c927656 100644 --- a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv6_base.json +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv6_base.json diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv6_no_rt2.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv6_no_rt2.json index 229c927656..229c927656 100644 --- a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv6_no_rt2.json +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv6_no_rt2.json diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv6_no_rt5.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv6_no_rt5.json index 94f82e6d4c..94f82e6d4c 100644 --- a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv6_no_rt5.json +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv6_no_rt5.json diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vni_routes_base.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vni_routes_base.json index 7b8d38e492..7b8d38e492 100644 --- a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vni_routes_base.json +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vni_routes_base.json diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vni_routes_no_rt2.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vni_routes_no_rt2.json index 6273b3e728..6273b3e728 100644 --- a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vni_routes_no_rt2.json +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vni_routes_no_rt2.json diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vni_routes_no_rt5.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vni_routes_no_rt5.json index 7b8d38e492..7b8d38e492 100644 --- a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vni_routes_no_rt5.json +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vni_routes_no_rt5.json diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv4_base.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv4_base.json index c03d70195f..c03d70195f 100644 --- a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv4_base.json +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv4_base.json diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv4_no_rt2.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv4_no_rt2.json index 7f1b8d2ef4..7f1b8d2ef4 100644 --- a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv4_no_rt2.json +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv4_no_rt2.json diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv4_no_rt5.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv4_no_rt5.json index 52e4311635..52e4311635 100644 --- a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv4_no_rt5.json +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv4_no_rt5.json diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv6_base.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv6_base.json index 1d90c9c798..1d90c9c798 100644 --- a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv6_base.json +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv6_base.json diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv6_no_rt2.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv6_no_rt2.json index a0e63c6e25..a0e63c6e25 100644 --- a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv6_no_rt2.json +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv6_no_rt2.json diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv6_no_rt5.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv6_no_rt5.json index 789fe69b28..789fe69b28 100644 --- a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv6_no_rt5.json +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv6_no_rt5.json diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgpd.conf b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgpd.conf index 59fee15dfc..59fee15dfc 100644 --- a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgpd.conf +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgpd.conf diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra.conf b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra.conf index b78cdcc512..b78cdcc512 100644 --- a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra.conf +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra.conf diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv4_base.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv4_base.json index b3a3640be4..b3a3640be4 100644 --- a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv4_base.json +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv4_base.json diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv4_no_rt2.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv4_no_rt2.json index 996fe52f44..996fe52f44 100644 --- a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv4_no_rt2.json +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv4_no_rt2.json diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv4_no_rt5.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv4_no_rt5.json index 996fe52f44..996fe52f44 100644 --- a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv4_no_rt5.json +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv4_no_rt5.json diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv6_base.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv6_base.json index d5be22a2ba..d5be22a2ba 100644 --- a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv6_base.json +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv6_base.json diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv6_no_rt2.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv6_no_rt2.json index 94f82e6d4c..94f82e6d4c 100644 --- a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv6_no_rt2.json +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv6_no_rt2.json diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv6_no_rt5.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv6_no_rt5.json index 94f82e6d4c..94f82e6d4c 100644 --- a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv6_no_rt5.json +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv6_no_rt5.json diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/__init__.py b/tests/topotests/bgp_evpn_overlay_index_gateway/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/__init__.py diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/host1/bgpd.conf b/tests/topotests/bgp_evpn_overlay_index_gateway/host1/bgpd.conf index 7608ec95c3..7608ec95c3 100644 --- a/tests/topotests/bgp-evpn-overlay-index-gateway/host1/bgpd.conf +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/host1/bgpd.conf diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/host1/zebra.conf b/tests/topotests/bgp_evpn_overlay_index_gateway/host1/zebra.conf index c8c832e9d8..c8c832e9d8 100644 --- a/tests/topotests/bgp-evpn-overlay-index-gateway/host1/zebra.conf +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/host1/zebra.conf diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/host2/bgpd.conf b/tests/topotests/bgp_evpn_overlay_index_gateway/host2/bgpd.conf index cdf4cb4feb..cdf4cb4feb 100644 --- a/tests/topotests/bgp-evpn-overlay-index-gateway/host2/bgpd.conf +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/host2/bgpd.conf diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/host2/zebra.conf b/tests/topotests/bgp_evpn_overlay_index_gateway/host2/zebra.conf index 9135545c58..9135545c58 100644 --- a/tests/topotests/bgp-evpn-overlay-index-gateway/host2/zebra.conf +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/host2/zebra.conf diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/test_bgp_evpn_overlay_index_gateway.py b/tests/topotests/bgp_evpn_overlay_index_gateway/test_bgp_evpn_overlay_index_gateway.py index a411f13d2e..a411f13d2e 100755 --- a/tests/topotests/bgp-evpn-overlay-index-gateway/test_bgp_evpn_overlay_index_gateway.py +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/test_bgp_evpn_overlay_index_gateway.py diff --git a/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2.py b/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2.py index e7ce216042..83bf4fcc18 100644 --- a/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2.py +++ b/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2.py @@ -249,6 +249,8 @@ def configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut, peer): This function groups the repetitive function calls into one function. """ + logger.info("configure_gr_followed_by_clear: dut %s peer %s", dut, peer) + result = create_router_bgp(tgen, topo, input_dict) assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) @@ -766,9 +768,7 @@ def test_BGP_GR_10_p2(request): # Creating configuration from JSON reset_config_on_routers(tgen) - logger.info( - "[Step 1] : Test Setup " "[Helper Mode]R3-----R1[Restart Mode] initialized" - ) + step("Test Setup: [Helper Mode]R3-----R1[Restart Mode] initialized") # Configure graceful-restart input_dict = { @@ -847,6 +847,8 @@ def test_BGP_GR_10_p2(request): configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r3") for addr_type in ADDR_TYPES: + step("Verifying GR config and operational state for addr_type {}".format(addr_type)) + result = verify_graceful_restart( tgen, topo, addr_type, input_dict, dut="r1", peer="r3" ) @@ -870,7 +872,7 @@ def test_BGP_GR_10_p2(request): # verify multi address family result = verify_gr_address_family( - tgen, topo, addr_type, "ipv4Unicast", dut="r1" + tgen, topo, addr_type, "ipv4Unicast", dut="r1", peer="r3", ) assert result is True, "Testcase {} : Failed \n Error {}".format( tc_name, result @@ -878,7 +880,7 @@ def test_BGP_GR_10_p2(request): # verify multi address family result = verify_gr_address_family( - tgen, topo, addr_type, "ipv6Unicast", dut="r1" + tgen, topo, addr_type, "ipv6Unicast", dut="r1", peer="r3", ) assert result is True, "Testcase {} : Failed \n Error {}".format( tc_name, result @@ -886,7 +888,7 @@ def test_BGP_GR_10_p2(request): # verify multi address family result = verify_gr_address_family( - tgen, topo, addr_type, "ipv4Unicast", dut="r3" + tgen, topo, addr_type, "ipv4Unicast", dut="r3", peer="r1", ) assert result is True, "Testcase {} : Failed \n Error {}".format( tc_name, result @@ -894,12 +896,14 @@ def test_BGP_GR_10_p2(request): # verify multi address family result = verify_gr_address_family( - tgen, topo, addr_type, "ipv6Unicast", dut="r3" + tgen, topo, addr_type, "ipv6Unicast", dut="r3", peer="r1", ) assert result is True, "Testcase {} : Failed \n Error {}".format( tc_name, result ) + step("Killing bgpd on r1") + # Kill BGPd daemon on R1 kill_router_daemons(tgen, "r1", ["bgpd"]) @@ -917,6 +921,8 @@ def test_BGP_GR_10_p2(request): tc_name, result ) + step("Starting bgpd on r1") + # Start BGPd daemon on R1 start_router_daemons(tgen, "r1", ["bgpd"]) @@ -1671,7 +1677,7 @@ def test_BGP_GR_26_p2(request): # verify multi address family result = verify_gr_address_family( - tgen, topo, addr_type, "ipv4Unicast", dut="r1" + tgen, topo, addr_type, "ipv4Unicast", dut="r1", peer="r3", ) assert result is True, "Testcase {} : Failed \n Error {}".format( tc_name, result @@ -1679,7 +1685,7 @@ def test_BGP_GR_26_p2(request): # verify multi address family result = verify_gr_address_family( - tgen, topo, addr_type, "ipv6Unicast", dut="r1" + tgen, topo, addr_type, "ipv6Unicast", dut="r1", peer="r3", ) assert result is True, "Testcase {} : Failed \n Error {}".format( tc_name, result @@ -1687,7 +1693,7 @@ def test_BGP_GR_26_p2(request): # verify multi address family result = verify_gr_address_family( - tgen, topo, addr_type, "ipv4Unicast", dut="r3" + tgen, topo, addr_type, "ipv4Unicast", dut="r3", peer="r1", ) assert result is True, "Testcase {} : Failed \n Error {}".format( tc_name, result @@ -1695,7 +1701,7 @@ def test_BGP_GR_26_p2(request): # verify multi address family result = verify_gr_address_family( - tgen, topo, addr_type, "ipv6Unicast", dut="r3" + tgen, topo, addr_type, "ipv6Unicast", dut="r3", peer="r1", ) assert result is True, "Testcase {} : Failed \n Error {}".format( tc_name, result diff --git a/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ebgp_ibgp_nbr.json b/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ebgp_ibgp_nbr.json new file mode 100644 index 0000000000..7f928b932b --- /dev/null +++ b/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ebgp_ibgp_nbr.json @@ -0,0 +1,85 @@ +{ + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24, "ipv6": "fd00::", "v6mask": 64}, + "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:DB8:F::", "v6mask": 128}, + "routers": { + "r0": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r1-link2": {"ipv4": "auto", "ipv6": "auto"}, + "r1-link3": {"ipv4": "auto", "ipv6": "auto"}, + "r1-link4": {"ipv4": "auto", "ipv6": "auto"}, + "r1-link5": {"ipv4": "auto", "ipv6": "auto"}} + }, + "r1": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r0-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r0-link2": {"ipv4": "auto", "ipv6": "auto"}, + "r0-link3": {"ipv4": "auto", "ipv6": "auto"}, + "r0-link4": {"ipv4": "auto", "ipv6": "auto"}, + "r0-link5": {"ipv4": "auto", "ipv6": "auto"}, + "r2-link0": {"ipv4": "auto", "ipv6": "auto"}}, + "bgp": { + "local_as": "100", + "default_ipv4_unicast": "False", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link0": { + "activate": "ipv4", + "capability": "extended-nexthop" + } + } + } + } + } + } + }}}, + "r2": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1-link0": {"ipv4": "auto", "ipv6": "auto"}, + "r3": {"ipv4": "auto", "ipv6": "auto"}, + "r4": {"ipv4": "auto", "ipv6": "auto"}}, + "bgp": { + "local_as": "200", + "default_ipv4_unicast": "False", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link0": { + "activate": "ipv4", + "capability": "extended-nexthop" + } + } + }, + "r3": {"dest_link": {"r2": {}}}} + } + } + }}}, + "r3": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2": {"ipv4": "auto", "ipv6": "auto"}}, + "bgp": { + "local_as": "200", + "default_ipv4_unicast": "False", + "address_family": { + "ipv6": {"unicast": {"neighbor": {"r2": {"dest_link": {"r3": {}}}}}} + }}}, + "r4": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2": {"ipv4": "auto", "ipv6": "auto"}} + }}} diff --git a/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ebgp_nbr.json b/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ebgp_nbr.json new file mode 100644 index 0000000000..8e0f448fe4 --- /dev/null +++ b/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ebgp_nbr.json @@ -0,0 +1,95 @@ +{ + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24, "ipv6": "fd00::", "v6mask": 64}, + "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:DB8:F::", "v6mask": 128}, + "routers": { + "r0": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r1-link2": {"ipv4": "auto", "ipv6": "auto"}, + "r1-link3": {"ipv4": "auto", "ipv6": "auto"}, + "r1-link4": {"ipv4": "auto", "ipv6": "auto"}, + "r1-link5": {"ipv4": "auto", "ipv6": "auto"}} + }, + "r1": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r0-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r0-link2": {"ipv4": "auto", "ipv6": "auto"}, + "r0-link3": {"ipv4": "auto", "ipv6": "auto"}, + "r0-link4": {"ipv4": "auto", "ipv6": "auto"}, + "r0-link5": {"ipv4": "auto", "ipv6": "auto"}, + "r2-link0": {"ipv4": "auto", "ipv6": "auto"}}, + "bgp": { + "local_as": "100", + "default_ipv4_unicast": "False", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link0": { + "capability": "extended-nexthop", + "activate": "ipv4" + } + } + } + } + } + } + }}}, + "r2": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1-link0": {"ipv4": "auto", "ipv6": "auto"}, + "r3": {"ipv4": "auto", "ipv6": "auto"}, + "r4": {"ipv4": "auto", "ipv6": "auto"}}, + "bgp": { + "local_as": "200", + "default_ipv4_unicast": "False", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": {"dest_link": {"r2": {"activate": "ipv4"}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link0": { + "capability": "extended-nexthop", + "activate": "ipv4" + } + } + }, + "r3": {"dest_link": {"r2": {}}}} + } + }}}}, + "r3": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2": {"ipv4": "auto", "ipv6": "auto"}}, + "bgp": { + "local_as": "300", + "default_ipv4_unicast": "False", + "address_family": { + "ipv6": {"unicast": {"neighbor": {"r2": {"dest_link": {"r3": {}}}}}} + }}}, + "r4": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2": {"ipv4": "auto", "ipv6": "auto"}}, + "bgp": { + "local_as": "400", + "address_family": { + "ipv4": {"unicast": {"neighbor": {"r2": {"dest_link": {"r4": {}}}}}} + }}}}} diff --git a/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ebgp_unnumbered_nbr.json b/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ebgp_unnumbered_nbr.json new file mode 100644 index 0000000000..72d3a93018 --- /dev/null +++ b/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ebgp_unnumbered_nbr.json @@ -0,0 +1,97 @@ +{ + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24, "ipv6": "fd00::", "v6mask": 64}, + "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:DB8:F::", "v6mask": 128}, + "routers": { + "r0": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r1-link2": {"ipv4": "auto", "ipv6": "auto"}, + "r1-link3": {"ipv4": "auto", "ipv6": "auto"}, + "r1-link4": {"ipv4": "auto", "ipv6": "auto"}, + "r1-link5": {"ipv4": "auto", "ipv6": "auto"}} + }, + "r1": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r0-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r0-link2": {"ipv4": "auto", "ipv6": "auto"}, + "r0-link3": {"ipv4": "auto", "ipv6": "auto"}, + "r0-link4": {"ipv4": "auto", "ipv6": "auto"}, + "r0-link5": {"ipv4": "auto", "ipv6": "auto"}, + "r2-link0": {"ipv4": "auto"}}, + "bgp": { + "local_as": "100", + "default_ipv4_unicast": "False", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link0": { + "activate": "ipv4", + "capability": "extended-nexthop", + "neighbor_type": "unnumbered" + } + } + } + } + } + } + }}}, + "r2": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1-link0": {"ipv4": "auto"}, + "r3": {"ipv4": "auto", "ipv6": "auto"}, + "r4": {"ipv4": "auto", "ipv6": "auto"}}, + "bgp": { + "local_as": "200", + "default_ipv4_unicast": "False", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": {"dest_link": {"r2": {"activate": "ipv4"}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link0": { + "activate": "ipv4", + "capability": "extended-nexthop", + "neighbor_type": "unnumbered" + } + } + }, + "r3": {"dest_link": {"r2": {}}}} + } + }}}}, + "r3": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2": {"ipv4": "auto", "ipv6": "auto"}}, + "bgp": { + "local_as": "300", + "default_ipv4_unicast": "False", + "address_family": { + "ipv6": {"unicast": {"neighbor": {"r2": {"dest_link": {"r3": {}}}}}} + }}}, + "r4": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2": {"ipv4": "auto", "ipv6": "auto"}}, + "bgp": { + "local_as": "400", + "address_family": { + "ipv4": {"unicast": {"neighbor": {"r2": {"dest_link": {"r4": {}}}}}} + }}}}} diff --git a/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ibgp_nbr.json b/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ibgp_nbr.json new file mode 100644 index 0000000000..a7ea0c811d --- /dev/null +++ b/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ibgp_nbr.json @@ -0,0 +1,95 @@ +{ + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24, "ipv6": "fd00::", "v6mask": 64}, + "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:DB8:F::", "v6mask": 128}, + "routers": { + "r0": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r1-link2": {"ipv4": "auto", "ipv6": "auto"}, + "r1-link3": {"ipv4": "auto", "ipv6": "auto"}, + "r1-link4": {"ipv4": "auto", "ipv6": "auto"}, + "r1-link5": {"ipv4": "auto", "ipv6": "auto"}} + }, + "r1": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r0-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r0-link2": {"ipv4": "auto", "ipv6": "auto"}, + "r0-link3": {"ipv4": "auto", "ipv6": "auto"}, + "r0-link4": {"ipv4": "auto", "ipv6": "auto"}, + "r0-link5": {"ipv4": "auto", "ipv6": "auto"}, + "r2-link0": {"ipv4": "auto", "ipv6": "auto"}}, + "bgp": { + "local_as": "100", + "default_ipv4_unicast": "False", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link0": { + "capability": "extended-nexthop", + "activate": "ipv4" + } + } + } + } + } + } + }}}, + "r2": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1-link0": {"ipv4": "auto", "ipv6": "auto"}, + "r3": {"ipv4": "auto", "ipv6": "auto"}, + "r4": {"ipv4": "auto", "ipv6": "auto"}}, + "bgp": { + "local_as": "100", + "default_ipv4_unicast": "False", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": {"dest_link": {"r2": {"activate": "ipv4"}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link0": { + "capability": "extended-nexthop", + "activate": "ipv4" + } + } + }, + "r3": {"dest_link": {"r2": {}}}} + } + }}}}, + "r3": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2": {"ipv4": "auto", "ipv6": "auto"}}, + "bgp": { + "local_as": "300", + "default_ipv4_unicast": "False", + "address_family": { + "ipv6": {"unicast": {"neighbor": {"r2": {"dest_link": {"r3": {}}}}}} + }}}, + "r4": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2": {"ipv4": "auto", "ipv6": "auto"}}, + "bgp": { + "local_as": "400", + "address_family": { + "ipv4": {"unicast": {"neighbor": {"r2": {"dest_link": {"r4": {}}}}}} + }}}}} diff --git a/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ibgp_unnumbered_nbr.json b/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ibgp_unnumbered_nbr.json new file mode 100644 index 0000000000..5e90d6b2d4 --- /dev/null +++ b/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ibgp_unnumbered_nbr.json @@ -0,0 +1,97 @@ +{ + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24, "ipv6": "fd00::", "v6mask": 64}, + "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:DB8:F::", "v6mask": 128}, + "routers": { + "r0": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1-link1": {"ipv4": "auto"}, + "r1-link2": {"ipv4": "auto"}, + "r1-link3": {"ipv4": "auto"}, + "r1-link4": {"ipv4": "auto"}, + "r1-link5": {"ipv4": "auto"}} + }, + "r1": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r0-link1": {"ipv4": "auto"}, + "r0-link2": {"ipv4": "auto"}, + "r0-link3": {"ipv4": "auto"}, + "r0-link4": {"ipv4": "auto"}, + "r0-link5": {"ipv4": "auto"}, + "r2-link0": {"ipv4": "auto"}}, + "bgp": { + "local_as": "100", + "default_ipv4_unicast": "False", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link0": { + "neighbor_type": "unnumbered", + "capability": "extended-nexthop", + "activate": "ipv4" + } + } + } + } + } + } + }}}, + "r2": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1-link0": {"ipv4": "auto"}, + "r3": {"ipv4": "auto", "ipv6": "auto"}, + "r4": {"ipv4": "auto"}}, + "bgp": { + "local_as": "100", + "default_ipv4_unicast": "False", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": {"dest_link": {"r2": {"activate": "ipv4"}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link0": { + "neighbor_type": "unnumbered", + "capability": "extended-nexthop", + "activate": "ipv4" + } + } + }, + "r3": {"dest_link": {"r2": {}}}} + } + }}}}, + "r3": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2": {"ipv4": "auto", "ipv6": "auto"}}, + "bgp": { + "local_as": "300", + "default_ipv4_unicast": "False", + "address_family": { + "ipv6": {"unicast": {"neighbor": {"r2": {"dest_link": {"r3": {}}}}}} + }}}, + "r4": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2": {"ipv4": "auto"}}, + "bgp": { + "local_as": "400", + "address_family": { + "ipv4": {"unicast": {"neighbor": {"r2": {"dest_link": {"r4": {}}}}}} + }}}}} diff --git a/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_ibgp_nbr.py b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_ibgp_nbr.py new file mode 100644 index 0000000000..4f72cbb300 --- /dev/null +++ b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_ibgp_nbr.py @@ -0,0 +1,987 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2021 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. +# ("NetDEF") in this file. +# +# 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 VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE 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. +# + + +"""RFC5549 Automation.""" +import os +import sys +import time +import json +import pytest +from copy import deepcopy +import ipaddr +from re import search as re_search + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from mininet.topo import Topo + +from lib.common_config import ( + start_topology, + write_test_header, + get_frr_ipv6_linklocal, + write_test_footer, + create_prefix_lists, + verify_rib, + create_static_routes, + check_address_types, + reset_config_on_routers, + step, + create_route_maps, + create_interfaces_cfg, +) +from lib.topolog import logger +from lib.bgp import ( + clear_bgp_and_verify, + verify_bgp_convergence, + create_router_bgp, + verify_bgp_rib, +) +from lib.topojson import build_topo_from_json, build_config_from_json + +# Global variables +topo = None +# Reading the data from JSON File for topology creation +jsonFile = "{}/rfc5549_ebgp_ibgp_nbr.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +# Global variables +NETWORK = { + "ipv4": [ + "11.0.20.1/32", + "11.0.20.2/32", + "11.0.20.3/32", + "11.0.20.4/32", + "11.0.20.5/32", + ], + "ipv6": ["1::1/128", "1::2/128", "1::3/128", "1::4/128", "1::5/128"], +} +MASK = {"ipv4": "32", "ipv6": "128"} +NEXT_HOP = { + "ipv4": ["10.0.0.1", "10.0.1.1", "10.0.2.1", "10.0.3.1", "10.0.4.1"], + "ipv6": ["Null0", "Null0", "Null0", "Null0", "Null0"], +} +NO_OF_RTES = 2 +NETWORK_CMD_IP = "1.0.1.17/32" +ADDR_TYPES = check_address_types() +TOPOOLOGY = """ + Please view in a fixed-width font such as Courier. + + +----+ + | R4 | + | | + +--+-+ + | ipv4 nbr + no bgp ebgp/ibgp | + | ebgp/ibgp + +----+ 5links +----+ 8links +--+-+ +----+ + |R0 +----------+ R1 +------------+ R2 | ipv6 nbr |R3 | + | +----------+ +------------+ +-------------+ | + +----+ +----+ ipv6 nbr +----+ +----+ +""" + +TESTCASES = """ +1. Verify Ipv4 route next hop is changed when advertised using +next hop -self command +2. Verify IPv4 route advertised to peer when IPv6 BGP session established + using peer-group +3. Verify IPv4 routes received with IPv6 nexthop are getting advertised + to another IBGP peer without changing the nexthop +4. Verify IPv4 routes advertised with correct nexthop when nexthop +unchange is configure on EBGP peers + """ + + +class CreateTopo(Topo): + """ + Test topology builder. + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function.""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """Set up the pytest environment.""" + + global topo + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(CreateTopo, mod.__name__) + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error:" " {}".format( + BGP_CONVERGENCE + ) + logger.info("Running setup_module() done") + + +def teardown_module(): + """ + Teardown the pytest environment. + + * `mod`: module name + """ + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + +def get_llip(onrouter, intf): + """ + API to get the link local ipv6 address of a perticular interface + + Parameters + ---------- + * `fromnode`: Source node + * `tonode` : interface for which link local ip needs to be returned. + + Usage + ----- + result = get_llip('r1', 'r2-link0') + + Returns + ------- + 1) link local ipv6 address from the interface. + 2) errormsg - when link local ip not found. + """ + tgen = get_topogen() + intf = topo["routers"][onrouter]["links"][intf]["interface"] + llip = get_frr_ipv6_linklocal(tgen, onrouter, intf) + if llip: + logger.info("llip ipv6 address to be set as NH is %s", llip) + return llip + return None + + +def get_glipv6(onrouter, intf): + """ + API to get the global ipv6 address of a perticular interface + + Parameters + ---------- + * `onrouter`: Source node + * `intf` : interface for which link local ip needs to be returned. + + Usage + ----- + result = get_glipv6('r1', 'r2-link0') + + Returns + ------- + 1) global ipv6 address from the interface. + 2) errormsg - when link local ip not found. + """ + glipv6 = (topo["routers"][onrouter]["links"][intf]["ipv6"]).split("/")[0] + if glipv6: + logger.info("Global ipv6 address to be set as NH is %s", glipv6) + return glipv6 + return None + + +# ################################## +# Test cases start here. +# ################################## +def test_ibgp_to_ibgp_p1(request): + """ + + Test Capability extended nexthop. + + Verify IPv4 routes received with IPv6 nexthop are getting advertised to + another IBGP peer without changing the nexthop + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + reset_config_on_routers(tgen) + global topo + topo23 = deepcopy(topo) + build_config_from_json(tgen, topo23, save_bkup=False) + + step("Configure IPv6 EBGP session between R1 and R2 with " "global IPv6 address") + step("Configure IPv6 IBGP session betn R2 & R3 using IPv6 global address") + step("Enable capability extended-nexthop on both the IPv6 BGP peers") + step("Activate same IPv6 nbr from IPv4 unicast family") + step("Enable cap ext nh on r1 and r2 and activate in ipv4 addr family") + step("Verify bgp convergence as ipv6 nbr is enabled on ipv4 addr family.") + + # verify bgp convergence as ipv6 nbr is enabled on ipv4 addr family. + bgp_convergence = verify_bgp_convergence(tgen, topo23) + assert bgp_convergence is True, "Testcase :Failed \n Error:" " {}".format( + bgp_convergence + ) + + step(" Configure 5 IPv4 static" " routes on R1, Nexthop as different links of R0") + for rte in range(0, NO_OF_RTES): + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][rte], + "no_of_ip": 1, + "next_hop": NEXT_HOP["ipv4"][rte], + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise static routes from IPv4 unicast family and IPv6 " + "unicast family respectively from R1 using red static cmd " + "Advertise loopback from IPv4 unicast family using network command " + "from R1" + ) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{"redist_type": "static"}], + "advertise_networks": [ + {"network": NETWORK_CMD_IP, "no_of_network": 1} + ], + } + } + } + } + } + } + result = create_router_bgp(tgen, topo23, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + step( + "IPv4 routes advertised using static and network command are " + " received on R2 BGP and routing table , " + "verify using show ip bgp, show ip route for IPv4 routes ." + ) + + gllip = get_llip("r1", "r2-link0") + assert gllip is not None, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + dut = "r2" + protocol = "bgp" + # verify the routes with nh as ext_nh + verify_nh_for_static_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "no_of_ip": NO_OF_RTES, + "next_hop": gllip, + } + ] + } + } + bgp_rib = verify_bgp_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gllip + ) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gllip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + configure_bgp_on_r2 = { + "r2": { + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2": { + "activate": "ipv4", + "capability": "extended-nexthop", + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + configure_bgp_on_r3 = { + "r3": { + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": { + "activate": "ipv4", + "capability": "extended-nexthop", + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "IPv4 routes installed on R3 with global address without " + "changing the nexthop ( nexthop should IPv6 link local which is" + " received from R1)" + ) + gipv6 = get_glipv6("r1", "r2-link0") + dut = "r3" + verify_nh_for_static_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "no_of_ip": NO_OF_RTES, + "next_hop": gipv6, + } + ] + } + } + bgp_rib = verify_bgp_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gipv6 + ) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + write_test_footer(tc_name) + + +def test_ext_nh_cap_red_static_network_ibgp_peer_p1(request): + """ + + Test Extended capability next hop, with ibgp peer. + + Verify IPv4 routes advertise using "redistribute static" and + "network command" are received on EBGP peer with IPv6 nexthop + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + reset_config_on_routers(tgen) + step( + " Configure IPv6 EBGP session between R1 & R2 with global IPv6 address" + " Enable capability extended-nexthop on the nbr from both the routers" + " Activate same IPv6 nbr from IPv4 unicast family" + ) + configure_bgp_on_r2 = { + "r2": { + "bgp": { + "default_ipv4_unicast": "False", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2": { + "capability": "extended-nexthop", + "activate": "ipv4", + "next_hop_self": True, + "activate": "ipv4", + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + configure_bgp_on_r3 = { + "r3": { + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": { + "capability": "extended-nexthop", + "activate": "ipv4", + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + for rte in range(0, NO_OF_RTES): + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][rte], + "no_of_ip": 1, + "next_hop": NEXT_HOP["ipv4"][rte], + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "default_ipv4_unicast": "False", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{"redist_type": "static"}], + "advertise_networks": [ + {"network": NETWORK_CMD_IP, "no_of_network": 1} + ], + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + gllip = get_llip("r1", "r2-link0") + assert gllip is not None, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + dut = "r2" + protocol = "bgp" + verify_nh_for_static_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "no_of_ip": NO_OF_RTES, + "next_hop": gllip, + } + ] + } + } + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gllip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + verify_nh_for_nw_cmd_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK_CMD_IP, + "no_of_ip": 1, + "next_hop": gllip, + } + ] + } + } + + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_nw_cmd_rtes, next_hop=gllip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + gllip = get_glipv6("r2", "r3") + assert gllip is not None, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + dut = "r3" + protocol = "bgp" + # verify the routes with nh as ext_nh + verify_nh_for_static_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "no_of_ip": NO_OF_RTES, + "next_hop": gllip, + } + ] + } + } + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gllip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + verify_nh_for_nw_cmd_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK_CMD_IP, + "no_of_ip": 1, + "next_hop": gllip, + } + ] + } + } + bgp_rib = verify_bgp_rib( + tgen, "ipv4", dut, verify_nh_for_nw_cmd_rtes, next_hop=gllip + ) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_nw_cmd_rtes, next_hop=gllip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_bgp_peer_group_p1(request): + """ + Test extended capability next hop with peer groups. + + Verify IPv4 routes received with IPv6 nexthop are getting advertised to + another IBGP peer without changing the nexthop + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + reset_config_on_routers(tgen) + global topo + topo1 = deepcopy(topo) + step("Configure IPv6 EBGP session between R1 and R2 with " "global IPv6 address") + step("Configure IPv6 IBGP session betn R2 & R3 using IPv6 global address") + step("Enable capability extended-nexthop on both the IPv6 BGP peers") + step("Activate same IPv6 nbr from IPv4 unicast family") + step("Enable cap ext nh on r1 and r2 and activate in ipv4 addr family") + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "default_ipv4_unicast": "False", + "peer-group": { + "rfc5549": {"capability": "extended-nexthop", "remote-as": "200"} + }, + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link0": { + "activate": "ipv4", + "capability": "extended-nexthop", + "peer-group": "rfc5549", + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + configure_bgp_on_r2 = { + "r2": { + "bgp": { + "default_ipv4_unicast": "False", + "peer-group": { + "rfc5549": {"capability": "extended-nexthop", "remote-as": "100"} + }, + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + configure_bgp_on_r2 = { + "r2": { + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link0": { + "capability": "extended-nexthop", + "activate": "ipv4", + "peer-group": "rfc5549", + } + } + }, + "r3": {"dest_link": {"r2": {}}}, + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + configure_bgp_on_r3 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": {"unicast": {"neighbor": {"r2": {"dest_link": {"r3": {}}}}}} + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify bgp convergence as ipv6 nbr is enabled on ipv4 addr family.") + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert bgp_convergence is True, "Testcase :Failed \n Error:" " {}".format( + bgp_convergence + ) + + step(" Configure 2 IPv4 static" " routes on R1, Nexthop as different links of R0") + for rte in range(0, NO_OF_RTES): + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][rte], + "no_of_ip": 1, + "next_hop": NEXT_HOP["ipv4"][rte], + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise static routes from IPv4 unicast family and IPv6 " + "unicast family respectively from R1 using red static cmd " + "Advertise loopback from IPv4 unicast family using network command " + "from R1" + ) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{"redist_type": "static"}], + "advertise_networks": [ + {"network": NETWORK_CMD_IP, "no_of_network": 1} + ], + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + step( + "IPv4 routes advertised using static and network command are " + " received on R2 BGP and routing table , " + "verify using show ip bgp, show ip route for IPv4 routes ." + ) + + gllip = get_llip("r1", "r2-link0") + assert gllip is not None, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + dut = "r2" + protocol = "bgp" + verify_nh_for_static_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "no_of_ip": NO_OF_RTES, + "next_hop": gllip, + } + ] + } + } + bgp_rib = verify_bgp_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gllip + ) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gllip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Enable cap ext nh on r1 and r2 and activate in ipv4 addr family") + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "default_ipv4_unicast": "False", + "peer-group": { + "rfc5549": {"capability": "extended-nexthop", "remote-as": "200"} + }, + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link0": { + "activate": "ipv4", + "capability": "extended-nexthop", + "peer-group": "rfc5549", + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + configure_bgp_on_r2 = { + "r2": { + "bgp": { + "default_ipv4_unicast": "False", + "peer-group": { + "rfc5549": {"capability": "extended-nexthop", "remote-as": "100"} + }, + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + configure_bgp_on_r2 = { + "r2": { + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link0": { + "capability": "extended-nexthop", + "activate": "ipv4", + "peer-group": "rfc5549", + } + } + }, + "r3": {"dest_link": {"r2": {}}}, + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + configure_bgp_on_r3 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": {"unicast": {"neighbor": {"r2": {"dest_link": {"r3": {}}}}}} + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify bgp convergence as ipv6 nbr is enabled on ipv4 addr family.") + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert bgp_convergence is True, "Testcase :Failed \n Error:" " {}".format( + bgp_convergence + ) + + step(" Configure 2 IPv4 static" " routes on R1, Nexthop as different links of R0") + for rte in range(0, NO_OF_RTES): + input_dict = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][rte], + "no_of_ip": 1, + "next_hop": NEXT_HOP["ipv4"][rte], + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise static routes from IPv4 unicast family and IPv6 " + "unicast family respectively from R1 using red static cmd " + "Advertise loopback from IPv4 unicast family using network command " + "from R1" + ) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{"redist_type": "static"}], + "advertise_networks": [ + {"network": NETWORK_CMD_IP, "no_of_network": 1} + ], + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + step( + "IPv4 routes advertised using static and network command are " + " received on R2 BGP and routing table , " + "verify using show ip bgp, show ip route for IPv4 routes ." + ) + + gllip = get_llip("r1", "r2-link0") + assert gllip is not None, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + dut = "r2" + protocol = "bgp" + verify_nh_for_static_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "no_of_ip": NO_OF_RTES, + "next_hop": gllip, + } + ] + } + } + bgp_rib = verify_bgp_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gllip + ) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gllip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_nbr.py b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_nbr.py new file mode 100644 index 0000000000..12237fec61 --- /dev/null +++ b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_nbr.py @@ -0,0 +1,661 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2021 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. +# ("NetDEF") in this file. +# +# 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 VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE 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. +# + + +"""RFC5549 Automation.""" +import os +import sys +import time +import json +import pytest +import datetime +from copy import deepcopy +import ipaddr +from re import search as re_search + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from mininet.topo import Topo + +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + create_prefix_lists, + get_frr_ipv6_linklocal, + verify_rib, + create_static_routes, + check_address_types, + reset_config_on_routers, + step, + create_route_maps, + addKernelRoute, + kill_router_daemons, + start_router_daemons, + create_interfaces_cfg, +) +from lib.topolog import logger +from lib.bgp import ( + clear_bgp_and_verify, + clear_bgp, + verify_bgp_convergence, + create_router_bgp, + verify_bgp_rib, +) +from lib.topojson import build_topo_from_json, build_config_from_json + +# Global variables +topo = None +# Reading the data from JSON File for topology creation +jsonFile = "{}/rfc5549_ebgp_nbr.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +# Global variables +NETWORK = { + "ipv4": [ + "11.0.20.1/32", + "11.0.20.2/32", + "11.0.20.3/32", + "11.0.20.4/32", + "11.0.20.5/32", + ], + "ipv6": ["1::1/128", "1::2/128", "1::3/128", "1::4/128", "1::5/128"], +} +MASK = {"ipv4": "32", "ipv6": "128"} +NEXT_HOP = { + "ipv4": ["10.0.0.1", "10.0.1.1", "10.0.2.1", "10.0.3.1", "10.0.4.1"], + "ipv6": ["Null0", "Null0", "Null0", "Null0", "Null0"], +} +NO_OF_RTES = 2 +NETWORK_CMD_IP = "1.0.1.17/32" +ADDR_TYPES = check_address_types() +BGP_CONVERGENCE_TIMEOUT = 10 +TOPOOLOGY = """ + Please view in a fixed-width font such as Courier. + +----+ + | R4 | + | | + +--+-+ + | ipv4 nbr + no bgp ebgp | + | ebgp/ibgp + +----+ 5links +----+ +--+-+ +----+ + |R0 +----------+ R1 | | R2 | ipv6 nbr |R3 | + | +----------+ +------------+ +-------------+ | + +----+ +----+ ipv6 nbr +----+ +----+ +""" + +TESTCASES = """ +TC6. Verify BGP speaker advertise IPv4 route to peer only if "extended + nexthop capability" is negotiated +TC7. Verify ipv4 route nexthop updated dynamically when in route-map is + applied on receiving BGP peer +TC8. Verify IPv4 routes advertise using "redistribute static" and "network + command" are received on EBGP peer with IPv6 nexthop +TC10. Verify IPv4 routes are deleted after un-configuring of "network +command" and "redistribute static knob" +TC18. Verify IPv4 routes installed with correct nexthop after deactivate + and activate neighbor from address family +TC19. Verify IPv4 route ping is working fine and nexhop installed in kernel + as IPv4 link-local address +TC24. Verify IPv4 prefix-list routes advertised to peer when prefix -list + applied in out direction +TC27. Verify IPv4 routes are intact after BGPd process restart +TC30. Verify Ipv4 route installed with correct next hop when same route + is advertised via IPV4 and IPv6 BGP peers +TC32. Verify IPv4 route received with IPv6 nexthop can be advertised to + another IPv4 BGP peers + """ + + +class CreateTopo(Topo): + """ + Test topology builder. + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function.""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """Set up the pytest environment.""" + global topo, ADDR_TYPES + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(CreateTopo, mod.__name__) + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error:" " {}".format( + BGP_CONVERGENCE + ) + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment.""" + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + +def get_llip(onrouter, intf): + """ + API to get the link local ipv6 address of a perticular interface + + Parameters + ---------- + * `fromnode`: Source node + * `tonode` : interface for which link local ip needs to be returned. + + Usage + ----- + result = get_llip('r1', 'r2-link0') + + Returns + ------- + 1) link local ipv6 address from the interface. + 2) errormsg - when link local ip not found. + """ + tgen = get_topogen() + intf = topo["routers"][onrouter]["links"][intf]["interface"] + llip = get_frr_ipv6_linklocal(tgen, onrouter, intf) + if llip: + logger.info("llip ipv6 address to be set as NH is %s", llip) + return llip + return None + + +def get_glipv6(onrouter, intf): + """ + API to get the global ipv6 address of a perticular interface + + Parameters + ---------- + * `onrouter`: Source node + * `intf` : interface for which link local ip needs to be returned. + + Usage + ----- + result = get_glipv6('r1', 'r2-link0') + + Returns + ------- + 1) global ipv6 address from the interface. + 2) errormsg - when link local ip not found. + """ + glipv6 = (topo["routers"][onrouter]["links"][intf]["ipv6"]).split("/")[0] + if glipv6: + logger.info("Global ipv6 address to be set as NH is %s", glipv6) + return glipv6 + return None + + +# ################################## +# Test cases start here. +# ################################## + + +def test_ext_nh_cap_red_static_network_ebgp_peer_tc8_p0(request): + """ + + Test exted capability nexthop with route map in. + + Verify IPv4 routes advertise using "redistribute static" and + "network command" are received on EBGP peer with IPv6 nexthop + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + step("Configure IPv6 EBGP session between R1 and R2 with global" " IPv6 address") + reset_config_on_routers(tgen) + + step( + "Enable capability extended-nexthop on the nbr from both the " + " routers Activate same IPv6 nbr from IPv4 unicast family" + ) + step( + " Configure 2 IPv4 static " + "routes on R1 (nexthop for static route exists on different " + "link of R0" + ) + for rte in range(0, NO_OF_RTES): + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][rte], + "no_of_ip": 1, + "next_hop": NEXT_HOP["ipv4"][rte], + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for rte in range(0, NO_OF_RTES): + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv6"][rte], + "no_of_ip": 1, + "next_hop": NEXT_HOP["ipv6"][rte], + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise static routes from IPv4 unicast family and IPv6 " + "unicast family respectively from R1 using red static cmd " + "Advertise loopback from IPv4 unicast family using network command " + "from R1" + ) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "local_as": "100", + "default_ipv4_unicast": "True", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{"redist_type": "static"}], + "advertise_networks": [ + {"network": NETWORK_CMD_IP, "no_of_network": 1} + ], + } + }, + "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + }, + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + glip = get_llip("r1", "r2-link0") + assert glip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "IPv4 and IPv6 routes advertised using static and network command " + "are received on R2 BGP & routing table , verify using show ip bgp " + "show ip route for IPv4 routes and show bgp ipv6,show ipv6 routes " + "for IPv6 routes ." + ) + + dut = "r2" + protocol = "bgp" + for addr_type in ADDR_TYPES: + # verify the routes with nh as ext_nh + verify_nh_for_static_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK[addr_type][0], + "no_of_ip": 2, + "next_hop": glip, + } + ] + } + } + bgp_rib = verify_bgp_rib( + tgen, addr_type, dut, verify_nh_for_static_rtes, next_hop=glip + ) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, bgp_rib + ) + result = verify_rib( + tgen, + addr_type, + dut, + verify_nh_for_static_rtes, + next_hop=glip, + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify IPv4 routes are installed with IPv6 global nexthop of R1" + " R1 to R2 connected link" + ) + + verify_nh_for_nw_cmd_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK_CMD_IP, + "no_of_ip": 1, + "next_hop": glip, + } + ] + } + } + bgp_rib = verify_bgp_rib( + tgen, "ipv4", dut, verify_nh_for_nw_cmd_rtes, next_hop=glip + ) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_nw_cmd_rtes, next_hop=glip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + +def test_ext_nh_cap_remove_red_static_network_ebgp_peer_tc10_p1(request): + """ + + Test exted capability nexthop with route map in. + + Verify IPv4 routes are deleted after un-configuring of + network command and redistribute static knob + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + step( + "Configure IPv6 EBGP session between R1 and R2 with global IPv6" + " address Enable capability extended-nexthop on the nbr from both" + " the routers , Activate same IPv6 nbr from IPv4 unicast family" + ) + step( + " Configure 2 IPv4 static routes " + " on R1 nexthop for static route exists on different link of R0" + ) + reset_config_on_routers(tgen) + + for rte in range(0, NO_OF_RTES): + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][rte], + "no_of_ip": 1, + "next_hop": NEXT_HOP["ipv4"][rte], + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + step( + "Advertise static routes from IPv4 unicast family and IPv6 unicast" + " family respectively from R1. Configure loopback on R1 with IPv4 " + "address Advertise loobak from IPv4 unicast family using network " + "command from R1" + ) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "local_as": "100", + "default_ipv4_unicast": "True", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{"redist_type": "static"}], + "advertise_networks": [ + {"network": NETWORK_CMD_IP, "no_of_network": 1} + ], + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "IPv4 and IPv6 routes advertised using static and network command are" + " received on R2 BGP and routing table , verify using show ip bgp" + " show ip route for IPv4 routes and show bgp, show ipv6 routes" + " for IPv6 routes ." + ) + + glipv6 = get_llip("r1", "r2-link0") + assert glipv6 is not None, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + dut = "r2" + protocol = "bgp" + verify_nh_for_static_rtes = { + "r1": { + "advertise_networks": [ + { + "network": NETWORK["ipv4"][0], + "no_of_ip": NO_OF_RTES, + "next_hop": get_glipv6, + } + ] + } + } + bgp_rib = verify_bgp_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=get_glipv6 + ) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, + "ipv4", + dut, + verify_nh_for_static_rtes, + next_hop=get_glipv6, + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify IPv4 routes are installed with IPv6 global nexthop of R1 " + " R1 to R2 connected link" + ) + verify_nh_for_nw_cmd_rtes = { + "r1": { + "advertise_networks": [ + { + "network": NETWORK_CMD_IP, + "no_of_ip": 1, + "next_hop": glipv6, + } + ] + } + } + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_nw_cmd_rtes, next_hop=glipv6, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{"redist_type": "static", "delete": True}] + } + }, + "ipv6": { + "unicast": { + "redistribute": [{"redist_type": "static", "delete": True}] + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # verify the routes with nh as ext_nh + verify_nh_for_static_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "no_of_ip": NO_OF_RTES, + "next_hop": glipv6, + } + ] + } + } + + bgp_rib = verify_bgp_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=glipv6, expected=False + ) + assert ( + bgp_rib is not True + ), "Testcase {} : Failed \n Error: Routes still" " present in BGP rib".format( + tc_name + ) + result = verify_rib( + tgen, + "ipv4", + dut, + verify_nh_for_static_rtes, + next_hop=glipv6, + protocol=protocol, + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n Error: Routes " "still present in RIB".format(tc_name) + + step( + "After removing IPv4 routes from redistribute static those routes" + " are removed from R2, after re-advertising routes which are " + " advertised using network are still present in the on R2 with " + " IPv6 global nexthop, verify using show ip bgp and show ip routes" + ) + + verify_nh_for_nw_cmd_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK_CMD_IP, + "no_of_ip": 1, + "next_hop": glipv6, + } + ] + } + } + + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_nw_cmd_rtes, next_hop=glipv6, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + { + "network": NETWORK_CMD_IP, + "no_of_network": 1, + "delete": True, + } + ] + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + result = verify_rib( + tgen, + "ipv4", + dut, + verify_nh_for_nw_cmd_rtes, + next_hop=glipv6, + protocol=protocol, + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n " "Error: Routes still present in BGP rib".format( + tc_name + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_unnumbered_nbr.py b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_unnumbered_nbr.py new file mode 100644 index 0000000000..2675f3a393 --- /dev/null +++ b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_unnumbered_nbr.py @@ -0,0 +1,613 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2021 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. +# ("NetDEF") in this file. +# +# 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 VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE 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. +# + + +"""RFC5549 Automation.""" +import os +import sys +import time +import json +import pytest +import ipaddr +from copy import deepcopy +from re import search as re_search + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../../")) + +from lib.topogen import Topogen, get_topogen +from mininet.topo import Topo + +from lib.common_config import ( + write_test_header, + start_topology, + create_route_maps, + write_test_footer, + start_router, + stop_router, + verify_rib, + create_static_routes, + check_address_types, + reset_config_on_routers, + step, + shutdown_bringup_interface, + create_interfaces_cfg, + get_frr_ipv6_linklocal, +) +from lib.topolog import logger +from lib.bgp import clear_bgp, verify_bgp_convergence, create_router_bgp, verify_bgp_rib + +from lib.topojson import build_topo_from_json, build_config_from_json + +# Global variables +topo = None +# Reading the data from JSON File for topology creation +jsonFile = "{}/rfc5549_ebgp_unnumbered_nbr.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +# Global variables +NO_OF_RTES = 2 +NETWORK_CMD_IP = "1.0.1.17/32" +NETWORK = { + "ipv4": [ + "11.0.20.1/32", + "11.0.20.2/32", + "11.0.20.3/32", + "11.0.20.4/32", + "11.0.20.5/32", + ], + "ipv6": ["1::1/128", "1::2/128", "1::3/128", "1::4/128", "1::5/128"], +} +MASK = {"ipv4": "32", "ipv6": "128"} +NEXT_HOP = { + "ipv4": ["10.0.0.1", "10.0.1.1", "10.0.2.1", "10.0.3.1", "10.0.4.1"], + "ipv6": ["Null0", "Null0", "Null0", "Null0", "Null0"], +} +INTF_LIST = [ + "r2-link0", + "r2-link1", + "r2-link2", + "r2-link3", + "r2-link4", + "r2-link5", + "r2-link6", + "r2-link7", +] +ADDR_TYPES = check_address_types() +TOPOOLOGY = """ + Please view in a fixed-width font such as Courier. + + +----+ + | R4 | + | | + +--+-+ + | ipv4 nbr + no bgp ebgp/ibgp | + | ebgp/ibgp + +----+ 5links +----+ 8links +--+-+ +----+ + |R0 +----------+ R1 +------------+ R2 | ipv6 nbr |R3 | + | +----------+ +------------+ +-------------+ | + +----+ +----+ ipv6 nbr +----+ +----+ +""" + +TESTCASES = """ +1. Verify IPv4 routes are advertised when IPv6 EBGP loopback session + established using Unnumbered interface +2. Verify IPv4 routes are installed with correct nexthop after +shut / no shut of nexthop and BGP peer interfaces +3. Verify IPv4 routes are intact after stop and start the FRR services + """ + + +class CreateTopo(Topo): + """ + Test topology builder. + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function.""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """Set up the pytest environment.""" + global topo, ADDR_TYPES + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(CreateTopo, mod.__name__) + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error:" " {}".format( + BGP_CONVERGENCE + ) + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment.""" + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + +def get_llip(onrouter, intf): + """ + API to get the link local ipv6 address of a perticular interface + + Parameters + ---------- + * `fromnode`: Source node + * `tonode` : interface for which link local ip needs to be returned. + + Usage + ----- + result = get_llip('r1', 'r2-link0') + + Returns + ------- + 1) link local ipv6 address from the interface. + 2) errormsg - when link local ip not found. + """ + tgen = get_topogen() + intf = topo["routers"][onrouter]["links"][intf]["interface"] + llip = get_frr_ipv6_linklocal(tgen, onrouter, intf) + if llip: + logger.info("llip ipv6 address to be set as NH is %s", llip) + return llip + return None + + +def get_glipv6(onrouter, intf): + """ + API to get the global ipv6 address of a perticular interface + + Parameters + ---------- + * `onrouter`: Source node + * `intf` : interface for which link local ip needs to be returned. + + Usage + ----- + result = get_glipv6('r1', 'r2-link0') + + Returns + ------- + 1) global ipv6 address from the interface. + 2) errormsg - when link local ip not found. + """ + glipv6 = (topo["routers"][onrouter]["links"][intf]["ipv6"]).split("/")[0] + if glipv6: + logger.info("Global ipv6 address to be set as NH is %s", glipv6) + return glipv6 + return None + + +# ################################## +# Test cases start here. +# ################################## + + +def test_unnumbered_loopback_ebgp_nbr_p0(request): + """ + + Test extended capability nexthop with un numbered ebgp. + + Verify IPv4 routes are advertised when IPv6 EBGP loopback + session established using Unnumbered interface + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + reset_config_on_routers(tgen) + + step("Configure IPv6 EBGP Unnumbered session between R1 and R2") + step("Enable capability extended-nexthop on both the IPv6 BGP peers") + step("Activate same IPv6 nbr from IPv4 unicast family") + step("Enable cap ext nh on r1 and r2 and activate in ipv4 addr family") + step("Verify bgp convergence as ipv6 nbr is enabled on ipv4 addr family.") + + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert bgp_convergence is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, bgp_convergence + ) + + step(" Configure 5 IPv4 static" " routes on R1, Nexthop as different links of R0") + for rte in range(0, NO_OF_RTES): + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][rte], + "no_of_ip": 1, + "next_hop": NEXT_HOP["ipv4"][rte], + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise static routes from IPv4 unicast family and IPv6 " + "unicast family respectively from R1 using red static cmd " + "Advertise loopback from IPv4 unicast family using network command " + "from R1" + ) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{"redist_type": "static"}], + "advertise_networks": [ + {"network": NETWORK_CMD_IP, "no_of_network": 1} + ], + } + }, + "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + step( + "IPv4 routes advertised using static and network command are " + " received on R2 BGP and routing table , " + "verify using show ip bgp, show ip route for IPv4 routes ." + ) + + llip = get_llip("r1", "r2-link0") + assert llip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, llip) + + dut = "r2" + protocol = "bgp" + for rte in range(0, NO_OF_RTES): + # verify the routes with nh as ext_nh + verify_nh_for_static_rtes = { + "r1": { + "static_routes": [ + {"network": NETWORK["ipv4"][rte], "no_of_ip": 1, "next_hop": llip} + ] + } + } + """ interface_list = ['r1-link0','r1-link1'] + nh_list =[] + for i in range(NO_OF_RTES): + nh_list.append(topo['routers']['r2']['links'][i][ + 'interface']) """ + bgp_rib = verify_rib( + tgen, + "ipv4", + dut, + # verify_nh_for_static_rtes, next_hop='r2-r1-eth0') + verify_nh_for_static_rtes, + next_hop=llip, + ) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, bgp_rib + ) + result = verify_rib( + tgen, + "ipv4", + dut, + verify_nh_for_static_rtes, + next_hop=llip, + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # verify the routes with nh as ext_nh + verify_nh_for_nw_rtes = { + "r1": { + "static_routes": [ + {"network": NETWORK_CMD_IP, "no_of_ip": 1, "next_hop": llip} + ] + } + } + + bgp_rib = verify_rib( + tgen, + "ipv4", + dut, + # verify_nh_for_nw_rtes, next_hop='r2-r1-eth0') + verify_nh_for_nw_rtes, + next_hop=llip, + ) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=llip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + # stop/start -> restart FRR router and verify + stop_router(tgen, "r1") + stop_router(tgen, "r2") + start_router(tgen, "r1") + start_router(tgen, "r2") + step( + "After stop/start of FRR services , verify session up and routes " + "came up fine ,nh is proper using show bgp & show ipv6 route on R2 " + ) + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert bgp_convergence is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, bgp_convergence + ) + + llip = get_llip("r1", "r2-link0") + assert llip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # verify the routes with nh as ext_nh + verify_nh_for_static_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "no_of_ip": NO_OF_RTES, + "next_hop": llip, + } + ] + } + } + bgp_rib = verify_bgp_rib( + tgen, + "ipv4", + dut, + # verify_nh_for_static_rtes, next_hop='r2-r1-eth0') + verify_nh_for_static_rtes, + next_hop=llip, + ) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=llip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + verify_nh_for_nw_rtes = { + "r1": { + "static_routes": [ + {"network": NETWORK_CMD_IP, "no_of_ip": 1, "next_hop": llip} + ] + } + } + bgp_rib = verify_rib( + tgen, + "ipv4", + dut, + # verify_nh_for_nw_rtes, next_hop='r2-r1-eth0') + verify_nh_for_nw_rtes, + next_hop=llip, + ) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=llip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + write_test_footer(tc_name) + + +def test_restart_frr_p2(request): + """ + + Test extended capability nexthop , restart frr. + + Verify IPv4 routes are intact after stop and start the FRR services + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + reset_config_on_routers(tgen) + step("Configure IPv6 EBGP Unnumbered session between R1 and R2") + step("Enable capability extended-nexthop on both the IPv6 BGP peers") + step("Activate same IPv6 nbr from IPv4 unicast family") + step("Enable cap ext nh on r1 and r2 and activate in ipv4 addr family") + step("Verify bgp convergence as ipv6 nbr is enabled on ipv4 addr family.") + reset_config_on_routers(tgen) + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert bgp_convergence is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, bgp_convergence + ) + + step(" Configure 5 IPv4 static" " routes on R1, Nexthop as different links of R0") + for rte in range(0, NO_OF_RTES): + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][rte], + "no_of_ip": 1, + "next_hop": NEXT_HOP["ipv4"][rte], + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise static routes from IPv4 unicast family and IPv6 " + "unicast family respectively from R1 using red static cmd " + "Advertise loopback from IPv4 unicast family using network command " + "from R1" + ) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{"redist_type": "static"}], + "advertise_networks": [ + {"network": NETWORK_CMD_IP, "no_of_network": 1} + ], + } + }, + "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + step( + "IPv4 routes advertised using static and network command are " + " received on R2 BGP and routing table , " + "verify using show ip bgp, show ip route for IPv4 routes ." + ) + + llip = get_llip("r1", "r2-link0") + assert llip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + dut = "r2" + protocol = "bgp" + verify_nh_for_static_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "no_of_ip": NO_OF_RTES, + "next_hop": llip, + } + ] + } + } + bgp_rib = verify_rib(tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=llip) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=llip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + verify_nh_for_nw_rtes = { + "r1": { + "static_routes": [ + {"network": NETWORK_CMD_IP, "no_of_ip": 1, "next_hop": llip} + ] + } + } + + bgp_rib = verify_rib(tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=llip) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=llip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # stop/start -> restart FRR router and verify + stop_router(tgen, "r1") + stop_router(tgen, "r2") + start_router(tgen, "r1") + start_router(tgen, "r2") + + step( + "After stop/start of FRR services , verify session up and routes " + "came up fine ,nh is proper using show bgp & show ipv6 route on R2 " + ) + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert bgp_convergence is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, bgp_convergence + ) + + llip = get_llip("r1", "r2-link0") + assert llip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # verify the routes with nh as ext_nh + verify_nh_for_static_rtes = { + "r1": { + "static_routes": [ + {"network": NETWORK["ipv4"][0], "no_of_ip": 1, "next_hop": llip} + ] + } + } + bgp_rib = verify_rib(tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=llip) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=llip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # verify the routes with nh as ext_nh + verify_nh_for_nw_rtes = { + "r1": { + "static_routes": [ + {"network": NETWORK_CMD_IP, "no_of_ip": 1, "next_hop": llip} + ] + } + } + bgp_rib = verify_rib(tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=llip) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=llip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ibgp_nbr.py b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ibgp_nbr.py new file mode 100644 index 0000000000..871f6b128a --- /dev/null +++ b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ibgp_nbr.py @@ -0,0 +1,1013 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2021 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. +# ("NetDEF") in this file. +# +# 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 VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE 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. +# + + +"""RFC5549 Automation.""" +import os +import sys +import time +import json +import pytest +from copy import deepcopy +import ipaddr +from re import search as re_search + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from mininet.topo import Topo + +from lib.common_config import ( + start_topology, + write_test_header, + addKernelRoute, + write_test_footer, + create_prefix_lists, + verify_rib, + create_static_routes, + check_address_types, + reset_config_on_routers, + step, + create_route_maps, + create_interfaces_cfg, + get_frr_ipv6_linklocal, +) +from lib.topolog import logger +from lib.bgp import ( + clear_bgp_and_verify, + verify_bgp_convergence, + create_router_bgp, + verify_bgp_rib, +) +from lib.topojson import build_topo_from_json, build_config_from_json + +# Global variables +topo = None +# Reading the data from JSON File for topology creation +jsonFile = "{}/rfc5549_ibgp_nbr.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +# Global variables +NETWORK = { + "ipv4": [ + "11.0.20.1/32", + "11.0.20.2/32", + "11.0.20.3/32", + "11.0.20.4/32", + "11.0.20.5/32", + ], + "ipv6": ["1::1/128", "1::2/128", "1::3/128", "1::4/128", "1::5/128"], +} +MASK = {"ipv4": "32", "ipv6": "128"} +NEXT_HOP = { + "ipv4": ["10.0.0.1", "10.0.1.1", "10.0.2.1", "10.0.3.1", "10.0.4.1"], + "ipv6": ["Null0", "Null0", "Null0", "Null0", "Null0"], +} +NETWORK_CMD_IP = "1.0.1.17/32" +NO_OF_RTES = 2 +TOPOOLOGY = """ + Please view in a fixed-width font such as Courier. + + +----+ + | R4 | + | | + +--+-+ + | ipv4 nbr + no bgp ebgp/ibgp | + | ebgp/ibgp + +----+ 5links +----+ +--+-+ +----+ + |R0 +----------+ R1 | | R2 | ipv6 nbr |R3 | + | +----------+ +------------+ +-------------+ | + +----+ +----+ ipv6 nbr +----+ +----+ +""" + +TESTCASES = """ +1. Verify IPv4 and IPv6 routes advertise using "redistribute static" + and "network command" are received on IBGP peer with IPv6 nexthop +2. Verify IPv4 routes are advertised and withdrawn when IPv6 IBGP session + established using loopback interface +3. Verify IPv4 routes are advertised to peer when static routes are + configured with ADMIN distance and tag option +4. Verify IPv4 routes advertised to peer when BGP session established + using link-local address + """ + + +class CreateTopo(Topo): + """ + Test topology builder. + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function.""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """Set up the pytest environment.""" + + global topo + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(CreateTopo, mod.__name__) + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error:" " {}".format( + BGP_CONVERGENCE + ) + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment.""" + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + +def get_llip(onrouter, intf): + """ + API to get the link local ipv6 address of a perticular interface + + Parameters + ---------- + * `fromnode`: Source node + * `tonode` : interface for which link local ip needs to be returned. + + Usage + ----- + result = get_llip('r1', 'r2-link0') + + Returns + ------- + 1) link local ipv6 address from the interface. + 2) errormsg - when link local ip not found. + """ + tgen = get_topogen() + intf = topo["routers"][onrouter]["links"][intf]["interface"] + llip = get_frr_ipv6_linklocal(tgen, onrouter, intf) + + if llip: + logger.info("llip ipv6 address to be set as NH is %s", llip) + return llip + return None + + +def get_glipv6(onrouter, intf): + """ + API to get the global ipv6 address of a perticular interface + + Parameters + ---------- + * `onrouter`: Source node + * `intf` : interface for which link local ip needs to be returned. + + Usage + ----- + result = get_glipv6('r1', 'r2-link0') + + Returns + ------- + 1) global ipv6 address from the interface. + 2) errormsg - when link local ip not found. + """ + glipv6 = (topo["routers"][onrouter]["links"][intf]["ipv6"]).split("/")[0] + if glipv6: + logger.info("Global ipv6 address to be set as NH is %s", glipv6) + return glipv6 + return None + + +# ################################## +# Test cases start here. +# ################################## + + +def test_ext_nh_cap_red_static_network_ibgp_peer_p1(request): + """ + + Test extended capability nexthop with ibgp peer. + + Verify IPv4 and IPv6 routes advertise using "redistribute static" + and "network command" are received on IBGP peer with IPv6 nexthop + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + reset_config_on_routers(tgen) + step( + "Configure IPv6 EBGP session between R1 and R2 with global IPv6" + " address Enable capability extended-nexthop on the nbr from both" + " the routers" + ) + step( + "Change ebgp to ibgp nbrs between r1 and r2 , Activate same IPv6" + " nbr from IPv4 unicast family " + ) + + step( + " Configure 5 IPv4 static routes" + " on R1 nexthop for static route exists on different link of R0" + ) + + for rte in range(0, NO_OF_RTES): + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][rte], + "no_of_ip": 1, + "next_hop": NEXT_HOP["ipv4"][rte], + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + step( + "Advertise static routes from IPv4 unicast family and IPv6 unicast" + " family respectively from R1.Configure loopback on R1 with IPv4 addr" + " & Advertise loopback from IPv4 unicast family using network cmd " + " from R1" + ) + # this test case needs ipv6 routes to be configured + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{"redist_type": "static"}], + "advertise_networks": [ + {"network": NETWORK_CMD_IP, "no_of_network": 1} + ], + } + }, + "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + glip = get_llip("r1", "r2-link0") + assert glip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "IPv4 and IPv6 routes advertised using static & network command are" + "received on R2 BGP and routing table , verify using show ip bgp" + "show ip route for IPv4 routes and show bgp, show ipv6 routes" + "for IPv6 routes ." + ) + + dut = "r2" + protocol = "bgp" + # verify the routes with nh as ext_nh + verify_nh_for_static_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "no_of_ip": NO_OF_RTES, + "next_hop": glip, + } + ] + } + } + bgp_rib = verify_bgp_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=glip + ) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=glip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify IPv4 routes are installed with IPv6 global nexthop of R1" + "R1 to R2 connected link" + ) + verify_nh_for_nw_cmd_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK_CMD_IP, + "no_of_ip": 1, + "next_hop": glip, + } + ] + } + } + bgp_rib = verify_bgp_rib( + tgen, "ipv4", dut, verify_nh_for_nw_cmd_rtes, next_hop=glip + ) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_nw_cmd_rtes, next_hop=glip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_ext_nh_cap_admin_dist_tag_ibgp_peer_p1(request): + """ + + Test extended capability nexthop with admin distance and route tag. + + Verify IPv4 routes are advertised to peer when static routes + are configured with ADMIN distance and tag option + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + reset_config_on_routers(tgen) + step( + "Configure IPv6 EBGP session between R1 and R2 with global IPv6" + " address Enable capability extended-nexthop on the nbr from both" + " the routers" + ) + step( + "Change ebgp to ibgp nbrs between r1 and r2 , Activate same IPv6" + " nbr from IPv4 unicast family " + ) + step( + " Configure 5 IPv4 static routes" + " on R1 nexthop for static route exists on different link of R0" + ) + count = 0 + for rte in range(0, NO_OF_RTES): + count += 1 + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][rte], + "no_of_ip": 1, + "next_hop": NEXT_HOP["ipv4"][rte], + "admin_distance": 100 + count, + "tag": 4001 + count, + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + step( + "Advertise static routes from IPv4 unicast family & IPv6 unicast" + " family respectively from R1.Configure loopback on R1 with IPv4 " + "address & Advertise loopback from IPv4 unicast family " + "using network cmd from R1" + ) + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": {"unicast": {"redistribute": [{"redist_type": "static"}]}} + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + glip = get_llip("r1", "r2-link0") + assert glip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "IPv4 and IPv6 routes advertised using static & network cmd are" + "received on R2 BGP and routing table , verify using show ip bgp" + "show ip route for IPv4 routes and show bgp, show ipv6 routes" + "for IPv6 routes ." + ) + + dut = "r2" + protocol = "bgp" + count = 0 + # verify the routes with nh as ext_nh + verify_nh_for_static_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "no_of_ip": NO_OF_RTES, + "next_hop": glip, + "admin_distance": 100 + count, + "tag": 4001 + count, + } + ] + } + } + bgp_rib = verify_bgp_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=glip + ) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=glip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + count = 0 + for rte in range(0, NO_OF_RTES): + count += 10 + input_dict_2 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + { + "seqid": 0 + count, + "action": "permit", + "network": NETWORK["ipv4"][rte], + } + ] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n " "Error: {}".format( + tc_name, result + ) + + # Create route map + input_dict_6 = { + "r3": { + "route_maps": { + "rmap_match_tag_1_{}".format("ipv4"): [ + { + "action": "deny", + "match": { + "ipv4": {"prefix_lists": "pf_list_1_{}".format("ipv4")} + }, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_6) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + input_dict_7 = { + "r1": { + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link0": { + "route_maps": [ + { + "name": "rmap_match_tag_1_ipv4", + "direction": "out", + } + ] + } + } + } + } + } + } + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_7) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_ibgp_loopback_nbr_p1(request): + """ + Verify Extended capability nexthop with loopback interface. + + Verify IPv4 routes are advertised and withdrawn when IPv6 IBGP + session established using loopback interface + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + global topo + topo1 = deepcopy(topo) + reset_config_on_routers(tgen) + step("Configure IPv6 global address between R1 and R2") + step( + "Configure loopback on R1 and R2 and establish EBGP session " + "between R1 and R2 over loopback global ip" + ) + step("Configure static route on R1 and R2 for loopback reachability") + step("Enable cap ext nh on r1 and r2 and activate in ipv4 addr family") + + for routerN in ["r1", "r2"]: + for addr_type in ["ipv6"]: + for bgp_neighbor in topo1["routers"][routerN]["bgp"]["address_family"][ + addr_type + ]["unicast"]["neighbor"].keys(): + # Adding ['source_link'] = 'lo' key:value pair + if bgp_neighbor == "r1" or bgp_neighbor == "r2": + topo1["routers"][routerN]["bgp"]["address_family"][addr_type][ + "unicast" + ]["neighbor"][bgp_neighbor]["dest_link"] = { + "lo": { + "source_link": "lo", + "ebgp_multihop": 2, + "capability": "extended-nexthop", + "activate": "ipv4", + } + } + # Creating configuration from JSON + build_config_from_json(tgen, topo1, save_bkup=False) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": {"r1-link0": {"deactivate": "ipv6"}} + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo1, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + configure_bgp_on_r2 = { + "r2": { + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": {"r2-link0": {"deactivate": "ipv6"}} + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo1, configure_bgp_on_r2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": {"r1-link0": {"deactivate": "ipv4"}} + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo1, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + configure_bgp_on_r2 = { + "r2": { + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": {"r2-link0": {"deactivate": "ipv4"}} + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo1, configure_bgp_on_r2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + r2_lo_v4 = topo["routers"]["r2"]["links"]["lo"]["ipv4"] + r2_lo_v6 = topo["routers"]["r2"]["links"]["lo"]["ipv6"] + r1_lo_v4 = topo["routers"]["r1"]["links"]["lo"]["ipv4"] + r1_lo_v6 = topo["routers"]["r1"]["links"]["lo"]["ipv6"] + r1_r2_intf = topo["routers"]["r1"]["links"]["r2-link0"]["interface"] + r2_r1_intf = topo["routers"]["r2"]["links"]["r1-link0"]["interface"] + + r1_r2_v6_nh = topo["routers"]["r1"]["links"]["r2-link0"]["ipv6"].split("/")[0] + r2_r1_v6_nh = topo["routers"]["r2"]["links"]["r1-link0"]["ipv6"].split("/")[0] + + ipv4_list = [("r1", r1_r2_intf, [r2_lo_v4]), ("r2", r2_r1_intf, [r1_lo_v4])] + + ipv6_list = [ + ("r1", r1_r2_intf, [r2_lo_v6], r2_r1_v6_nh), + ("r2", r2_r1_intf, [r1_lo_v6], r1_r2_v6_nh), + ] + + for dut, intf, loop_addr in ipv4_list: + result = addKernelRoute(tgen, dut, intf, loop_addr) + # assert result is True, "Testcase {}:Failed \n Error: {}". \ + # format(tc_name, result) + + for dut, intf, loop_addr, next_hop in ipv6_list: + result = addKernelRoute(tgen, dut, intf, loop_addr, next_hop) + # assert result is True, "Testcase {}:Failed \n Error: {}". \ + # format(tc_name, result) + + r2_lo_v4 = topo["routers"]["r2"]["links"]["lo"]["ipv4"] + r2_lo_v6 = topo["routers"]["r2"]["links"]["lo"]["ipv6"] + r1_lo_v4 = topo["routers"]["r1"]["links"]["lo"]["ipv4"] + r1_lo_v6 = topo["routers"]["r1"]["links"]["lo"]["ipv6"] + r1_r2_intf = topo["routers"]["r1"]["links"]["r2-link0"]["interface"] + r2_r1_intf = topo["routers"]["r2"]["links"]["r1-link0"]["interface"] + + r1_r2_v6_nh = topo["routers"]["r1"]["links"]["r2-link0"]["ipv6"].split("/")[0] + r2_r1_v6_nh = topo["routers"]["r2"]["links"]["r1-link0"]["ipv6"].split("/")[0] + + r1_r2_v4_nh = topo["routers"]["r1"]["links"]["r2-link0"]["ipv4"].split("/")[0] + r2_r1_v4_nh = topo["routers"]["r2"]["links"]["r1-link0"]["ipv4"].split("/")[0] + + input_dict = { + "r1": { + "static_routes": [ + {"network": r2_lo_v4, "next_hop": r2_r1_v4_nh}, + {"network": r2_lo_v6, "next_hop": r2_r1_v6_nh}, + ] + }, + "r2": { + "static_routes": [ + {"network": r1_lo_v4, "next_hop": r1_r2_v4_nh}, + {"network": r1_lo_v6, "next_hop": r1_r2_v6_nh}, + ] + }, + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + # Api call verify whether BGP is converged + result = verify_bgp_convergence(tgen, topo1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Enable cap ext nh on r1 and r2 and activate in ipv4 addr family") + configure_bgp_on_r1 = { + "r1": { + "default_ipv4_unicast": False, + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "lo": { + "activate": "ipv4", + "capability": "extended-nexthop", + } + } + } + } + } + } + } + }, + } + } + result = create_router_bgp(tgen, topo1, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + configure_bgp_on_r2 = { + "r2": { + "default_ipv4_unicast": False, + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "lo": { + "activate": "ipv4", + "capability": "extended-nexthop", + } + } + } + } + } + } + } + }, + } + } + result = create_router_bgp(tgen, topo1, configure_bgp_on_r2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify bgp convergence.") + bgp_convergence = verify_bgp_convergence(tgen, topo1) + assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, bgp_convergence + ) + + step("Configure 2 IPv4 static" " routes on R1, Nexthop as different links of R0") + + for rte in range(0, NO_OF_RTES): + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][rte], + "no_of_ip": 1, + "next_hop": NEXT_HOP["ipv4"][rte], + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise static routes from IPv4 unicast family and IPv6 " + "unicast family respectively from R1 using red static cmd " + "Advertise loopback from IPv4 unicast family using network command " + "from R1" + ) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{"redist_type": "static"}], + "advertise_networks": [ + {"network": NETWORK_CMD_IP, "no_of_network": 1} + ], + } + } + } + } + } + } + result = create_router_bgp(tgen, topo1, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "IPv4 routes advertised using static and network command are " + " received on R2 BGP and routing table , " + "verify using show ip bgp, show ip route for IPv4 routes ." + ) + + gllip = (topo1["routers"]["r1"]["links"]["lo"]["ipv6"].split("/")[0]).lower() + assert gllip is not None, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + dut = "r2" + protocol = "bgp" + verify_nh_for_static_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "no_of_ip": NO_OF_RTES, + "next_hop": gllip, + } + ] + } + } + bgp_rib = verify_bgp_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gllip + ) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gllip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + verify_nh_for_nw_rtes = { + "r1": { + "static_routes": [ + {"network": NETWORK_CMD_IP, "no_of_ip": 1, "next_hop": gllip} + ] + } + } + bgp_rib = verify_bgp_rib(tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=gllip) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=gllip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Remove IPv4 routes advertised using network command" + " from R1 and advertise again" + ) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{"redist_type": "static"}], + "advertise_networks": [ + { + "network": NETWORK_CMD_IP, + "no_of_network": 1, + "delete": True, + } + ], + } + } + } + } + } + } + result = create_router_bgp(tgen, topo1, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{"redist_type": "static"}], + "advertise_networks": [ + { + "network": NETWORK_CMD_IP, + "no_of_network": 1, + } + ], + } + } + } + } + } + } + result = create_router_bgp(tgen, topo1, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + step( + "After removing IPv4 routes from network command , routes which are " + "advertised using redistribute static are still present in the on " + "R2 , verify using show ip bgp and show ip route" + ) + + verify_nh_for_nw_rtes = { + "r1": { + "static_routes": [ + {"network": NETWORK_CMD_IP, "no_of_ip": 1, "next_hop": gllip} + ] + } + } + bgp_rib = verify_bgp_rib(tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=gllip) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=gllip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Remove IPv4 routes advertised using redistribute static" + " command from R1 and advertise again" + ) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{"redist_type": "static", "delete": True}] + } + } + } + } + } + } + result = create_router_bgp(tgen, topo1, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": {"unicast": {"redistribute": [{"redist_type": "static"}]}} + } + } + } + } + result = create_router_bgp(tgen, topo1, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + step( + "After removing IPv4 routes from redistribute static , routes which" + " are advertised using network are still present in the on R2 , " + "verify using show ip bgp and show ip route" + ) + + verify_nh_for_nw_rtes = { + "r1": { + "static_routes": [ + {"network": NETWORK_CMD_IP, "no_of_ip": 1, "next_hop": gllip} + ] + } + } + bgp_rib = verify_bgp_rib(tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=gllip) + assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib) + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=gllip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ibgp_unnumbered_nbr.py b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ibgp_unnumbered_nbr.py new file mode 100644 index 0000000000..5f4292b81e --- /dev/null +++ b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ibgp_unnumbered_nbr.py @@ -0,0 +1,347 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2021 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. +# ("NetDEF") in this file. +# +# 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 VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE 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. +# + + +"""RFC5549 Automation.""" +import os +import sys +import time +import json +import pytest +import ipaddr +from re import search as re_search + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from mininet.topo import Topo + +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + create_interfaces_cfg, + verify_rib, + create_static_routes, + check_address_types, + step, + reset_config_on_routers, + get_frr_ipv6_linklocal, +) +from lib.topolog import logger +from lib.bgp import clear_bgp, verify_bgp_convergence, create_router_bgp +from lib.topojson import build_topo_from_json, build_config_from_json + +# Global variables +topo = None +# Reading the data from JSON File for topology creation +jsonFile = "{}/rfc5549_ibgp_unnumbered_nbr.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + + +# Global variables +NETWORK_CMD_IP = "1.0.1.17/32" +NETWORK = { + "ipv4": [ + "11.0.20.1/32", + "11.0.20.2/32", + "11.0.20.3/32", + "11.0.20.4/32", + "11.0.20.5/32", + ], + "ipv6": ["1::1/128", "1::2/128", "1::3/128", "1::4/128", "1::5/128"], +} +MASK = {"ipv4": "32", "ipv6": "128"} +NEXT_HOP = { + "ipv4": ["10.0.0.1", "10.0.1.1", "10.0.2.1", "10.0.3.1", "10.0.4.1"], + "ipv6": ["Null0", "Null0", "Null0", "Null0", "Null0"], +} +ADDR_TYPES = check_address_types() +NO_OF_RTES = 2 +TOPOOLOGY = """ + Please view in a fixed-width font such as Courier. + +----+ + | R4 | + | | + +--+-+ + | ipv4 nbr + no bgp ebgp/ibgp | + | ebgp/ibgp + +----+ 2links +----+ 8links +--+-+ +----+ + |R0 +----------+ R1 + + R2 | ipv6 nbr |R3 | + | +----------+ +------------+ +-------------+ | + +----+ +----+ ipv6 nbr +----+ +----+ +""" + +TESTCASES = """ +1. Verify IPv4 routes are deleted after un-configuring "network command +" and "redistribute static knob" with Unnumbered IPv6 IBGP session + """ + + +class CreateTopo(Topo): + """ + Test topology builder. + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function.""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """Set up the pytest environment.""" + + global topo + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(CreateTopo, mod.__name__) + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error:" " {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment.""" + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + +def get_llip(onrouter, intf): + """ + API to get the link local ipv6 address of a perticular interface + + Parameters + ---------- + * `fromnode`: Source node + * `tonode` : interface for which link local ip needs to be returned. + + Usage + ----- + result = get_llip('r1', 'r2-link0') + + Returns + ------- + 1) link local ipv6 address from the interface. + 2) errormsg - when link local ip not found. + """ + tgen = get_topogen() + intf = topo["routers"][onrouter]["links"][intf]["interface"] + llip = get_frr_ipv6_linklocal(tgen, onrouter, intf) + + if llip: + logger.info("llip ipv6 address to be set as NH is %s", llip) + return llip + return None + + +def get_glipv6(onrouter, intf): + """ + API to get the global ipv6 address of a perticular interface + + Parameters + ---------- + * `onrouter`: Source node + * `intf` : interface for which link local ip needs to be returned. + + Usage + ----- + result = get_glipv6('r1', 'r2-link0') + + Returns + ------- + 1) global ipv6 address from the interface. + 2) errormsg - when link local ip not found. + """ + glipv6 = (topo["routers"][onrouter]["links"][intf]["ipv6"]).split("/")[0] + if glipv6: + logger.info("Global ipv6 address to be set as NH is %s", glipv6) + return glipv6 + return None + + +# ################################## +# Test cases start here. +# ################################## + + +def test_ext_nh_cap_red_static_network_ebgp_peer_unnumbered_nbr_p1(request): + """ + + Test extended capability nexthop. + + Verify IPv4 routes advertise using "redistribute static" and + "network command" are received on EBGP peer with IPv6 nexthop + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + reset_config_on_routers(tgen) + step( + "Configure IPv6 IBGP Unnumbered session between R1 and R2 and enable " + "ipv6 nd ra-interval 10 in the interface" + ) + + step( + "Enable capability extended-nexthop" + "on the neighbor from both the routers and " + "ipv6 nd ra-interval 10 on link connected between R1 and R2" + ) + + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert bgp_convergence is True, "Testcase :Failed \n Error:" " {}".format( + bgp_convergence + ) + + for rte in range(0, NO_OF_RTES): + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][rte], + "no_of_ip": 1, + "next_hop": NEXT_HOP["ipv4"][rte], + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + step( + "Advertise static routes from IPv4 unicast family and IPv6 unicast " + "family respectively from R1 " + "Configure loopback on R1 with IPv4 address Advertise loopback " + "from IPv4 unicast family using network cmd from R1 " + ) + + configure_bgp_on_r1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{"redist_type": "static"}], + "advertise_networks": [ + {"network": NETWORK_CMD_IP, "no_of_network": 1} + ], + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + llip = get_llip("r1", "r2-link0") + assert llip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + step( + " IPv4 and IPv6 routes advertised using static and network command are" + " received on R2 BGP and routing table , verify using show ip bgp" + " show ip route for IPv4 routes and show bgp show ipv6 routes" + " for IPv6 routes ." + ) + + dut = "r2" + protocol = "bgp" + verify_nh_for_static_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "no_of_ip": NO_OF_RTES, + "next_hop": llip, + } + ] + } + } + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=llip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + verify_nh_for_nw_cmd_rtes = { + "r1": { + "static_routes": [ + { + "network": NETWORK_CMD_IP, + "no_of_ip": 1, + "next_hop": llip, + } + ] + } + } + + result = verify_rib( + tgen, "ipv4", dut, verify_nh_for_nw_cmd_rtes, next_hop=llip, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_minimum_holdtime/__init__.py b/tests/topotests/bgp_minimum_holdtime/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/bgp_minimum_holdtime/__init__.py diff --git a/tests/topotests/bgp_minimum_holdtime/r1/bgpd.conf b/tests/topotests/bgp_minimum_holdtime/r1/bgpd.conf new file mode 100644 index 0000000000..847a2d4b08 --- /dev/null +++ b/tests/topotests/bgp_minimum_holdtime/r1/bgpd.conf @@ -0,0 +1,6 @@ +router bgp 65000 + bgp minimum-holdtime 20 + neighbor 192.168.255.2 remote-as 65001 + neighbor 192.168.255.2 timers 3 10 + neighbor 192.168.255.2 timers connect 10 +! diff --git a/tests/topotests/bgp_minimum_holdtime/r1/zebra.conf b/tests/topotests/bgp_minimum_holdtime/r1/zebra.conf new file mode 100644 index 0000000000..e2c399e536 --- /dev/null +++ b/tests/topotests/bgp_minimum_holdtime/r1/zebra.conf @@ -0,0 +1,6 @@ +! +interface r1-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_minimum_holdtime/r2/bgpd.conf b/tests/topotests/bgp_minimum_holdtime/r2/bgpd.conf new file mode 100644 index 0000000000..6d1080c119 --- /dev/null +++ b/tests/topotests/bgp_minimum_holdtime/r2/bgpd.conf @@ -0,0 +1,5 @@ +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 65000 + neighbor 192.168.255.1 timers 3 10 +! diff --git a/tests/topotests/bgp_minimum_holdtime/r2/zebra.conf b/tests/topotests/bgp_minimum_holdtime/r2/zebra.conf new file mode 100644 index 0000000000..606c17bec9 --- /dev/null +++ b/tests/topotests/bgp_minimum_holdtime/r2/zebra.conf @@ -0,0 +1,6 @@ +! +interface r2-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_minimum_holdtime/test_bgp_minimum_holdtime.py b/tests/topotests/bgp_minimum_holdtime/test_bgp_minimum_holdtime.py new file mode 100755 index 0000000000..c5afcdf112 --- /dev/null +++ b/tests/topotests/bgp_minimum_holdtime/test_bgp_minimum_holdtime.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python + +# Copyright (c) 2021 by +# Takemasa Imada <takemasa.imada@gmail.com> +# +# 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 if minimum-holdtime works. +""" + +import os +import sys +import json +import time +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from mininet.topo import Topo + +pytestmark = [pytest.mark.bgpd] + + +class TemplateTopo(Topo): + def build(self, *_args, **_opts): + tgen = get_topogen(self) + + for routern in range(1, 3): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + tgen = Topogen(TemplateTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_minimum_holdtime(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def _bgp_neighbor_check_if_notification_sent(): + output = json.loads( + tgen.gears["r1"].vtysh_cmd("show ip bgp neighbor 192.168.255.2 json") + ) + expected = { + "192.168.255.2": { + "connectionsEstablished": 0, + "lastNotificationReason": "OPEN Message Error/Unacceptable Hold Time", + "lastResetDueTo": "BGP Notification send", + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_neighbor_check_if_notification_sent) + success, result = topotest.run_and_expect(test_func, None, count=40, wait=0.5) + assert result is None, "Failed to send notification message\n" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_multi_vrf_topo1/test_bgp_multi_vrf_topo1.py b/tests/topotests/bgp_multi_vrf_topo1/test_bgp_multi_vrf_topo1.py index c9a93bd75f..84e10af5b3 100644 --- a/tests/topotests/bgp_multi_vrf_topo1/test_bgp_multi_vrf_topo1.py +++ b/tests/topotests/bgp_multi_vrf_topo1/test_bgp_multi_vrf_topo1.py @@ -178,14 +178,10 @@ NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"} LOOPBACK_1 = { "ipv4": "10.10.10.10/32", "ipv6": "10::10:10/128", - "ipv4_mask": "255.255.255.255", - "ipv6_mask": None, } LOOPBACK_2 = { "ipv4": "20.20.20.20/32", "ipv6": "20::20:20/128", - "ipv4_mask": "255.255.255.255", - "ipv6_mask": None, } @@ -1911,7 +1907,6 @@ def test_static_routes_for_inter_vrf_route_leaking_p0(request): "loopback1", LOOPBACK_1[addr_type], "RED_A", - LOOPBACK_1["{}_mask".format(addr_type)], ) create_interface_in_kernel( tgen, @@ -1919,7 +1914,6 @@ def test_static_routes_for_inter_vrf_route_leaking_p0(request): "loopback2", LOOPBACK_2[addr_type], "RED_B", - LOOPBACK_2["{}_mask".format(addr_type)], ) step( @@ -2047,7 +2041,6 @@ def test_inter_vrf_and_intra_vrf_communication_iBGP_p0(request): "loopback1", LOOPBACK_1[addr_type], "RED_A", - LOOPBACK_1["{}_mask".format(addr_type)], ) create_interface_in_kernel( @@ -2056,7 +2049,6 @@ def test_inter_vrf_and_intra_vrf_communication_iBGP_p0(request): "loopback2", LOOPBACK_2[addr_type], "BLUE_A", - LOOPBACK_2["{}_mask".format(addr_type)], ) step( @@ -2216,7 +2208,6 @@ def test_inter_vrf_and_intra_vrf_communication_eBGP_p0(request): "loopback1", LOOPBACK_1[addr_type], "RED_A", - LOOPBACK_1["{}_mask".format(addr_type)], ) create_interface_in_kernel( tgen, @@ -2224,7 +2215,6 @@ def test_inter_vrf_and_intra_vrf_communication_eBGP_p0(request): "loopback2", LOOPBACK_2[addr_type], "BLUE_A", - LOOPBACK_2["{}_mask".format(addr_type)], ) step( diff --git a/tests/topotests/bgp_multi_vrf_topo2/test_bgp_multi_vrf_topo2.py b/tests/topotests/bgp_multi_vrf_topo2/test_bgp_multi_vrf_topo2.py index 37da53fc31..31569e69b4 100644 --- a/tests/topotests/bgp_multi_vrf_topo2/test_bgp_multi_vrf_topo2.py +++ b/tests/topotests/bgp_multi_vrf_topo2/test_bgp_multi_vrf_topo2.py @@ -131,8 +131,6 @@ NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"} LOOPBACK_2 = { "ipv4": "20.20.20.20/32", "ipv6": "20::20:20/128", - "ipv4_mask": "255.255.255.255", - "ipv6_mask": None, } MAX_PATHS = 2 @@ -1928,7 +1926,6 @@ def test_vrf_route_leaking_next_hop_interface_flapping_p1(request): "loopback2", LOOPBACK_2[addr_type], "RED_B", - LOOPBACK_2["{}_mask".format(addr_type)], ) intf_red1_r11 = topo["routers"]["red1"]["links"]["r1-link2"]["interface"] diff --git a/tests/topotests/bgp_recursive_route_ebgp_multi_hop/test_bgp_recursive_route_ebgp_multi_hop.py b/tests/topotests/bgp_recursive_route_ebgp_multi_hop/test_bgp_recursive_route_ebgp_multi_hop.py index e7d70f6d8e..2fe80c77f0 100644 --- a/tests/topotests/bgp_recursive_route_ebgp_multi_hop/test_bgp_recursive_route_ebgp_multi_hop.py +++ b/tests/topotests/bgp_recursive_route_ebgp_multi_hop/test_bgp_recursive_route_ebgp_multi_hop.py @@ -1103,7 +1103,7 @@ def test_next_hop_with_recursive_lookup_p1(request): tc_name, result ) - step("Toggle the interface on R3(ifconfig 192.34).") + step("Toggle the interface on R3.") intf_r3_r4 = topo["routers"]["r3"]["links"]["r4"]["interface"] shutdown_bringup_interface(tgen, "r3", intf_r3_r4) @@ -1161,7 +1161,7 @@ def test_next_hop_with_recursive_lookup_p1(request): tc_name, result ) - step("Toggle the interface on R4(ifconfig 192.34).") + step("Toggle the interface on R4.") intf_r4_r3 = topo["routers"]["r4"]["links"]["r3"]["interface"] shutdown_bringup_interface(tgen, "r4", intf_r4_r3) diff --git a/tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo1.py b/tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo1.py index d6f1058a98..ea1b1a42d7 100644 --- a/tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo1.py +++ b/tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo1.py @@ -123,8 +123,6 @@ LOOPBACK_1 = { LOOPBACK_2 = { "ipv4": "10.0.0.16/24", "ipv6": "fd00:0:0:3::5/64", - "ipv4_mask": "255.255.255.0", - "ipv6_mask": None, } PREFERRED_NEXT_HOP = "global" @@ -692,14 +690,13 @@ def test_dynamic_imported_routes_advertised_to_iBGP_peer_p0(request): "loopback2", LOOPBACK_2[addr_type], "ISR", - LOOPBACK_2["{}_mask".format(addr_type)], ) for addr_type in ADDR_TYPES: step( "On router R1 Change the next-hop of static routes in vrf " - "ISR to LOOPBACK_1" + "ISR to LOOPBACK_2" ) input_routes_r1 = { diff --git a/tests/topotests/bgp_vrf_netns/r1/bgpd.conf b/tests/topotests/bgp_vrf_netns/r1/bgpd.conf index cfe3f2e2b5..572dce7455 100644 --- a/tests/topotests/bgp_vrf_netns/r1/bgpd.conf +++ b/tests/topotests/bgp_vrf_netns/r1/bgpd.conf @@ -1,5 +1,5 @@ ! -router bgp 100 vrf r1-cust1 +router bgp 100 vrf r1-bgp-cust1 bgp router-id 10.0.1.1 bgp bestpath as-path multipath-relax no bgp ebgp-requires-policy diff --git a/tests/topotests/bgp_vrf_netns/r1/summary.txt b/tests/topotests/bgp_vrf_netns/r1/summary.txt index 1a079ff130..819f26133f 100644 --- a/tests/topotests/bgp_vrf_netns/r1/summary.txt +++ b/tests/topotests/bgp_vrf_netns/r1/summary.txt @@ -2,7 +2,7 @@ "ipv4Unicast":{ "routerId":"10.0.1.1", "as":100, - "vrfName":"r1-cust1", + "vrfName":"r1-bgp-cust1", "peerCount":1, "peers":{ "10.0.1.101":{ diff --git a/tests/topotests/bgp_vrf_netns/r1/summary20.txt b/tests/topotests/bgp_vrf_netns/r1/summary20.txt index 2b5787e6da..ea04a56d85 100644 --- a/tests/topotests/bgp_vrf_netns/r1/summary20.txt +++ b/tests/topotests/bgp_vrf_netns/r1/summary20.txt @@ -1,7 +1,7 @@ { "routerId":"10.0.1.1", "as":100, - "vrfName":"re1-cust1", + "vrfName":"re1-bgp-cust1", "peerCount":1, "peers":{ "10.0.1.101":{ diff --git a/tests/topotests/bgp_vrf_netns/r1/zebra.conf b/tests/topotests/bgp_vrf_netns/r1/zebra.conf index 817d9544d3..fd0e18f5fd 100644 --- a/tests/topotests/bgp_vrf_netns/r1/zebra.conf +++ b/tests/topotests/bgp_vrf_netns/r1/zebra.conf @@ -1,5 +1,5 @@ ! -interface r1-eth0 vrf r1-cust1 +interface r1-eth0 vrf r1-bgp-cust1 ip address 10.0.1.1/24 ! line vty diff --git a/tests/topotests/bgp_vrf_netns/test_bgp_vrf_netns_topo.py b/tests/topotests/bgp_vrf_netns/test_bgp_vrf_netns_topo.py index 9889e1cdd5..60511aebde 100644 --- a/tests/topotests/bgp_vrf_netns/test_bgp_vrf_netns_topo.py +++ b/tests/topotests/bgp_vrf_netns/test_bgp_vrf_netns_topo.py @@ -106,13 +106,12 @@ def setup_module(module): if CustomizeVrfWithNetns == True: logger.info("Testing with VRF Namespace support") - # create VRF r1-cust1 - # move r1-eth0 to VRF r1-cust1 + # create VRF r1-bgp-cust1 + # move r1-eth0 to VRF r1-bgp-cust1 cmds = [ - "if [ -e /var/run/netns/{0}-cust1 ] ; then ip netns del {0}-cust1 ; fi", - "ip netns add {0}-cust1", - "ip link set dev {0}-eth0 netns {0}-cust1", - "ip netns exec {0}-cust1 ifconfig {0}-eth0 up", + "if [ -e /var/run/netns/{0}-bgp-cust1 ] ; then ip netns del {0}-bgp-cust1 ; fi", + "ip netns add {0}-bgp-cust1", + "ip link set {0}-eth0 netns {0}-bgp-cust1 up", ] for cmd in cmds: cmd = cmd.format("r1") @@ -154,10 +153,10 @@ def setup_module(module): def teardown_module(module): tgen = get_topogen() # move back r1-eth0 to default VRF - # delete VRF r1-cust1 + # delete VRF r1-bgp-cust1 cmds = [ - "ip netns exec {0}-cust1 ip link set {0}-eth0 netns 1", - "ip netns delete {0}-cust1", + "ip netns exec {0}-bgp-cust1 ip link set {0}-eth0 netns 1", + "ip netns delete {0}-bgp-cust1", ] for cmd in cmds: tgen.net["r1"].cmd(cmd.format("r1")) @@ -203,7 +202,7 @@ def test_bgp_convergence(): expected = json.loads(open(reffile).read()) test_func = functools.partial( - topotest.router_json_cmp, router, "show bgp vrf r1-cust1 summary json", expected + topotest.router_json_cmp, router, "show bgp vrf r1-bgp-cust1 summary json", expected ) _, res = topotest.run_and_expect(test_func, None, count=90, wait=0.5) assertmsg = "BGP router network did not converge" @@ -231,11 +230,11 @@ def test_bgp_vrf_netns(): test_func = functools.partial( topotest.router_json_cmp, tgen.gears["r1"], - "show ip bgp vrf r1-cust1 ipv4 json", + "show ip bgp vrf r1-bgp-cust1 ipv4 json", expect, ) _, res = topotest.run_and_expect(test_func, None, count=12, wait=0.5) - assertmsg = 'expected routes in "show ip bgp vrf r1-cust1 ipv4" output' + assertmsg = 'expected routes in "show ip bgp vrf r1-bgp-cust1 ipv4" output' assert res is None, assertmsg diff --git a/tests/topotests/conftest.py b/tests/topotests/conftest.py index d119b0931b..76e4714bfa 100755 --- a/tests/topotests/conftest.py +++ b/tests/topotests/conftest.py @@ -244,11 +244,11 @@ def pytest_runtest_makereport(item, call): modname = parent.module.__name__ # Treat skips as non errors, don't pause after - if call.excinfo.typename != "AssertionError": + if call.excinfo.typename == "Skipped": pause = False error = False logger.info( - 'assert skipped at "{}/{}": {}'.format( + 'test skipped at "{}/{}": {}'.format( modname, item.name, call.excinfo.value ) ) @@ -257,7 +257,7 @@ def pytest_runtest_makereport(item, call): # Handle assert failures parent._previousfailed = item # pylint: disable=W0212 logger.error( - 'assert failed at "{}/{}": {}'.format( + 'test failed at "{}/{}": {}'.format( modname, item.name, call.excinfo.value ) ) diff --git a/tests/topotests/lib/bgp.py b/tests/topotests/lib/bgp.py index 2f1f67439f..afa7f7fdaf 100644 --- a/tests/topotests/lib/bgp.py +++ b/tests/topotests/lib/bgp.py @@ -33,7 +33,7 @@ from lib.topotest import frr_unicode # Import common_config to use commomnly used APIs from lib.common_config import ( - create_common_configuration, + create_common_configurations, InvalidCLIError, load_config_to_router, check_address_types, @@ -148,6 +148,8 @@ def create_router_bgp(tgen, topo, input_dict=None, build=False, load_config=True topo = topo["routers"] input_dict = deepcopy(input_dict) + config_data_dict = {} + for router in input_dict.keys(): if "bgp" not in input_dict[router]: logger.debug("Router %s: 'bgp' not present in input_dict", router) @@ -158,6 +160,8 @@ def create_router_bgp(tgen, topo, input_dict=None, build=False, load_config=True if type(bgp_data_list) is not list: bgp_data_list = [bgp_data_list] + config_data = [] + for bgp_data in bgp_data_list: data_all_bgp = __create_bgp_global(tgen, bgp_data, router, build) if data_all_bgp: @@ -198,16 +202,19 @@ def create_router_bgp(tgen, topo, input_dict=None, build=False, load_config=True data_all_bgp = __create_l2vpn_evpn_address_family( tgen, topo, bgp_data, router, config_data=data_all_bgp ) + if data_all_bgp: + config_data.extend(data_all_bgp) - try: - result = create_common_configuration( - tgen, router, data_all_bgp, "bgp", build, load_config - ) - except InvalidCLIError: - # Traceback - errormsg = traceback.format_exc() - logger.error(errormsg) - return errormsg + if config_data: + config_data_dict[router] = config_data + + try: + result = create_common_configurations( + tgen, config_data_dict, "bgp", build, load_config + ) + except InvalidCLIError: + logger.error("create_router_bgp", exc_info=True) + result = False logger.debug("Exiting lib API: create_router_bgp()") return result @@ -226,7 +233,7 @@ def __create_bgp_global(tgen, input_dict, router, build=False): Returns ------- - True or False + list of config commands """ result = False @@ -241,7 +248,7 @@ def __create_bgp_global(tgen, input_dict, router, build=False): logger.debug( "Router %s: 'local_as' not present in input_dict" "for BGP", router ) - return False + return config_data local_as = bgp_data.setdefault("local_as", "") cmd = "router bgp {}".format(local_as) @@ -265,6 +272,7 @@ def __create_bgp_global(tgen, input_dict, router, build=False): if router_id: config_data.append("bgp router-id {}".format(router_id)) + config_data.append("bgp log-neighbor-changes") config_data.append("no bgp network import-check") bgp_peer_grp_data = bgp_data.setdefault("peer-group", {}) @@ -798,6 +806,7 @@ def __create_bgp_neighbor(topo, input_dict, router, addr_type, add_neigh=True): ) disable_connected = peer.setdefault("disable_connected_check", False) + connect = peer.setdefault("connecttimer", 120) keep_alive = peer.setdefault("keepalivetimer", 3) hold_down = peer.setdefault("holddowntimer", 10) password = peer.setdefault("password", None) @@ -827,6 +836,9 @@ def __create_bgp_neighbor(topo, input_dict, router, addr_type, add_neigh=True): config_data.append( "{} timers {} {}".format(neigh_cxt, keep_alive, hold_down) ) + if int(connect) != 120: + config_data.append("{} connect {}".format(neigh_cxt, connect)) + if graceful_restart: config_data.append("{} graceful-restart".format(neigh_cxt)) elif graceful_restart == False: @@ -1532,15 +1544,16 @@ def modify_as_number(tgen, topo, input_dict): create_router_bgp(tgen, topo, router_dict) logger.info("Applying modified bgp configuration") - create_router_bgp(tgen, new_topo) - + result = create_router_bgp(tgen, new_topo) + if result is not True: + result = "Error applying new AS number config" except Exception as e: errormsg = traceback.format_exc() logger.error(errormsg) return errormsg logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) - return True + return result @retry(retry_timeout=8) @@ -3765,7 +3778,7 @@ def verify_graceful_restart_timers(tgen, topo, addr_type, input_dict, dut, peer) @retry(retry_timeout=8) -def verify_gr_address_family(tgen, topo, addr_type, addr_family, dut, expected=True): +def verify_gr_address_family(tgen, topo, addr_type, addr_family, dut, peer, expected=True): """ This API is to verify gr_address_family in the BGP gr capability advertised by the neighbor router @@ -3777,80 +3790,86 @@ def verify_gr_address_family(tgen, topo, addr_type, addr_family, dut, expected=T * `addr_type` : ip type ipv4/ipv6 * `addr_type` : ip type IPV4 Unicast/IPV6 Unicast * `dut`: input dut router name + * `peer`: input peer router to check * `expected` : expected results from API, by-default True Usage ----- - result = verify_gr_address_family(tgen, topo, "ipv4", "ipv4Unicast", "r1") + result = verify_gr_address_family(tgen, topo, "ipv4", "ipv4Unicast", "r1", "r3") Returns ------- - errormsg(str) or True + errormsg(str) or None """ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) - for router, rnode in tgen.routers().items(): - if router != dut: - continue + if not check_address_types(addr_type): + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return - bgp_addr_type = topo["routers"][router]["bgp"]["address_family"] + routers = tgen.routers() + if dut not in routers: + return "{} not in routers".format(dut) - if addr_type in bgp_addr_type: - if not check_address_types(addr_type): - continue + rnode = routers[dut] + bgp_addr_type = topo["routers"][dut]["bgp"]["address_family"] - bgp_neighbors = bgp_addr_type[addr_type]["unicast"]["neighbor"] + if addr_type not in bgp_addr_type: + return "{} not in bgp_addr_types".format(addr_type) - for bgp_neighbor, peer_data in bgp_neighbors.items(): - for dest_link, peer_dict in peer_data["dest_link"].items(): - data = topo["routers"][bgp_neighbor]["links"] + if peer not in bgp_addr_type[addr_type]["unicast"]["neighbor"]: + return "{} not a peer of {} over {}".format(peer, dut, addr_type) - if dest_link in data: - neighbor_ip = data[dest_link][addr_type].split("/")[0] + nbr_links = topo["routers"][peer]["links"] + if dut not in nbr_links or addr_type not in nbr_links[dut]: + return "peer {} missing back link to {} over {}".format(peer, dut, addr_type) - logger.info( - "[DUT: {}]: Checking bgp graceful-restart" - " show o/p {}".format(dut, neighbor_ip) - ) + neighbor_ip = nbr_links[dut][addr_type].split("/")[0] - show_bgp_graceful_json = run_frr_cmd( - rnode, - "show bgp {} neighbor {} graceful-restart json".format( - addr_type, neighbor_ip - ), - isjson=True, - ) + logger.info( + "[DUT: {}]: Checking bgp graceful-restart show o/p {} for {}".format( + dut, neighbor_ip, addr_family + ) + ) - show_bgp_graceful_json_out = show_bgp_graceful_json[neighbor_ip] + show_bgp_graceful_json = run_frr_cmd( + rnode, + "show bgp {} neighbor {} graceful-restart json".format( + addr_type, neighbor_ip + ), + isjson=True, + ) - if show_bgp_graceful_json_out["neighborAddr"] == neighbor_ip: - logger.info("Neighbor ip matched {}".format(neighbor_ip)) - else: - errormsg = "Neighbor ip NOT a match {}".format(neighbor_ip) - return errormsg + show_bgp_graceful_json_out = show_bgp_graceful_json[neighbor_ip] - if addr_family == "ipv4Unicast": - if "ipv4Unicast" in show_bgp_graceful_json_out: - logger.info("ipv4Unicast present for {} ".format(neighbor_ip)) - return True - else: - errormsg = "ipv4Unicast NOT present for {} ".format(neighbor_ip) - return errormsg + if show_bgp_graceful_json_out["neighborAddr"] == neighbor_ip: + logger.info("Neighbor ip matched {}".format(neighbor_ip)) + else: + errormsg = "Neighbor ip NOT a match {}".format(neighbor_ip) + return errormsg - elif addr_family == "ipv6Unicast": - if "ipv6Unicast" in show_bgp_graceful_json_out: - logger.info("ipv6Unicast present for {} ".format(neighbor_ip)) - return True - else: - errormsg = "ipv6Unicast NOT present for {} ".format(neighbor_ip) - return errormsg - else: - errormsg = "Aaddress family: {} present for {} ".format( - addr_family, neighbor_ip - ) - return errormsg + if addr_family == "ipv4Unicast": + if "ipv4Unicast" in show_bgp_graceful_json_out: + logger.info("ipv4Unicast present for {} ".format(neighbor_ip)) + return True + else: + errormsg = "ipv4Unicast NOT present for {} ".format(neighbor_ip) + return errormsg + + elif addr_family == "ipv6Unicast": + if "ipv6Unicast" in show_bgp_graceful_json_out: + logger.info("ipv6Unicast present for {} ".format(neighbor_ip)) + return True + else: + errormsg = "ipv6Unicast NOT present for {} ".format(neighbor_ip) + return errormsg + else: + errormsg = "Aaddress family: {} present for {} ".format( + addr_family, neighbor_ip + ) + return errormsg logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) diff --git a/tests/topotests/lib/common_config.py b/tests/topotests/lib/common_config.py index 22a678862a..81c7ba4d5c 100644 --- a/tests/topotests/lib/common_config.py +++ b/tests/topotests/lib/common_config.py @@ -27,6 +27,7 @@ from re import search as re_search from tempfile import mkdtemp import json +import logging import os import sys import traceback @@ -34,6 +35,7 @@ import socket import subprocess import ipaddress import platform +import pytest try: # Imports from python2 @@ -274,7 +276,8 @@ def apply_raw_config(tgen, input_dict): True or errormsg """ - result = True + rlist = [] + for router_name in input_dict.keys(): config_cmd = input_dict[router_name]["raw_config"] @@ -286,13 +289,14 @@ def apply_raw_config(tgen, input_dict): for cmd in config_cmd: cfg.write("{}\n".format(cmd)) - result = load_config_to_router(tgen, router_name) + rlist.append(router_name) - return result + # Load config on all routers + return load_config_to_routers(tgen, rlist) -def create_common_configuration( - tgen, router, data, config_type=None, build=False, load_config=True +def create_common_configurations( + tgen, config_dict, config_type=None, build=False, load_config=True ): """ API to create object of class FRRConfig and also create frr_json.conf @@ -301,8 +305,8 @@ def create_common_configuration( Parameters ---------- * `tgen`: tgen object - * `data`: Configuration data saved in a list. - * `router` : router id to be configured. + * `config_dict`: Configuration data saved in a dict of { router: config-list } + * `routers` : list of router id to be configured. * `config_type` : Syntactic information while writing configuration. Should be one of the value as mentioned in the config_map below. * `build` : Only for initial setup phase this is set as True @@ -312,8 +316,6 @@ def create_common_configuration( """ TMPDIR = os.path.join(LOGDIR, tgen.modname) - fname = "{}/{}/{}".format(TMPDIR, router, FRRCFG_FILE) - config_map = OrderedDict( { "general_config": "! FRR General Config\n", @@ -338,27 +340,55 @@ def create_common_configuration( else: mode = "w" - try: - frr_cfg_fd = open(fname, mode) - if config_type: - frr_cfg_fd.write(config_map[config_type]) - for line in data: - frr_cfg_fd.write("{} \n".format(str(line))) - frr_cfg_fd.write("\n") - - except IOError as err: - logger.error( - "Unable to open FRR Config File. error(%s): %s" % (err.errno, err.strerror) - ) - return False - finally: - frr_cfg_fd.close() + routers = config_dict.keys() + for router in routers: + fname = "{}/{}/{}".format(TMPDIR, router, FRRCFG_FILE) + try: + frr_cfg_fd = open(fname, mode) + if config_type: + frr_cfg_fd.write(config_map[config_type]) + for line in config_dict[router]: + frr_cfg_fd.write("{} \n".format(str(line))) + frr_cfg_fd.write("\n") + + except IOError as err: + logger.error( + "Unable to open FRR Config '%s': %s" % (fname, str(err)) + ) + return False + finally: + frr_cfg_fd.close() # If configuration applied from build, it will done at last + result = True if not build and load_config: - load_config_to_router(tgen, router) + result = load_config_to_routers(tgen, routers) - return True + return result + + +def create_common_configuration( + tgen, router, data, config_type=None, build=False, load_config=True +): + """ + API to create object of class FRRConfig and also create frr_json.conf + file. It will create interface and common configurations and save it to + frr_json.conf and load to router + Parameters + ---------- + * `tgen`: tgen object + * `data`: Configuration data saved in a list. + * `router` : router id to be configured. + * `config_type` : Syntactic information while writing configuration. Should + be one of the value as mentioned in the config_map below. + * `build` : Only for initial setup phase this is set as True + Returns + ------- + True or False + """ + return create_common_configurations( + tgen, {router: data}, config_type, build, load_config + ) def kill_router_daemons(tgen, router, daemons, save_config=True): @@ -540,8 +570,8 @@ def reset_config_on_routers(tgen, routerName=None): '\nvtysh config apply => "{}"\nvtysh output <= "{}"'.format(vtysh_command, output) ) else: - router_list[rname].logger.error( - '\nvtysh config apply => "{}"\nvtysh output <= "{}"'.format(vtysh_command, output) + router_list[rname].logger.warning( + '\nvtysh config apply failed => "{}"\nvtysh output <= "{}"'.format(vtysh_command, output) ) logger.error("Delta file apply for %s failed %d: %s", rname, p.returncode, output) @@ -569,38 +599,46 @@ def reset_config_on_routers(tgen, routerName=None): for rname, p in procs.items(): output, _ = p.communicate() if p.returncode: - logger.warning( - "Get running config for %s failed %d: %s", rname, p.returncode, output - ) + logger.warning("Get running config for %s failed %d: %s", rname, p.returncode, output) else: - logger.info("Configuration on router {} after reset:\n{}".format(rname, output)) + logger.info("Configuration on router %s after reset:\n%s", rname, output) logger.debug("Exiting API: reset_config_on_routers") return True -def load_config_to_router(tgen, routerName, save_bkup=False): +def load_config_to_routers(tgen, routers, save_bkup=False): """ - Loads configuration on router from the file FRRCFG_FILE. + Loads configuration on routers from the file FRRCFG_FILE. Parameters ---------- * `tgen` : Topogen object - * `routerName` : router for which configuration to be loaded + * `routers` : routers for which configuration is to be loaded * `save_bkup` : If True, Saves snapshot of FRRCFG_FILE to FRRCFG_BKUP_FILE + Returns + ------- + True or False """ - logger.debug("Entering API: load_config_to_router") + logger.debug("Entering API: load_config_to_routers") - router_list = tgen.routers() - for rname in ROUTER_LIST: - if routerName and rname != routerName: + base_router_list = tgen.routers() + router_list = {} + for router in routers: + if (router not in ROUTER_LIST) or (router not in base_router_list): continue + router_list[router] = base_router_list[router] + + frr_cfg_file_fmt = TMPDIR + "/{}/" + FRRCFG_FILE + frr_cfg_bkup_fmt = TMPDIR + "/{}/" + FRRCFG_BKUP_FILE + procs = {} + for rname in router_list: router = router_list[rname] try: - frr_cfg_file = "{}/{}/{}".format(TMPDIR, rname, FRRCFG_FILE) - frr_cfg_bkup = "{}/{}/{}".format(TMPDIR, rname, FRRCFG_BKUP_FILE) + frr_cfg_file = frr_cfg_file_fmt.format(rname) + frr_cfg_bkup = frr_cfg_bkup_fmt.format(rname) with open(frr_cfg_file, "r+") as cfg: data = cfg.read() logger.info( @@ -610,31 +648,76 @@ def load_config_to_router(tgen, routerName, save_bkup=False): if save_bkup: with open(frr_cfg_bkup, "w") as bkup: bkup.write(data) + procs[rname] = router_list[rname].popen( + ["/usr/bin/env", "vtysh", "-f", frr_cfg_file], + stdin=None, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + ) + except IOError as err: + logging.error( + "Unable to open config File. error(%s): %s", + err.errno, err.strerror + ) + return False + except Exception as error: + logging.error("Unable to apply config on %s: %s", rname, str(error)) + return False - output = router.vtysh_multicmd(data, pretty_output=False) - for out_err in ERROR_LIST: - if out_err.lower() in output.lower(): - raise InvalidCLIError("%s" % output) + errors = [] + for rname, p in procs.items(): + output, _ = p.communicate() + frr_cfg_file = frr_cfg_file_fmt.format(rname) + vtysh_command = "vtysh -f " + frr_cfg_file + if not p.returncode: + router_list[rname].logger.info( + '\nvtysh config apply => "{}"\nvtysh output <= "{}"'.format(vtysh_command, output) + ) + else: + router_list[rname].logger.error( + '\nvtysh config apply failed => "{}"\nvtysh output <= "{}"'.format(vtysh_command, output) + ) + logger.error("Config apply for %s failed %d: %s", rname, p.returncode, output) + # We can't thorw an exception here as we won't clear the config file. + errors.append(InvalidCLIError("load_config_to_routers error for {}: {}".format(rname, output))) - cfg.truncate(0) + # Empty the config file or we append to it next time through. + with open(frr_cfg_file, "r+") as cfg: + cfg.truncate(0) - except IOError as err: - errormsg = ( - "Unable to open config File. error(%s):" " %s", - (err.errno, err.strerror), + # Router current configuration to log file or console if + # "show_router_config" is defined in "pytest.ini" + if show_router_config: + procs = {} + for rname in router_list: + procs[rname] = router_list[rname].popen( + ["/usr/bin/env", "vtysh", "-c", "show running-config no-header"], + stdin=None, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, ) - return errormsg + for rname, p in procs.items(): + output, _ = p.communicate() + if p.returncode: + logger.warning("Get running config for %s failed %d: %s", rname, p.returncode, output) + else: + logger.info("New configuration for router %s:\n%s", rname,output) - # Router current configuration to log file or console if - # "show_router_config" is defined in "pytest.ini" - if show_router_config: - logger.info("New configuration for router {}:".format(rname)) - new_config = router.run("vtysh -c 'show running'") - logger.info(new_config) + logger.debug("Exiting API: load_config_to_routers") + return not errors - logger.debug("Exiting API: load_config_to_router") - return True +def load_config_to_router(tgen, routerName, save_bkup=False): + """ + Loads configuration on router from the file FRRCFG_FILE. + + Parameters + ---------- + * `tgen` : Topogen object + * `routerName` : router for which configuration to be loaded + * `save_bkup` : If True, Saves snapshot of FRRCFG_FILE to FRRCFG_BKUP_FILE + """ + return load_config_to_routers(tgen, [routerName], save_bkup) def get_frr_ipv6_linklocal(tgen, router, intf=None, vrf=None): @@ -971,22 +1054,31 @@ def add_interfaces_to_vlan(tgen, input_dict): for intf_dict in interfaces: for interface, data in intf_dict.items(): # Adding interface to VLAN - cmd = "vconfig add {} {}".format(interface, vlan) + vlan_intf = "{}.{}".format(interface, vlan) + cmd = "ip link add link {} name {} type vlan id {}".format( + interface, + vlan_intf, + vlan + ) logger.info("[DUT: %s]: Running command: %s", dut, cmd) rnode.run(cmd) - vlan_intf = "{}.{}".format(interface, vlan) - - ip = data["ip"] - subnet = data["subnet"] - # Bringing interface up - cmd = "ip link set up {}".format(vlan_intf) + cmd = "ip link set {} up".format(vlan_intf) logger.info("[DUT: %s]: Running command: %s", dut, cmd) rnode.run(cmd) # Assigning IP address - cmd = "ifconfig {} {} netmask {}".format(vlan_intf, ip, subnet) + ifaddr = ipaddress.ip_interface( + u"{}/{}".format( + frr_unicode(data["ip"]), + frr_unicode(data["subnet"]) + ) + ) + + cmd = "ip -{0} a flush {1} scope global && ip a add {2} dev {1} && ip l set {1} up".format( + ifaddr.version, vlan_intf, ifaddr + ) logger.info("[DUT: %s]: Running command: %s", dut, cmd) rnode.run(cmd) @@ -1164,6 +1256,8 @@ def create_debug_log_config(tgen, input_dict, build=False): result = False try: + debug_config_dict = {} + for router in input_dict.keys(): debug_config = [] if "debug" in input_dict[router]: @@ -1194,10 +1288,12 @@ def create_debug_log_config(tgen, input_dict, build=False): for daemon, debug_logs in disable_logs.items(): for debug_log in debug_logs: debug_config.append("no {}".format(debug_log)) + if debug_config: + debug_config_dict[router] = debug_config - result = create_common_configuration( - tgen, router, debug_config, "debug_log_config", build=build - ) + result = create_common_configurations( + tgen, debug_config_dict, "debug_log_config", build=build + ) except InvalidCLIError: # Traceback errormsg = traceback.format_exc() @@ -1275,11 +1371,14 @@ def create_vrf_cfg(tgen, topo, input_dict=None, build=False): input_dict = deepcopy(input_dict) try: + config_data_dict = {} + for c_router, c_data in input_dict.items(): rnode = tgen.routers()[c_router] + config_data = [] + if "vrfs" in c_data: for vrf in c_data["vrfs"]: - config_data = [] del_action = vrf.setdefault("delete", False) name = vrf.setdefault("name", None) table_id = vrf.setdefault("id", None) @@ -1356,9 +1455,12 @@ def create_vrf_cfg(tgen, topo, input_dict=None, build=False): cmd = "no vni {}".format(del_vni) config_data.append(cmd) - result = create_common_configuration( - tgen, c_router, config_data, "vrf", build=build - ) + if config_data: + config_data_dict[c_router] = config_data + + result = create_common_configurations( + tgen, config_data_dict, "vrf", build=build + ) except InvalidCLIError: # Traceback @@ -1391,15 +1493,20 @@ def create_interface_in_kernel( rnode = tgen.routers()[dut] if create: - cmd = "sudo ip link add name {} type dummy".format(name) + cmd = "ip link show {0} >/dev/null || ip link add {0} type dummy".format(name) rnode.run(cmd) - addr_type = validate_ip_address(ip_addr) - if addr_type == "ipv4": - cmd = "ifconfig {} {} netmask {}".format(name, ip_addr, netmask) + if not netmask: + ifaddr = ipaddress.ip_interface(frr_unicode(ip_addr)) else: - cmd = "ifconfig {} inet6 add {}/{}".format(name, ip_addr, netmask) - + ifaddr = ipaddress.ip_interface(u"{}/{}".format( + frr_unicode(ip_addr), + frr_unicode(netmask) + )) + cmd = "ip -{0} a flush {1} scope global && ip a add {2} dev {1} && ip l set {1} up".format( + ifaddr.version, name, ifaddr + ) + logger.info("[DUT: %s]: Running command: %s", dut, cmd) rnode.run(cmd) if vrf: @@ -1587,7 +1694,7 @@ def find_interface_with_greater_ip(topo, router, loopback=True, interface=True): def write_test_header(tc_name): - """ Display message at beginning of test case""" + """Display message at beginning of test case""" count = 20 logger.info("*" * (len(tc_name) + count)) step("START -> Testcase : %s" % tc_name, reset=True) @@ -1595,7 +1702,7 @@ def write_test_header(tc_name): def write_test_footer(tc_name): - """ Display message at end of test case""" + """Display message at end of test case""" count = 21 logger.info("=" * (len(tc_name) + count)) logger.info("Testcase : %s -> PASSED", tc_name) @@ -1623,7 +1730,8 @@ def interface_status(tgen, topo, input_dict): logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) try: - global frr_cfg + rlist = [] + for router in input_dict.keys(): interface_list = input_dict[router]["interface_list"] @@ -1632,8 +1740,10 @@ def interface_status(tgen, topo, input_dict): rnode = tgen.routers()[router] interface_set_status(rnode, intf, status) - # Load config to router - load_config_to_router(tgen, router) + rlist.append(router) + + # Load config to routers + load_config_to_routers(tgen, rlist) except Exception as e: errormsg = traceback.format_exc() @@ -1822,6 +1932,8 @@ def create_interfaces_cfg(tgen, topo, build=False): topo = deepcopy(topo) try: + interface_data_dict = {} + for c_router, c_data in topo.items(): interface_data = [] for destRouterLink, data in sorted(c_data["links"].items()): @@ -1878,7 +1990,7 @@ def create_interfaces_cfg(tgen, topo, build=False): "network", "priority", "cost", - "mtu_ignore" + "mtu_ignore", ] if "ospf" in data: interface_data += _create_interfaces_ospf_cfg( @@ -1888,10 +2000,12 @@ def create_interfaces_cfg(tgen, topo, build=False): interface_data += _create_interfaces_ospf_cfg( "ospf6", c_data, data, ospf_keywords + ["area"] ) + if interface_data: + interface_data_dict[c_router] = interface_data - result = create_common_configuration( - tgen, c_router, interface_data, "interface_config", build=build - ) + result = create_common_configurations( + tgen, interface_data_dict, "interface_config", build=build + ) except InvalidCLIError: # Traceback @@ -1950,6 +2064,8 @@ def create_static_routes(tgen, input_dict, build=False): input_dict = deepcopy(input_dict) try: + static_routes_list_dict = {} + for router in input_dict.keys(): if "static_routes" not in input_dict[router]: errormsg = "static_routes not present in input_dict" @@ -2005,9 +2121,12 @@ def create_static_routes(tgen, input_dict, build=False): static_routes_list.append(cmd) - result = create_common_configuration( - tgen, router, static_routes_list, "static_route", build=build - ) + if static_routes_list: + static_routes_list_dict[router] = static_routes_list + + result = create_common_configurations( + tgen, static_routes_list_dict, "static_route", build=build + ) except InvalidCLIError: # Traceback @@ -2064,6 +2183,8 @@ def create_prefix_lists(tgen, input_dict, build=False): logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) result = False try: + config_data_dict = {} + for router in input_dict.keys(): if "prefix_lists" not in input_dict[router]: errormsg = "prefix_lists not present in input_dict" @@ -2110,9 +2231,12 @@ def create_prefix_lists(tgen, input_dict, build=False): cmd = "no {}".format(cmd) config_data.append(cmd) - result = create_common_configuration( - tgen, router, config_data, "prefix_list", build=build - ) + if config_data: + config_data_dict[router] = config_data + + result = create_common_configurations( + tgen, config_data_dict, "prefix_list", build=build + ) except InvalidCLIError: # Traceback @@ -2208,6 +2332,8 @@ def create_route_maps(tgen, input_dict, build=False): logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) input_dict = deepcopy(input_dict) try: + rmap_data_dict = {} + for router in input_dict.keys(): if "route_maps" not in input_dict[router]: logger.debug("route_maps not present in input_dict") @@ -2485,9 +2611,12 @@ def create_route_maps(tgen, input_dict, build=False): cmd = "match metric {}".format(metric) rmap_data.append(cmd) - result = create_common_configuration( - tgen, router, rmap_data, "route_maps", build=build - ) + if rmap_data: + rmap_data_dict[router] = rmap_data + + result = create_common_configurations( + tgen, rmap_data_dict, "route_maps", build=build + ) except InvalidCLIError: # Traceback @@ -2562,6 +2691,8 @@ def create_bgp_community_lists(tgen, input_dict, build=False): logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) input_dict = deepcopy(input_dict) try: + config_data_dict = {} + for router in input_dict.keys(): if "bgp_community_lists" not in input_dict[router]: errormsg = "bgp_community_lists not present in input_dict" @@ -2598,9 +2729,12 @@ def create_bgp_community_lists(tgen, input_dict, build=False): config_data.append(cmd) - result = create_common_configuration( - tgen, router, config_data, "bgp_community_list", build=build - ) + if config_data: + config_data_dict[router] = config_data + + result = create_common_configurations( + tgen, config_data_dict, "bgp_community_list", build=build + ) except InvalidCLIError: # Traceback @@ -2933,7 +3067,7 @@ def configure_interface_mac(tgen, input_dict): rnode = tgen.routers()[dut] for intf, mac in input_dict[dut].items(): - cmd = "ifconfig {} hw ether {}".format(intf, mac) + cmd = "ip link set {} address {}".format(intf, mac) logger.info("[DUT: %s]: Running command: %s", dut, cmd) try: @@ -4577,3 +4711,64 @@ def verify_ip_nht(tgen, input_dict): logger.debug("Exiting lib API: verify_ip_nht()") return False + + +def scapy_send_raw_packet( + tgen, topo, senderRouter, intf, packet=None, interval=1, count=1 +): + """ + Using scapy Raw() method to send BSR raw packet from one FRR + to other + + Parameters: + ----------- + * `tgen` : Topogen object + * `topo` : json file data + * `senderRouter` : Sender router + * `packet` : packet in raw format + * `interval` : Interval between the packets + * `count` : Number of packets to be sent + + returns: + -------- + errormsg or True + """ + + global CD + result = "" + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + sender_interface = intf + rnode = tgen.routers()[senderRouter] + + for destLink, data in topo["routers"][senderRouter]["links"].items(): + if "type" in data and data["type"] == "loopback": + continue + + if not packet: + packet = topo["routers"][senderRouter]["pkt"]["test_packets"][packet][ + "data" + ] + + if interval > 1 or count > 1: + cmd = ( + "nohup /usr/bin/python {}/send_bsr_packet.py '{}' '{}' " + "--interval={} --count={} &".format( + CD, packet, sender_interface, interval, count + ) + ) + else: + cmd = ( + "/usr/bin/python {}/send_bsr_packet.py '{}' '{}' " + "--interval={} --count={}".format( + CD, packet, sender_interface, interval, count + ) + ) + + logger.info("Scapy cmd: \n %s", cmd) + result = rnode.run(cmd) + + if result == "": + return result + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True diff --git a/tests/topotests/lib/ospf.py b/tests/topotests/lib/ospf.py index 40da7c8fbe..beac768905 100644 --- a/tests/topotests/lib/ospf.py +++ b/tests/topotests/lib/ospf.py @@ -18,7 +18,6 @@ # OF THIS SOFTWARE. # -import traceback import ipaddr import ipaddress import sys @@ -28,10 +27,11 @@ from time import sleep from lib.topolog import logger from lib.topotest import frr_unicode from ipaddress import IPv6Address +import sys # Import common_config to use commomnly used APIs from lib.common_config import ( - create_common_configuration, + create_common_configurations, InvalidCLIError, retry, generate_ips, @@ -85,32 +85,36 @@ def create_router_ospf(tgen, topo, input_dict=None, build=False, load_config=Tru topo = topo["routers"] input_dict = deepcopy(input_dict) - for router in input_dict.keys(): - if "ospf" not in input_dict[router]: - logger.debug("Router %s: 'ospf' not present in input_dict", router) - continue + for ospf in ["ospf", "ospf6"]: + config_data_dict = {} - result = __create_ospf_global(tgen, input_dict, router, build, load_config) - if result is True: - ospf_data = input_dict[router]["ospf"] - - for router in input_dict.keys(): - if "ospf6" not in input_dict[router]: - logger.debug("Router %s: 'ospf6' not present in input_dict", router) - continue + for router in input_dict.keys(): + if ospf not in input_dict[router]: + logger.debug("Router %s: %s not present in input_dict", router, ospf) + continue - result = __create_ospf_global( - tgen, input_dict, router, build, load_config, ospf="ospf6" - ) - if result is True: - ospf_data = input_dict[router]["ospf6"] + config_data = __create_ospf_global( + tgen, input_dict, router, build, load_config, ospf + ) + if config_data: + if router not in config_data_dict: + config_data_dict[router] = config_data + else: + config_data_dict[router].extend(config_data) + try: + result = create_common_configurations( + tgen, config_data_dict, ospf, build, load_config + ) + except InvalidCLIError: + logger.error("create_router_ospf (ipv4)", exc_info=True) + result = False logger.debug("Exiting lib API: create_router_ospf()") return result def __create_ospf_global( - tgen, input_dict, router, build=False, load_config=True, ospf="ospf" + tgen, input_dict, router, build, load_config, ospf ): """ Helper API to create ospf global configuration. @@ -132,12 +136,12 @@ def __create_ospf_global( "links": { "r3": { "ipv6": "2013:13::1/64", - "ospf6": { + "ospf6": { "hello_interval": 1, "dead_interval": 4, "network": "point-to-point" } - } + } }, "ospf6": { "router_id": "1.1.1.1", @@ -152,190 +156,221 @@ def __create_ospf_global( Returns ------- - True or False + list of configuration commands """ - result = False - logger.debug("Entering lib API: __create_ospf_global()") - try: + config_data = [] - ospf_data = input_dict[router][ospf] - del_ospf_action = ospf_data.setdefault("delete", False) - if del_ospf_action: - config_data = ["no router {}".format(ospf)] - result = create_common_configuration( - tgen, router, config_data, ospf, build, load_config - ) - return result + if ospf not in input_dict[router]: + return config_data - config_data = [] - cmd = "router {}".format(ospf) + logger.debug("Entering lib API: __create_ospf_global()") + ospf_data = input_dict[router][ospf] + del_ospf_action = ospf_data.setdefault("delete", False) + if del_ospf_action: + config_data = ["no router {}".format(ospf)] + return config_data + + cmd = "router {}".format(ospf) + + config_data.append(cmd) + + # router id + router_id = ospf_data.setdefault("router_id", None) + del_router_id = ospf_data.setdefault("del_router_id", False) + if del_router_id: + config_data.append("no {} router-id".format(ospf)) + if router_id: + config_data.append("{} router-id {}".format(ospf, router_id)) + + # log-adjacency-changes + log_adj_changes = ospf_data.setdefault("log_adj_changes", None) + del_log_adj_changes = ospf_data.setdefault("del_log_adj_changes", False) + if del_log_adj_changes: + config_data.append("no log-adjacency-changes detail") + if log_adj_changes: + config_data.append("log-adjacency-changes {}".format( + log_adj_changes)) + + # aggregation timer + aggr_timer = ospf_data.setdefault("aggr_timer", None) + del_aggr_timer = ospf_data.setdefault("del_aggr_timer", False) + if del_aggr_timer: + config_data.append("no aggregation timer") + if aggr_timer: + config_data.append("aggregation timer {}".format( + aggr_timer)) + + # maximum path information + ecmp_data = ospf_data.setdefault("maximum-paths", {}) + if ecmp_data: + cmd = "maximum-paths {}".format(ecmp_data) + del_action = ospf_data.setdefault("del_max_path", False) + if del_action: + cmd = "no maximum-paths" config_data.append(cmd) - # router id - router_id = ospf_data.setdefault("router_id", None) - del_router_id = ospf_data.setdefault("del_router_id", False) - if del_router_id: - config_data.append("no {} router-id".format(ospf)) - if router_id: - config_data.append("{} router-id {}".format(ospf, router_id)) - - # log-adjacency-changes - log_adj_changes = ospf_data.setdefault("log_adj_changes", None) - del_log_adj_changes = ospf_data.setdefault("del_log_adj_changes", False) - if del_log_adj_changes: - config_data.append("no log-adjacency-changes detail") - if log_adj_changes: - config_data.append("log-adjacency-changes {}".format(log_adj_changes)) - - # aggregation timer - aggr_timer = ospf_data.setdefault("aggr_timer", None) - del_aggr_timer = ospf_data.setdefault("del_aggr_timer", False) - if del_aggr_timer: - config_data.append("no aggregation timer") - if aggr_timer: - config_data.append("aggregation timer {}".format(aggr_timer)) - - # maximum path information - ecmp_data = ospf_data.setdefault("maximum-paths", {}) - if ecmp_data: - cmd = "maximum-paths {}".format(ecmp_data) - del_action = ospf_data.setdefault("del_max_path", False) + # redistribute command + redistribute_data = ospf_data.setdefault("redistribute", {}) + if redistribute_data: + for redistribute in redistribute_data: + if "redist_type" not in redistribute: + logger.debug( + "Router %s: 'redist_type' not present in " "input_dict", router + ) + else: + cmd = "redistribute {}".format(redistribute["redist_type"]) + for red_type in redistribute_data: + if "route_map" in red_type: + cmd = cmd + " route-map {}".format(red_type["route_map"]) + del_action = redistribute.setdefault("delete", False) + if del_action: + cmd = "no {}".format(cmd) + config_data.append(cmd) + + # area information + area_data = ospf_data.setdefault("area", {}) + if area_data: + for area in area_data: + if "id" not in area: + logger.debug( + "Router %s: 'area id' not present in " "input_dict", router + ) + else: + cmd = "area {}".format(area["id"]) + + if "type" in area: + cmd = cmd + " {}".format(area["type"]) + + del_action = area.setdefault("delete", False) + if del_action: + cmd = "no {}".format(cmd) + config_data.append(cmd) + + #def route information + def_rte_data = ospf_data.setdefault("default-information", {}) + if def_rte_data: + if "originate" not in def_rte_data: + logger.debug("Router %s: 'originate key' not present in " + "input_dict", router) + else: + cmd = "default-information originate" + + if "always" in def_rte_data: + cmd = cmd + " always" + + if "metric" in def_rte_data: + cmd = cmd + " metric {}".format(def_rte_data["metric"]) + + if "metric-type" in def_rte_data: + cmd = cmd + " metric-type {}".format(def_rte_data[ + "metric-type"]) + + if "route-map" in def_rte_data: + cmd = cmd + " route-map {}".format(def_rte_data["route-map"]) + + del_action = def_rte_data.setdefault("delete", False) if del_action: - cmd = "no maximum-paths" + cmd = "no {}".format(cmd) config_data.append(cmd) - # redistribute command - redistribute_data = ospf_data.setdefault("redistribute", {}) - if redistribute_data: - for redistribute in redistribute_data: - if "redist_type" not in redistribute: - logger.debug( - "Router %s: 'redist_type' not present in " "input_dict", router + # area interface information for ospf6d only + if ospf == "ospf6": + area_iface = ospf_data.setdefault("neighbors", {}) + if area_iface: + for neighbor in area_iface: + if "area" in area_iface[neighbor]: + iface = input_dict[router]["links"][neighbor]["interface"] + cmd = "interface {} area {}".format( + iface, area_iface[neighbor]["area"] ) - else: - cmd = "redistribute {}".format(redistribute["redist_type"]) - for red_type in redistribute_data: - if "route_map" in red_type: - cmd = cmd + " route-map {}".format(red_type["route_map"]) - del_action = redistribute.setdefault("delete", False) - if del_action: + if area_iface[neighbor].setdefault("delete", False): cmd = "no {}".format(cmd) config_data.append(cmd) - # area information - area_data = ospf_data.setdefault("area", {}) - if area_data: - for area in area_data: - if "id" not in area: - logger.debug( - "Router %s: 'area id' not present in " "input_dict", router - ) - else: - cmd = "area {}".format(area["id"]) + try: + if "area" in input_dict[router]['links'][neighbor][ + 'ospf6']: + iface = input_dict[router]["links"][neighbor]["interface"] + cmd = "interface {} area {}".format( + iface, input_dict[router]['links'][neighbor][ + 'ospf6']['area']) + if input_dict[router]['links'][neighbor].setdefault( + "delete", False): + cmd = "no {}".format(cmd) + config_data.append(cmd) + except KeyError: + pass - if "type" in area: - cmd = cmd + " {}".format(area["type"]) - del_action = area.setdefault("delete", False) - if del_action: - cmd = "no {}".format(cmd) - config_data.append(cmd) - - # def route information - def_rte_data = ospf_data.setdefault("default-information", {}) - if def_rte_data: - if "originate" not in def_rte_data: + # summary information + summary_data = ospf_data.setdefault("summary-address", {}) + if summary_data: + for summary in summary_data: + if "prefix" not in summary: logger.debug( - "Router %s: 'originate key' not present in " "input_dict", router + "Router %s: 'summary-address' not present in " "input_dict", + router, ) else: - cmd = "default-information originate" - - if "always" in def_rte_data: - cmd = cmd + " always" - - if "metric" in def_rte_data: - cmd = cmd + " metric {}".format(def_rte_data["metric"]) + cmd = "summary {}/{}".format(summary["prefix"], summary["mask"]) - if "metric-type" in def_rte_data: - cmd = cmd + " metric-type {}".format(def_rte_data["metric-type"]) + _tag = summary.setdefault("tag", None) + if _tag: + cmd = "{} tag {}".format(cmd, _tag) - if "route-map" in def_rte_data: - cmd = cmd + " route-map {}".format(def_rte_data["route-map"]) + _advertise = summary.setdefault("advertise", True) + if not _advertise: + cmd = "{} no-advertise".format(cmd) - del_action = def_rte_data.setdefault("delete", False) + del_action = summary.setdefault("delete", False) if del_action: cmd = "no {}".format(cmd) config_data.append(cmd) - # area interface information for ospf6d only - if ospf == "ospf6": - area_iface = ospf_data.setdefault("neighbors", {}) - if area_iface: - for neighbor in area_iface: - if "area" in area_iface[neighbor]: - iface = input_dict[router]["links"][neighbor]["interface"] - cmd = "interface {} area {}".format( - iface, area_iface[neighbor]["area"] - ) - if area_iface[neighbor].setdefault("delete", False): - cmd = "no {}".format(cmd) - config_data.append(cmd) - - try: - if "area" in input_dict[router]["links"][neighbor]["ospf6"]: - iface = input_dict[router]["links"][neighbor]["interface"] - cmd = "interface {} area {}".format( - iface, - input_dict[router]["links"][neighbor]["ospf6"]["area"], - ) - if input_dict[router]["links"][neighbor].setdefault( - "delete", False - ): - cmd = "no {}".format(cmd) - config_data.append(cmd) - except KeyError: - pass - - # summary information - summary_data = ospf_data.setdefault("summary-address", {}) - if summary_data: - for summary in summary_data: - if "prefix" not in summary: - logger.debug( - "Router %s: 'summary-address' not present in " "input_dict", - router, - ) - else: - cmd = "summary {}/{}".format(summary["prefix"], summary["mask"]) + # ospf gr information + gr_data = ospf_data.setdefault("graceful-restart", {}) + if gr_data: - _tag = summary.setdefault("tag", None) - if _tag: - cmd = "{} tag {}".format(cmd, _tag) - - _advertise = summary.setdefault("advertise", True) - if not _advertise: - cmd = "{} no-advertise".format(cmd) + if "opaque" in gr_data and gr_data["opaque"]: + cmd = "capability opaque" + if gr_data.setdefault("delete", False): + cmd = "no {}".format(cmd) + config_data.append(cmd) - del_action = summary.setdefault("delete", False) - if del_action: - cmd = "no {}".format(cmd) - config_data.append(cmd) + if "helper-only" in gr_data and not gr_data["helper-only"]: + cmd = "graceful-restart helper-only" + if gr_data.setdefault("delete", False): + cmd = "no {}".format(cmd) + config_data.append(cmd) + elif "helper-only" in gr_data and type(gr_data["helper-only"]) is list: + for rtrs in gr_data["helper-only"]: + cmd = "graceful-restart helper-only {}".format(rtrs) + if gr_data.setdefault("delete", False): + cmd = "no {}".format(cmd) + config_data.append(cmd) - result = create_common_configuration( - tgen, router, config_data, ospf, build, load_config - ) + if "helper" in gr_data: + if type(gr_data["helper"]) is not list: + gr_data["helper"] = list(gr_data["helper"]) + for helper_role in gr_data["helper"]: + cmd = "graceful-restart helper {}".format(helper_role) + if gr_data.setdefault("delete", False): + cmd = "no {}".format(cmd) + config_data.append(cmd) - except InvalidCLIError: - # Traceback - errormsg = traceback.format_exc() - logger.error(errormsg) - return errormsg + if "supported-grace-time" in gr_data: + cmd = "graceful-restart helper supported-grace-time {}".format( + gr_data["supported-grace-time"] + ) + if gr_data.setdefault("delete", False): + cmd = "no {}".format(cmd) + config_data.append(cmd) logger.debug("Exiting lib API: create_ospf_global()") - return result + + return config_data def create_router_ospf6(tgen, topo, input_dict=None, build=False, load_config=True): @@ -370,14 +405,27 @@ def create_router_ospf6(tgen, topo, input_dict=None, build=False, load_config=Tr else: topo = topo["routers"] input_dict = deepcopy(input_dict) + + config_data_dict = {} + for router in input_dict.keys(): if "ospf6" not in input_dict[router]: logger.debug("Router %s: 'ospf6' not present in input_dict", router) continue - result = __create_ospf_global( + config_data = __create_ospf_global( tgen, input_dict, router, build, load_config, "ospf6" ) + if config_data: + config_data_dict[router] = config_data + + try: + result = create_common_configurations( + tgen, config_data_dict, "ospf6", build, load_config + ) + except InvalidCLIError: + logger.error("create_router_ospf6", exc_info=True) + result = False logger.debug("Exiting lib API: create_router_ospf6()") return result @@ -422,6 +470,9 @@ def config_ospf_interface(tgen, topo, input_dict=None, build=False, load_config= input_dict = deepcopy(topo) else: input_dict = deepcopy(input_dict) + + config_data_dict = {} + for router in input_dict.keys(): config_data = [] for lnk in input_dict[router]["links"].keys(): @@ -506,10 +557,14 @@ def config_ospf_interface(tgen, topo, input_dict=None, build=False, load_config= if build: return config_data - else: - result = create_common_configuration( - tgen, router, config_data, "interface_config", build=build - ) + + if config_data: + config_data_dict[router] = config_data + + result = create_common_configurations( + tgen, config_data_dict, "interface_config", build=build + ) + logger.debug("Exiting lib API: config_ospf_interface()") return result @@ -2299,6 +2354,9 @@ def config_ospf6_interface(tgen, topo, input_dict=None, build=False, load_config input_dict = deepcopy(topo) else: input_dict = deepcopy(input_dict) + + config_data_dict = {} + for router in input_dict.keys(): config_data = [] for lnk in input_dict[router]['links'].keys(): @@ -2369,9 +2427,76 @@ def config_ospf6_interface(tgen, topo, input_dict=None, build=False, load_config if build: return config_data + + if config_data: + config_data_dict[router] = config_data + + result = create_common_configurations( + tgen, config_data_dict, "interface_config", build=build + ) + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return result + +@retry(retry_timeout=20) +def verify_ospf_gr_helper(tgen, topo, dut, input_dict=None): + """ + This API is used to vreify gr helper using command + show ip ospf graceful-restart helper + + Parameters + ---------- + * `tgen` : Topogen object + * `topo` : topology descriptions + * 'dut' : router + * 'input_dict' - values to be verified + + Usage: + ------- + input_dict = { + "helperSupport":"Disabled", + "strictLsaCheck":"Enabled", + "restartSupoort":"Planned and Unplanned Restarts", + "supportedGracePeriod":1800 + } + result = verify_ospf_gr_helper(tgen, topo, dut, input_dict) + + """ + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + result = False + + if 'ospf' not in topo['routers'][dut]: + errormsg = "[DUT: {}] OSPF is not configured on the router.".format( + dut) + return errormsg + + rnode = tgen.routers()[dut] + logger.info("Verifying OSPF GR details on router %s:", dut) + show_ospf_json = run_frr_cmd(rnode, "show ip ospf graceful-restart helper json", + isjson=True) + + # Verifying output dictionary show_ospf_json is empty or not + if not bool(show_ospf_json): + errormsg = "OSPF is not running" + raise ValueError (errormsg) + return errormsg + + for ospf_gr, gr_data in input_dict.items(): + try: + if input_dict[ospf_gr] == show_ospf_json[ospf_gr]: + logger.info("[DUT: FRR] OSPF GR Helper: %s is %s", ospf_gr, + show_ospf_json[ospf_gr]) + result = True else: - result = create_common_configuration( - tgen, router, config_data, "interface_config", build=build - ) + errormsg = ("[DUT: FRR] OSPF GR Helper: {} expected is {}, Found " + "is {}".format(ospf_gr, input_dict[ospf_gr], show_ospf_json[ + ospf_gr])) + raise ValueError (errormsg) + return errormsg + + except KeyError: + errormsg = ("[DUT: FRR] OSPF GR Helper: {}".format(ospf_gr)) + return errormsg + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) return result diff --git a/tests/topotests/lib/pim.py b/tests/topotests/lib/pim.py index 7de1c7a2f9..e702e53c00 100644 --- a/tests/topotests/lib/pim.py +++ b/tests/topotests/lib/pim.py @@ -29,6 +29,7 @@ from lib.topolog import logger # Import common_config to use commomnly used APIs from lib.common_config import ( create_common_configuration, + create_common_configurations, InvalidCLIError, retry, run_frr_cmd, @@ -55,7 +56,7 @@ def create_pim_config(tgen, topo, input_dict=None, build=False, load_config=True input_dict = { "r1": { "pim": { - "disable" : ["l1-i1-eth1"], + "join-prune-interval": "5", "rp": [{ "rp_addr" : "1.0.3.17". "keep-alive-timer": "100" @@ -79,30 +80,40 @@ def create_pim_config(tgen, topo, input_dict=None, build=False, load_config=True else: topo = topo["routers"] input_dict = deepcopy(input_dict) + + config_data_dict = {} + for router in input_dict.keys(): - result = _enable_disable_pim(tgen, topo, input_dict, router, build) + config_data = _enable_disable_pim_config(tgen, topo, input_dict, router, build) + if config_data: + config_data_dict[router] = config_data + + # Now add RP config to all routers + for router in input_dict.keys(): if "pim" not in input_dict[router]: - logger.debug("Router %s: 'pim' is not present in " "input_dict", router) continue + if "rp" not in input_dict[router]["pim"]: + continue + _add_pim_rp_config( + tgen, topo, input_dict, router, build, config_data_dict + ) - if result is True: - if "rp" not in input_dict[router]["pim"]: - continue - - result = _create_pim_config( - tgen, topo, input_dict, router, build, load_config - ) - if result is not True: - return False + try: + result = create_common_configurations( + tgen, config_data_dict, "pim", build, load_config + ) + except InvalidCLIError: + logger.error("create_pim_config", exc_info=True) + result = False logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) return result -def _create_pim_config(tgen, topo, input_dict, router, build=False, load_config=False): +def _add_pim_rp_config(tgen, topo, input_dict, router, build, config_data_dict): """ - Helper API to create pim configuration. + Helper API to create pim RP configurations. Parameters ---------- @@ -111,107 +122,91 @@ def _create_pim_config(tgen, topo, input_dict, router, build=False, load_config= * `input_dict` : Input dict data, required when configuring from testcase * `router` : router id to be configured. * `build` : Only for initial setup phase this is set as True. - + * `config_data_dict` : OUT: adds `router` config to dictinary Returns ------- - True or False + None """ - result = False logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) - try: - pim_data = input_dict[router]["pim"] + pim_data = input_dict[router]["pim"] + rp_data = pim_data["rp"] - for dut in tgen.routers(): - if "pim" not in input_dict[router]: - continue + # Configure this RP on every router. + for dut in tgen.routers(): + # At least one interface must be enabled for PIM on the router + pim_if_enabled = False + for destLink, data in topo[dut]["links"].items(): + if "pim" in data: + pim_if_enabled = True + if not pim_if_enabled: + continue - for destLink, data in topo[dut]["links"].items(): - if "pim" not in data: - continue + config_data = [] - if "rp" in pim_data: - config_data = [] - rp_data = pim_data["rp"] + for rp_dict in deepcopy(rp_data): + # ip address of RP + if "rp_addr" not in rp_dict and build: + logger.error( + "Router %s: 'ip address of RP' not " + "present in input_dict/JSON", + router, + ) - for rp_dict in deepcopy(rp_data): - # ip address of RP - if "rp_addr" not in rp_dict and build: - logger.error( - "Router %s: 'ip address of RP' not " - "present in input_dict/JSON", - router, - ) + return False + rp_addr = rp_dict.setdefault("rp_addr", None) - return False - rp_addr = rp_dict.setdefault("rp_addr", None) + # Keep alive Timer + keep_alive_timer = rp_dict.setdefault("keep_alive_timer", None) - # Keep alive Timer - keep_alive_timer = rp_dict.setdefault("keep_alive_timer", None) + # Group Address range to cover + if "group_addr_range" not in rp_dict and build: + logger.error( + "Router %s:'Group Address range to cover'" + " not present in input_dict/JSON", + router, + ) - # Group Address range to cover - if "group_addr_range" not in rp_dict and build: - logger.error( - "Router %s:'Group Address range to cover'" - " not present in input_dict/JSON", - router, - ) + return False + group_addr_range = rp_dict.setdefault("group_addr_range", None) - return False - group_addr_range = rp_dict.setdefault("group_addr_range", None) + # Group prefix-list filter + prefix_list = rp_dict.setdefault("prefix_list", None) - # Group prefix-list filter - prefix_list = rp_dict.setdefault("prefix_list", None) + # Delete rp config + del_action = rp_dict.setdefault("delete", False) - # Delete rp config - del_action = rp_dict.setdefault("delete", False) + if keep_alive_timer: + cmd = "ip pim rp keep-alive-timer {}".format(keep_alive_timer) + if del_action: + cmd = "no {}".format(cmd) + config_data.append(cmd) - if keep_alive_timer: - cmd = "ip pim rp keep-alive-timer {}".format(keep_alive_timer) - config_data.append(cmd) + if rp_addr: + if group_addr_range: + if type(group_addr_range) is not list: + group_addr_range = [group_addr_range] + for grp_addr in group_addr_range: + cmd = "ip pim rp {} {}".format(rp_addr, grp_addr) if del_action: cmd = "no {}".format(cmd) - config_data.append(cmd) - - if rp_addr: - if group_addr_range: - if type(group_addr_range) is not list: - group_addr_range = [group_addr_range] - - for grp_addr in group_addr_range: - cmd = "ip pim rp {} {}".format(rp_addr, grp_addr) - config_data.append(cmd) - - if del_action: - cmd = "no {}".format(cmd) - config_data.append(cmd) - - if prefix_list: - cmd = "ip pim rp {} prefix-list {}".format( - rp_addr, prefix_list - ) - config_data.append(cmd) - - if del_action: - cmd = "no {}".format(cmd) - config_data.append(cmd) - - result = create_common_configuration( - tgen, dut, config_data, "pim", build, load_config - ) - if result is not True: - return False + config_data.append(cmd) - except InvalidCLIError: - # Traceback - errormsg = traceback.format_exc() - logger.error(errormsg) - return errormsg + if prefix_list: + cmd = "ip pim rp {} prefix-list {}".format( + rp_addr, prefix_list + ) + if del_action: + cmd = "no {}".format(cmd) + config_data.append(cmd) - logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) - return result + if config_data: + if dut not in config_data_dict: + config_data_dict[dut] = config_data + else: + config_data_dict[dut].extend(config_data) def create_igmp_config(tgen, topo, input_dict=None, build=False): @@ -258,6 +253,9 @@ def create_igmp_config(tgen, topo, input_dict=None, build=False): else: topo = topo["routers"] input_dict = deepcopy(input_dict) + + config_data_dict = {} + for router in input_dict.keys(): if "igmp" not in input_dict[router]: logger.debug("Router %s: 'igmp' is not present in " "input_dict", router) @@ -303,21 +301,22 @@ def create_igmp_config(tgen, topo, input_dict=None, build=False): cmd = "no {}".format(cmd) config_data.append(cmd) - try: + if config_data: + config_data_dict[router] = config_data - result = create_common_configuration( - tgen, router, config_data, "interface_config", build=build - ) - except InvalidCLIError: - errormsg = traceback.format_exc() - logger.error(errormsg) - return errormsg + try: + result = create_common_configurations( + tgen, config_data_dict, "interface_config", build=build + ) + except InvalidCLIError: + logger.error("create_igmp_config", exc_info=True) + result = False logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) return result -def _enable_disable_pim(tgen, topo, input_dict, router, build=False): +def _enable_disable_pim_config(tgen, topo, input_dict, router, build=False): """ Helper API to enable or disable pim on interfaces @@ -331,57 +330,40 @@ def _enable_disable_pim(tgen, topo, input_dict, router, build=False): Returns ------- - True or False + list of config """ - result = False - logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) - try: - config_data = [] - - enable_flag = True - # Disable pim on interface - if "pim" in input_dict[router]: - if "disable" in input_dict[router]["pim"]: - enable_flag = False - interfaces = input_dict[router]["pim"]["disable"] - if type(interfaces) is not list: - interfaces = [interfaces] - - for interface in interfaces: - cmd = "interface {}".format(interface) - config_data.append(cmd) - config_data.append("no ip pim") - - # Enable pim on interface - if enable_flag: - for destRouterLink, data in sorted(topo[router]["links"].items()): - if "pim" in data and data["pim"] == "enable": - - # Loopback interfaces - if "type" in data and data["type"] == "loopback": - interface_name = destRouterLink - else: - interface_name = data["interface"] + config_data = [] - cmd = "interface {}".format(interface_name) - config_data.append(cmd) - config_data.append("ip pim") + # Enable pim on interfaces + for destRouterLink, data in sorted(topo[router]["links"].items()): + if "pim" in data and data["pim"] == "enable": + # Loopback interfaces + if "type" in data and data["type"] == "loopback": + interface_name = destRouterLink + else: + interface_name = data["interface"] - result = create_common_configuration( - tgen, router, config_data, "interface_config", build=build - ) - if result is not True: - return False + cmd = "interface {}".format(interface_name) + config_data.append(cmd) + config_data.append("ip pim") - except InvalidCLIError: - # Traceback - errormsg = traceback.format_exc() - logger.error(errormsg) - return errormsg + # pim global config + if "pim" in input_dict[router]: + pim_data = input_dict[router]["pim"] + del_action = pim_data.setdefault("delete", False) + for t in [ + "join-prune-interval", + "keep-alive-timer", + "register-suppress-time", + ]: + if t in pim_data: + cmd = "ip pim {} {}".format(t, pim_data[t]) + if del_action: + cmd = "no {}".format(cmd) + config_data.append(cmd) - logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) - return result + return config_data def find_rp_details(tgen, topo): @@ -454,7 +436,9 @@ def configure_pim_force_expire(tgen, topo, input_dict, build=False): result = False logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + try: + config_data_dict = {} for dut in input_dict.keys(): if "pim" not in input_dict[dut]: @@ -462,8 +446,8 @@ def configure_pim_force_expire(tgen, topo, input_dict, build=False): pim_data = input_dict[dut]["pim"] + config_data = [] if "force_expire" in pim_data: - config_data = [] force_expire_data = pim_data["force_expire"] for source, groups in force_expire_data.items(): @@ -476,17 +460,15 @@ def configure_pim_force_expire(tgen, topo, input_dict, build=False): ) config_data.append(cmd) - result = create_common_configuration( - tgen, dut, config_data, "pim", build=build - ) - if result is not True: - return False + if config_data: + config_data_dict[dut] = config_data + result = create_common_configurations( + tgen, config_data_dict, "pim", build=build + ) except InvalidCLIError: - # Traceback - errormsg = traceback.format_exc() - logger.error(errormsg) - return errormsg + logger.error("configure_pim_force_expire", exc_info=True) + result = False logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) return result @@ -966,7 +948,7 @@ def verify_join_state_and_timer(tgen, dut, iif, src_address, group_addresses, ex return True -@retry(retry_timeout=80) +@retry(retry_timeout=120) def verify_ip_mroutes( tgen, dut, src_address, group_addresses, iif, oil, return_uptime=False, mwait=0, expected=True ): @@ -2029,6 +2011,7 @@ def add_rp_interfaces_and_pim_config(tgen, topo, interface, rp, rp_mapping): config_data.append("ip address {}".format(_rp)) config_data.append("ip pim") + # Why not config just once, why per group? result = create_common_configuration( tgen, rp, config_data, "interface_config" ) diff --git a/tests/topotests/lib/topojson.py b/tests/topotests/lib/topojson.py index 1ae482a265..003a971373 100644 --- a/tests/topotests/lib/topojson.py +++ b/tests/topotests/lib/topojson.py @@ -34,7 +34,7 @@ from lib.topolog import logger from lib.common_config import ( number_to_row, number_to_column, - load_config_to_router, + load_config_to_routers, create_interfaces_cfg, create_static_routes, create_prefix_lists, @@ -342,10 +342,8 @@ def build_config_from_json(tgen, topo, save_bkup=True): func_dict.get(func_type)(tgen, data, build=True) - for router in sorted(topo["routers"].keys()): - logger.debug("Configuring router {}...".format(router)) - - result = load_config_to_router(tgen, router, save_bkup) - if not result: - logger.info("Failed while configuring {}".format(router)) - pytest.exit(1) + routers = sorted(topo["routers"].keys()) + result = load_config_to_routers(tgen, routers, save_bkup) + if not result: + logger.info("build_config_from_json: failed to configure topology") + pytest.exit(1) diff --git a/tests/topotests/multicast_pim_bsm_topo2/test_mcast_pim_bsmp_02.py b/tests/topotests/multicast_pim_bsm_topo2/test_mcast_pim_bsmp_02.py index 98af4433ab..894326f19f 100644 --- a/tests/topotests/multicast_pim_bsm_topo2/test_mcast_pim_bsmp_02.py +++ b/tests/topotests/multicast_pim_bsm_topo2/test_mcast_pim_bsmp_02.py @@ -1008,8 +1008,8 @@ def test_BSM_fragmentation_p1(request): # set mtu of fhr(f1) to i1 interface to 100 so that bsm fragments step("set mtu of fhr(f1) to i1 interface to 100 so that bsm fragments") - fhr_node.run("ifconfig f1-i1-eth2 mtu 100") - inter_node.run("ifconfig i1-f1-eth0 mtu 100") + fhr_node.run("ip link set f1-i1-eth2 mtu 100") + inter_node.run("ip link set i1-f1-eth0 mtu 100") # Use scapy to send pre-defined packet from senser to receiver result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet2") diff --git a/tests/topotests/multicast_pim_sm_topo1/multicast_pim_sm_topo1.json b/tests/topotests/multicast_pim_sm_topo1/multicast_pim_sm_topo1.json index 71454c2ab2..cc20abbe6a 100644 --- a/tests/topotests/multicast_pim_sm_topo1/multicast_pim_sm_topo1.json +++ b/tests/topotests/multicast_pim_sm_topo1/multicast_pim_sm_topo1.json @@ -13,10 +13,12 @@ "r2": {"ipv4": "auto", "pim": "enable"}, "c1": {"ipv4": "auto", "pim": "enable"} }, + "pim": { "join-prune-interval": "5", "keep-alive-timer": 15, "register-suppress-time": 12 }, "igmp": { "interfaces": { "l1-i1-eth1" :{ "igmp":{ + "query": {"query-max-response-time": 40, "query-interval": 5}, "version": "2" } } @@ -38,6 +40,7 @@ "f1": {"ipv4": "auto", "pim": "enable"}, "i3": {"ipv4": "auto", "pim": "enable"} }, + "pim": { "join-prune-interval": "5", "keep-alive-timer": 15, "register-suppress-time": 12 }, "static_routes": [{ "network": ["10.0.5.0/24", "10.0.6.0/24", "1.0.2.2/32", "10.0.1.0/24"], "next_hop": "10.0.7.1" @@ -55,6 +58,7 @@ "i2": {"ipv4": "auto", "pim": "enable"}, "i8": {"ipv4": "auto", "pim": "enable"} }, + "pim": { "join-prune-interval": "5", "keep-alive-timer": 15, "register-suppress-time": 12 }, "static_routes": [{ "network": ["1.0.5.17/32", "10.0.8.0/24", "10.0.9.0/24", "10.0.10.0/24", "10.0.12.0/24", "10.0.11.0/24"], "next_hop": "10.0.7.2" @@ -71,6 +75,7 @@ "l1": {"ipv4": "auto", "pim": "enable"}, "i4": {"ipv4": "auto", "pim": "enable"} }, + "pim": { "join-prune-interval": "5", "keep-alive-timer": 15, "register-suppress-time": 12 }, "static_routes": [{ "network": ["1.0.5.17/32", "10.0.6.0/24", "10.0.3.0/24", "10.0.8.0/24", "10.0.9.0/24", "10.0.12.0/24", "10.0.10.0/24", "10.0.11.0/24"], "next_hop": "10.0.2.2" @@ -87,6 +92,7 @@ "f1": {"ipv4": "auto", "pim": "enable"}, "i5": {"ipv4": "auto", "pim": "enable"} }, + "pim": { "join-prune-interval": "5", "keep-alive-timer": 15, "register-suppress-time": 12 }, "static_routes": [{ "network": ["1.0.5.17/32", "10.0.5.0/24", "10.0.6.0/24", "10.0.7.0/24", "10.0.8.0/24", "10.0.9.0/24", "10.0.7.0/24", "10.0.10.0/24", "10.0.11.0/24"], "next_hop": "10.0.3.2" diff --git a/tests/topotests/multicast_pim_sm_topo1/test_multicast_pim_sm_topo1.py b/tests/topotests/multicast_pim_sm_topo1/test_multicast_pim_sm_topo1.py index 99a6e5bacf..36a3103c9d 100755 --- a/tests/topotests/multicast_pim_sm_topo1/test_multicast_pim_sm_topo1.py +++ b/tests/topotests/multicast_pim_sm_topo1/test_multicast_pim_sm_topo1.py @@ -78,6 +78,7 @@ from lib.common_config import ( step, iperfSendIGMPJoin, addKernelRoute, + apply_raw_config, reset_config_on_routers, iperfSendTraffic, kill_iperf, @@ -1553,8 +1554,10 @@ def test_modify_igmp_max_query_response_timer_p0(request): assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) step("Delete the PIM and IGMP on FRR1") - input_dict_1 = {"l1": {"pim": {"disable": ["l1-i1-eth1"]}}} - result = create_pim_config(tgen, topo, input_dict_1) + raw_config = { + "l1": {"raw_config": ["interface l1-i1-eth1", "no ip pim"]} + } + result = apply_raw_config(tgen, raw_config) assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) input_dict_2 = { diff --git a/tests/topotests/multicast_pim_sm_topo2/multicast_pim_sm_topo2.json b/tests/topotests/multicast_pim_sm_topo2/multicast_pim_sm_topo2.json index 71454c2ab2..cc20abbe6a 100644 --- a/tests/topotests/multicast_pim_sm_topo2/multicast_pim_sm_topo2.json +++ b/tests/topotests/multicast_pim_sm_topo2/multicast_pim_sm_topo2.json @@ -13,10 +13,12 @@ "r2": {"ipv4": "auto", "pim": "enable"}, "c1": {"ipv4": "auto", "pim": "enable"} }, + "pim": { "join-prune-interval": "5", "keep-alive-timer": 15, "register-suppress-time": 12 }, "igmp": { "interfaces": { "l1-i1-eth1" :{ "igmp":{ + "query": {"query-max-response-time": 40, "query-interval": 5}, "version": "2" } } @@ -38,6 +40,7 @@ "f1": {"ipv4": "auto", "pim": "enable"}, "i3": {"ipv4": "auto", "pim": "enable"} }, + "pim": { "join-prune-interval": "5", "keep-alive-timer": 15, "register-suppress-time": 12 }, "static_routes": [{ "network": ["10.0.5.0/24", "10.0.6.0/24", "1.0.2.2/32", "10.0.1.0/24"], "next_hop": "10.0.7.1" @@ -55,6 +58,7 @@ "i2": {"ipv4": "auto", "pim": "enable"}, "i8": {"ipv4": "auto", "pim": "enable"} }, + "pim": { "join-prune-interval": "5", "keep-alive-timer": 15, "register-suppress-time": 12 }, "static_routes": [{ "network": ["1.0.5.17/32", "10.0.8.0/24", "10.0.9.0/24", "10.0.10.0/24", "10.0.12.0/24", "10.0.11.0/24"], "next_hop": "10.0.7.2" @@ -71,6 +75,7 @@ "l1": {"ipv4": "auto", "pim": "enable"}, "i4": {"ipv4": "auto", "pim": "enable"} }, + "pim": { "join-prune-interval": "5", "keep-alive-timer": 15, "register-suppress-time": 12 }, "static_routes": [{ "network": ["1.0.5.17/32", "10.0.6.0/24", "10.0.3.0/24", "10.0.8.0/24", "10.0.9.0/24", "10.0.12.0/24", "10.0.10.0/24", "10.0.11.0/24"], "next_hop": "10.0.2.2" @@ -87,6 +92,7 @@ "f1": {"ipv4": "auto", "pim": "enable"}, "i5": {"ipv4": "auto", "pim": "enable"} }, + "pim": { "join-prune-interval": "5", "keep-alive-timer": 15, "register-suppress-time": 12 }, "static_routes": [{ "network": ["1.0.5.17/32", "10.0.5.0/24", "10.0.6.0/24", "10.0.7.0/24", "10.0.8.0/24", "10.0.9.0/24", "10.0.7.0/24", "10.0.10.0/24", "10.0.11.0/24"], "next_hop": "10.0.3.2" diff --git a/tests/topotests/multicast_pim_sm_topo3/multicast_pim_sm_topo3.json b/tests/topotests/multicast_pim_sm_topo3/multicast_pim_sm_topo3.json index f582f4929d..89c54a41d6 100644 --- a/tests/topotests/multicast_pim_sm_topo3/multicast_pim_sm_topo3.json +++ b/tests/topotests/multicast_pim_sm_topo3/multicast_pim_sm_topo3.json @@ -13,10 +13,12 @@ "r2": {"ipv4": "auto", "pim": "enable"}, "c1": {"ipv4": "auto", "pim": "enable"} }, + "pim": { "join-prune-interval": "5", "keep-alive-timer": 15, "register-suppress-time": 12 }, "igmp": { "interfaces": { "l1-i1-eth1" :{ "igmp":{ + "query": {"query-max-response-time": 40, "query-interval": 5}, "version": "2" } } @@ -38,6 +40,7 @@ "f1": {"ipv4": "auto", "pim": "enable"}, "i3": {"ipv4": "auto", "pim": "enable"} }, + "pim": { "join-prune-interval": "5", "keep-alive-timer": 15, "register-suppress-time": 12 }, "static_routes": [{ "network": ["10.0.5.0/24", "10.0.6.0/24", "1.0.2.2/32", "10.0.1.0/24", "1.0.3.5/32"], "next_hop": "10.0.7.1" @@ -55,6 +58,7 @@ "i2": {"ipv4": "auto", "pim": "enable"}, "i8": {"ipv4": "auto", "pim": "enable"} }, + "pim": { "join-prune-interval": "5", "keep-alive-timer": 15, "register-suppress-time": 12 }, "static_routes": [{ "network": ["1.0.5.17/32", "10.0.8.0/24", "10.0.9.0/24", "10.0.10.0/24", "10.0.11.0/24", "10.0.12.0/24"], "next_hop": "10.0.7.2" @@ -71,6 +75,7 @@ "l1": {"ipv4": "auto", "pim": "enable"}, "i4": {"ipv4": "auto", "pim": "enable"} }, + "pim": { "join-prune-interval": "5", "keep-alive-timer": 15, "register-suppress-time": 12 }, "static_routes": [{ "network": ["1.0.5.17/32", "10.0.6.0/24", "10.0.8.0/24", "10.0.9.0/24", "10.0.10.0/24", "10.0.11.0/24"], "next_hop": "10.0.2.2" @@ -87,6 +92,7 @@ "f1": {"ipv4": "auto", "pim": "enable"}, "i5": {"ipv4": "auto", "pim": "enable"} }, + "pim": { "join-prune-interval": "5", "keep-alive-timer": 15, "register-suppress-time": 12 }, "static_routes": [{ "network": ["1.0.5.17/32", "10.0.5.0/24", "10.0.6.0/24", "10.0.7.0/24", "10.0.8.0/24", "10.0.9.0/24", "10.0.10.0/24", "10.0.11.0/24"], "next_hop": "10.0.3.2" diff --git a/tests/topotests/multicast_pim_sm_topo3/multicast_pim_sm_topo4.json b/tests/topotests/multicast_pim_sm_topo3/multicast_pim_sm_topo4.json index 4635dac7d2..afb55994a7 100644 --- a/tests/topotests/multicast_pim_sm_topo3/multicast_pim_sm_topo4.json +++ b/tests/topotests/multicast_pim_sm_topo3/multicast_pim_sm_topo4.json @@ -13,10 +13,12 @@ "r2": {"ipv4": "auto", "pim": "enable"}, "c1": {"ipv4": "auto", "pim": "enable"} }, + "pim": { "join-prune-interval": "5", "keep-alive-timer": 15, "register-suppress-time": 12 }, "igmp": { "interfaces": { "l1-i1-eth1" :{ "igmp":{ + "query": {"query-max-response-time": 40, "query-interval": 5}, "version": "2" } } @@ -40,6 +42,7 @@ "f1": {"ipv4": "auto", "pim": "enable"}, "i3": {"ipv4": "auto", "pim": "enable"} }, + "pim": { "join-prune-interval": "5", "keep-alive-timer": 15, "register-suppress-time": 12 }, "static_routes": [{ "network": ["10.0.4.0/24","10.0.3.1/24"], "next_hop": "10.0.7.1" @@ -57,6 +60,7 @@ "i2": {"ipv4": "auto", "pim": "enable"}, "i8": {"ipv4": "auto", "pim": "enable"} }, + "pim": { "join-prune-interval": "5", "keep-alive-timer": 15, "register-suppress-time": 12 }, "static_routes": [{ "network": ["10.0.4.0/24","10.0.3.1/24"], "next_hop": "10.0.3.1" @@ -73,6 +77,7 @@ "l1": {"ipv4": "auto", "pim": "enable"}, "i4": {"ipv4": "auto", "pim": "enable"} }, + "pim": { "join-prune-interval": "5", "keep-alive-timer": 15, "register-suppress-time": 12 }, "static_routes": [{ "network": ["1.0.4.11/32","10.0.4.2/24", "10.0.3.1/24"], "next_hop": "10.0.2.2" @@ -87,6 +92,7 @@ "f1": {"ipv4": "auto", "pim": "enable"}, "i5": {"ipv4": "auto", "pim": "enable"} }, + "pim": { "join-prune-interval": "5", "keep-alive-timer": 15, "register-suppress-time": 12 }, "static_routes": [ { "network": ["1.0.4.11/32", "10.0.2.1/24", "10.0.1.2/24"], diff --git a/tests/topotests/multicast_pim_sm_topo3/test_multicast_pim_sm_topo3.py b/tests/topotests/multicast_pim_sm_topo3/test_multicast_pim_sm_topo3.py index 33f476de44..033c76081a 100755 --- a/tests/topotests/multicast_pim_sm_topo3/test_multicast_pim_sm_topo3.py +++ b/tests/topotests/multicast_pim_sm_topo3/test_multicast_pim_sm_topo3.py @@ -2014,7 +2014,7 @@ def test_verify_remove_add_igmp_commands_when_pim_configured_p0(request): intf_l1_i1 = topo["routers"]["l1"]["links"]["i1"]["interface"] input_dict_1 = { - "l1": {"igmp": {"interfaces": {intf_l1_i1: {"igmp": {"version": "2"}}}}} + "l1": {"igmp": {"interfaces": {intf_l1_i1: {"igmp": {"version": "2", "query": {"query-max-response-time": 40, "query-interval": 5}}}}}} } result = verify_igmp_config(tgen, input_dict_1) @@ -2231,8 +2231,10 @@ def test_verify_remove_add_pim_commands_when_igmp_configured_p1(request): step("Remove 'no ip pim' on receiver interface on FRR1") intf_l1_i1 = topo["routers"]["l1"]["links"]["i1"]["interface"] - input_dict_1 = {"l1": {"pim": {"disable": intf_l1_i1}}} - result = create_pim_config(tgen, topo, input_dict_1) + raw_config = { + "l1": {"raw_config": ["interface {}".format(intf_l1_i1), "no ip pim"]} + } + result = apply_raw_config(tgen, raw_config) assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) step("Verify that no core is observed") diff --git a/tests/topotests/multicast_pim_static_rp_topo1/multicast_pim_static_rp.json b/tests/topotests/multicast_pim_static_rp_topo1/multicast_pim_static_rp.json index 6d6c047b00..39c68408b4 100644 --- a/tests/topotests/multicast_pim_static_rp_topo1/multicast_pim_static_rp.json +++ b/tests/topotests/multicast_pim_static_rp_topo1/multicast_pim_static_rp.json @@ -4,7 +4,11 @@ "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24}, "lo_prefix": {"ipv4": "1.0.", "v4mask": 32}, "routers": { - "r0": {"links": {"r1": {"ipv4": "auto"}}}, + "r0": { + "links": { + "r1": {"ipv4": "auto"} + } + }, "r1": { "links": { "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}, @@ -14,9 +18,20 @@ "r4": {"ipv4": "auto", "pim": "enable"} }, "pim": { - "rp": [{"rp_addr": "1.0.2.17", "group_addr_range": ["224.0.0.0/4"]}] + "join-prune-interval": "5", + "keep-alive-timer": 15, + "register-suppress-time": 12 + }, + "igmp": { + "interfaces": { + "r1-r0-eth0": { + "igmp": { + "query": {"query-max-response-time": 40, "query-interval": 5}, + "version": "2" + } + } + } }, - "igmp": {"interfaces": {"r1-r0-eth0": {"igmp": {"version": "2"}}}}, "static_routes": [ {"network": "10.0.4.0/24", "next_hop": "10.0.2.2"}, {"network": "10.0.5.0/24", "next_hop": "10.0.2.2"}, @@ -35,6 +50,9 @@ "r3": {"ipv4": "auto", "pim": "enable"} }, "pim": { + "join-prune-interval": "5", + "keep-alive-timer": 15, + "register-suppress-time": 12, "rp": [{"rp_addr": "1.0.2.17", "group_addr_range": ["224.0.0.0/4"]}] }, "static_routes": [ @@ -57,7 +75,9 @@ "r5": {"ipv4": "auto", "pim": "enable"} }, "pim": { - "rp": [{"rp_addr": "1.0.2.17", "group_addr_range": ["224.0.0.0/4"]}] + "join-prune-interval": "5", + "keep-alive-timer": 15, + "register-suppress-time": 12 }, "static_routes": [ {"network": "10.0.0.0/24", "next_hop": "10.0.2.1"}, @@ -75,7 +95,9 @@ "r3": {"ipv4": "auto", "pim": "enable"} }, "pim": { - "rp": [{"rp_addr": "1.0.2.17", "group_addr_range": ["224.0.0.0/4"]}] + "join-prune-interval": "5", + "keep-alive-timer": 15, + "register-suppress-time": 12 }, "static_routes": [ {"network": "10.0.0.0/24", "next_hop": "10.0.3.1"}, @@ -88,6 +110,10 @@ {"network": "1.0.3.17/32", "next_hop": "10.0.5.1"} ] }, - "r5": {"links": {"r3": {"ipv4": "auto"}}} + "r5": { + "links": { + "r3": {"ipv4": "auto"} + } + } } } diff --git a/tests/topotests/ospf6_topo2/r1/ospf6d.conf b/tests/topotests/ospf6_topo2/r1/ospf6d.conf index c403fcd8dc..2e465e6d1f 100644 --- a/tests/topotests/ospf6_topo2/r1/ospf6d.conf +++ b/tests/topotests/ospf6_topo2/r1/ospf6d.conf @@ -1,3 +1,28 @@ +debug ospf6 lsa router +debug ospf6 lsa router originate +debug ospf6 lsa router examine +debug ospf6 lsa router flooding +debug ospf6 lsa as-external +debug ospf6 lsa as-external originate +debug ospf6 lsa as-external examine +debug ospf6 lsa as-external flooding +debug ospf6 lsa intra-prefix +debug ospf6 lsa intra-prefix originate +debug ospf6 lsa intra-prefix examine +debug ospf6 lsa intra-prefix flooding +debug ospf6 border-routers +debug ospf6 zebra +debug ospf6 interface +debug ospf6 neighbor +debug ospf6 flooding +debug ospf6 gr helper +debug ospf6 spf process +debug ospf6 route intra-area +debug ospf6 route inter-area +debug ospf6 abr +debug ospf6 asbr +debug ospf6 nssa +! interface r1-eth0 ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 10 diff --git a/tests/topotests/ospf6_topo2/r2/ospf6d.conf b/tests/topotests/ospf6_topo2/r2/ospf6d.conf index e88e965c78..4a1d10693d 100644 --- a/tests/topotests/ospf6_topo2/r2/ospf6d.conf +++ b/tests/topotests/ospf6_topo2/r2/ospf6d.conf @@ -1,3 +1,28 @@ +debug ospf6 lsa router +debug ospf6 lsa router originate +debug ospf6 lsa router examine +debug ospf6 lsa router flooding +debug ospf6 lsa as-external +debug ospf6 lsa as-external originate +debug ospf6 lsa as-external examine +debug ospf6 lsa as-external flooding +debug ospf6 lsa intra-prefix +debug ospf6 lsa intra-prefix originate +debug ospf6 lsa intra-prefix examine +debug ospf6 lsa intra-prefix flooding +debug ospf6 border-routers +debug ospf6 zebra +debug ospf6 interface +debug ospf6 neighbor +debug ospf6 flooding +debug ospf6 gr helper +debug ospf6 spf process +debug ospf6 route intra-area +debug ospf6 route inter-area +debug ospf6 abr +debug ospf6 asbr +debug ospf6 nssa +! interface r2-eth0 ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 10 diff --git a/tests/topotests/ospf6_topo2/r3/ospf6d.conf b/tests/topotests/ospf6_topo2/r3/ospf6d.conf index aaef00d5bb..5faeb70e56 100644 --- a/tests/topotests/ospf6_topo2/r3/ospf6d.conf +++ b/tests/topotests/ospf6_topo2/r3/ospf6d.conf @@ -1,3 +1,28 @@ +debug ospf6 lsa router +debug ospf6 lsa router originate +debug ospf6 lsa router examine +debug ospf6 lsa router flooding +debug ospf6 lsa as-external +debug ospf6 lsa as-external originate +debug ospf6 lsa as-external examine +debug ospf6 lsa as-external flooding +debug ospf6 lsa intra-prefix +debug ospf6 lsa intra-prefix originate +debug ospf6 lsa intra-prefix examine +debug ospf6 lsa intra-prefix flooding +debug ospf6 border-routers +debug ospf6 zebra +debug ospf6 interface +debug ospf6 neighbor +debug ospf6 flooding +debug ospf6 gr helper +debug ospf6 spf process +debug ospf6 route intra-area +debug ospf6 route inter-area +debug ospf6 abr +debug ospf6 asbr +debug ospf6 nssa +! interface r3-eth0 ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 10 diff --git a/tests/topotests/ospf6_topo2/r4/ospf6d.conf b/tests/topotests/ospf6_topo2/r4/ospf6d.conf index 813c0abff2..04d763f6a8 100644 --- a/tests/topotests/ospf6_topo2/r4/ospf6d.conf +++ b/tests/topotests/ospf6_topo2/r4/ospf6d.conf @@ -1,3 +1,28 @@ +debug ospf6 lsa router +debug ospf6 lsa router originate +debug ospf6 lsa router examine +debug ospf6 lsa router flooding +debug ospf6 lsa as-external +debug ospf6 lsa as-external originate +debug ospf6 lsa as-external examine +debug ospf6 lsa as-external flooding +debug ospf6 lsa intra-prefix +debug ospf6 lsa intra-prefix originate +debug ospf6 lsa intra-prefix examine +debug ospf6 lsa intra-prefix flooding +debug ospf6 border-routers +debug ospf6 zebra +debug ospf6 interface +debug ospf6 neighbor +debug ospf6 flooding +debug ospf6 gr helper +debug ospf6 spf process +debug ospf6 route intra-area +debug ospf6 route inter-area +debug ospf6 abr +debug ospf6 asbr +debug ospf6 nssa +! interface r4-eth0 ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 10 diff --git a/tests/topotests/ospf6_topo2/test_ospf6_topo2.py b/tests/topotests/ospf6_topo2/test_ospf6_topo2.py index 0fe5228ce6..8c5f1e6f60 100644 --- a/tests/topotests/ospf6_topo2/test_ospf6_topo2.py +++ b/tests/topotests/ospf6_topo2/test_ospf6_topo2.py @@ -73,15 +73,20 @@ def expect_lsas(router, area, lsas, wait=5, extra_params=""): assert result is None, assertmsg -def expect_ospfv3_routes(router, routes, wait=5): +def expect_ospfv3_routes(router, routes, wait=5, detail=False): "Run command `ipv6 ospf6 route` and expect route with type." tgen = get_topogen() + if detail == False: + cmd = "show ipv6 ospf6 route json" + else: + cmd = "show ipv6 ospf6 route detail json" + logger.info("waiting OSPFv3 router '{}' route".format(router)) test_func = partial( topotest.router_json_cmp, tgen.gears[router], - "show ipv6 ospf6 route json", + cmd, {"routes": routes} ) _, result = topotest.run_and_expect(test_func, None, count=wait, wait=1) @@ -199,7 +204,7 @@ def test_ospfv3_expected_route_types(): { "numberOfIntraAreaRoutes": 1, "numberOfInterAreaRoutes": 2, - "numberOfExternal1Routes": 0, + "numberOfExternal1Routes": 4, "numberOfExternal2Routes": 0, }, ) @@ -236,6 +241,51 @@ def test_ospf6_default_route(): expect_route("r1", "::/0", metric + 10) +def test_redistribute_metrics(): + """ + Test that the configured metrics are honored when a static route is + redistributed. + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Add new static route on r3. + config = """ + configure terminal + ipv6 route 2001:db8:500::/64 Null0 + """ + tgen.gears["r3"].vtysh_cmd(config) + + route = { + "2001:db8:500::/64": { + "metricType":2, + "metricCost":10, + } + } + logger.info("Expecting AS-external route 2001:db8:500::/64 to show up with default metrics") + expect_ospfv3_routes("r2", route, wait=30, detail=True) + + # Change the metric of redistributed routes of the static type on r3. + config = """ + configure terminal + router ospf6 + redistribute static metric 50 metric-type 1 + """ + tgen.gears["r3"].vtysh_cmd(config) + + # Check if r3 reinstalled 2001:db8:500::/64 using the new metric type and value. + route = { + "2001:db8:500::/64": { + "metricType":1, + "metricCost":60, + } + } + logger.info("Expecting AS-external route 2001:db8:500::/64 to show up with updated metric type and value") + expect_ospfv3_routes("r2", route, wait=30, detail=True) + + + def test_nssa_lsa_type7(): """ Test that static route gets announced as external route when redistributed diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_topo1.py b/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_topo1.py index e61a6b5905..a3f1bc76ff 100644 --- a/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_topo1.py +++ b/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_topo1.py @@ -1384,7 +1384,7 @@ def test_ospf_type5_summary_tc45_p0(request): step("Verify that summary lsa is withdrawn from R1 and deleted from R0.") dut = "r1" - result = verify_ospf_rib(tgen, dut, input_dict, expected=False) + result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False) assert ( result is not True ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_routemaps.py b/tests/topotests/ospf_basic_functionality/test_ospf_routemaps.py index bc6c248ad2..066f53aa58 100644 --- a/tests/topotests/ospf_basic_functionality/test_ospf_routemaps.py +++ b/tests/topotests/ospf_basic_functionality/test_ospf_routemaps.py @@ -53,6 +53,7 @@ from lib.common_config import ( create_route_maps, verify_prefix_lists, topo_daemons, + shutdown_bringup_interface ) from lib.topolog import logger from lib.topojson import build_topo_from_json, build_config_from_json @@ -1046,6 +1047,336 @@ def test_ospf_routemaps_functionality_tc24_p0(request): write_test_footer(tc_name) +def test_ospf_routemaps_functionality_tc25_p0(request): + """ + OSPF route map support functionality. + + Verify OSPF route map support functionality + when route map actions are toggled. + + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + global topo + step("Bring up the base config as per the topology") + + reset_config_on_routers(tgen) + + step( + "Create static routes(10.0.20.1/32) in R1 and redistribute " + "to OSPF using route map.") + + # Create Static routes + input_dict = { + "r0": { + "static_routes": [ + { + "network": NETWORK['ipv4'][0], + "no_of_ip": 5, + "next_hop": 'Null0', + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + ospf_red_r0 = { + "r0": { + "ospf": { + "redistribute": [{ + "redist_type": "static", + "route_map": "rmap_ipv4" + }] + } + } + } + result = create_router_ospf(tgen, topo, ospf_red_r0) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + step("Configure route map with permit rule") + # Create route map + routemaps = { + "r0": { + "route_maps": { + "rmap_ipv4": [{ + "action": "permit" + }] + } + } + } + result = create_route_maps(tgen, routemaps) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Verify that route is advertised to R1.") + dut = 'r1' + protocol = 'ospf' + result = verify_ospf_rib(tgen, dut, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + step("Configure route map with deny rule") + # Create route map + routemaps = { + "r0": { + "route_maps": { + "rmap_ipv4": [{ + "seq_id": 10, + "action": "deny" + }] + } + } + } + result = create_route_maps(tgen, routemaps) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + # Api call verify whether OSPF is converged + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, ("setup_module :Failed \n Error:" + " {}".format(ospf_covergence)) + + step("Verify that route is not advertised to R1.") + dut = 'r1' + protocol = 'ospf' + result = verify_ospf_rib(tgen, dut, input_dict, expected=False) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, + expected=False) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + write_test_footer(tc_name) + + +def test_ospf_routemaps_functionality_tc22_p0(request): + """ + OSPF Route map - Multiple sequence numbers. + + Verify OSPF route map support functionality with multiple sequence + numbers in a single route-map for different match/set clauses. + + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + global topo + step("Bring up the base config as per the topology") + + reset_config_on_routers(tgen) + + step( + "Configure route map with seq number 10 to with ip prefix" + " permitting route 10.0.20.1/32 in R1") + step( + "Configure route map with seq number 20 to with ip prefix" + " permitting route 10.0.20.2/32 in R1") + + # Create route map + input_dict_3 = { + "r0": { + "route_maps": { + "rmap_ipv4": [{ + "action": "permit", + 'seq_id': '10', + "match": { + "ipv4": { + "prefix_lists": "pf_list_1_ipv4" + } + } + }, + { + "action": "permit", + 'seq_id': '20', + "match": { + "ipv4": { + "prefix_lists": "pf_list_2_ipv4" + } + } + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + # Create ip prefix list + input_dict_2 = { + 'r0': { + 'prefix_lists': { + 'ipv4': { + 'pf_list_1_ipv4': [{ + 'seqid': 10, + 'network': NETWORK['ipv4'][0], + 'action': 'permit' + }] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + # Create ip prefix list + input_dict_2 = { + 'r0': { + 'prefix_lists': { + 'ipv4': { + 'pf_list_2_ipv4': [{ + 'seqid': 10, + 'network': NETWORK['ipv4'][1], + 'action': 'permit' + }] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + step("Configure static routes 10.0.20.1/32 and 10.0.20.2 in R1") + # Create Static routes + input_dict = { + "r0": { + "static_routes": [ + { + "network": NETWORK['ipv4'][0], + "no_of_ip": 5, + "next_hop": 'Null0', + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Configure redistribute static route with route map.") + ospf_red_r0 = { + "r0": { + "ospf": { + "redistribute": [{ + "redist_type": "static", + "route_map": "rmap_ipv4" + }] + } + } + } + result = create_router_ospf(tgen, topo, ospf_red_r0) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + input_dict = { + "r0": { + "static_routes": [ + { + "network": NETWORK['ipv4'][0], + "no_of_ip": 2, + "next_hop": 'Null0', + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Verify that both routes are learned in R1 and R2") + dut = 'r1' + protocol = 'ospf' + result = verify_ospf_rib(tgen, dut, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + dut = 'r2' + protocol = 'ospf' + result = verify_ospf_rib(tgen, dut, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + step("Change route map with seq number 20 to deny.") + # Create route map + input_dict_3 = { + "r0": { + "route_maps": { + "rmap_ipv4": [ + { + "action": "deny", + 'seq_id': '20', + "match": { + "ipv4": { + "prefix_lists": "pf_list_2_ipv4" + } + } + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + step( + "Verify the route 10.0.20.2/32 is withdrawn and not present " + "in the routing table of R0 and R1.") + + input_dict = { + "r0": { + "static_routes": [ + { + "network": NETWORK['ipv4'][1], + "next_hop": 'Null0' + } + ] + } + } + + dut = 'r1' + protocol = 'ospf' + result = verify_ospf_rib(tgen, dut, input_dict, expected=False) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, + expected=False) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + dut = 'r2' + protocol = 'ospf' + result = verify_ospf_rib(tgen, dut, input_dict, expected=False) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, + expected=False) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + write_test_footer(tc_name) + + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] sys.exit(pytest.main(args)) diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_single_area.py b/tests/topotests/ospf_basic_functionality/test_ospf_single_area.py index a595bc0491..0d0668a931 100644 --- a/tests/topotests/ospf_basic_functionality/test_ospf_single_area.py +++ b/tests/topotests/ospf_basic_functionality/test_ospf_single_area.py @@ -1011,7 +1011,7 @@ def test_ospf_tc4_mtu_ignore_p0(request): r0_r1_intf = topo["routers"]["r0"]["links"]["r1"]["interface"] r1_r0_intf = topo["routers"]["r1"]["links"]["r0"]["interface"] - rtr0.run("ifconfig {} mtu 1200".format(r0_r1_intf)) + rtr0.run("ip link set {} mtu 1200".format(r0_r1_intf)) clear_ospf(tgen, "r0") @@ -1037,7 +1037,7 @@ def test_ospf_tc4_mtu_ignore_p0(request): "Modify the MTU to non default Value on R0 to R1 interface. " "Reset ospf neighbors on R0." ) - rtr0.run("ifconfig {} mtu 1500".format(r0_r1_intf)) + rtr0.run("ip link set {} mtu 1500".format(r0_r1_intf)) clear_ospf(tgen, "r0") @@ -1062,7 +1062,7 @@ def test_ospf_tc4_mtu_ignore_p0(request): result = config_ospf_interface(tgen, topo, r1_ospf_mtu) assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) - rtr0.run("ifconfig {} mtu 1200".format(r0_r1_intf)) + rtr0.run("ip link set {} mtu 1200".format(r0_r1_intf)) clear_ospf(tgen, "r0") @@ -1076,7 +1076,7 @@ def test_ospf_tc4_mtu_ignore_p0(request): ) r1_ospf_mtu = { - "r1": {"links": {"r0": {"ospf": {"mtu_ignore": True, "delete": True}}}} + "r1": {"links": {"r0": {"ospf": {"mtu_ignore": True, "del_action": True}}}} } result = config_ospf_interface(tgen, topo, r1_ospf_mtu) assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) @@ -1094,7 +1094,7 @@ def test_ospf_tc4_mtu_ignore_p0(request): step("Modify the MTU to again default valaue on R0 to R1 interface.") - rtr0.run("ifconfig {} mtu 1500".format(r0_r1_intf)) + rtr0.run("ip link set {} mtu 1500".format(r0_r1_intf)) clear_ospf(tgen, "r0") @@ -1106,8 +1106,8 @@ def test_ospf_tc4_mtu_ignore_p0(request): "Configure ospf interface with jumbo MTU (9216)." "Reset ospf neighbors on R0." ) - rtr0.run("ifconfig {} mtu 9216".format(r0_r1_intf)) - rtr1.run("ifconfig {} mtu 9216".format(r1_r0_intf)) + rtr0.run("ip link set {} mtu 9216".format(r0_r1_intf)) + rtr1.run("ip link set {} mtu 9216".format(r1_r0_intf)) clear_ospf(tgen, "r0") clear_ospf(tgen, "r1") diff --git a/tests/topotests/ospf_gr_helper/ospf_gr_helper.json b/tests/topotests/ospf_gr_helper/ospf_gr_helper.json new file mode 100644 index 0000000000..efd339ef88 --- /dev/null +++ b/tests/topotests/ospf_gr_helper/ospf_gr_helper.json @@ -0,0 +1,119 @@ +{ + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 24 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32 + }, + "switches": { + "s1": { + "links": { + "r0": { + "ipv4": "17.1.1.2/24", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 40, + "priority": 98 + } + }, + "r1": { + "ipv4": "17.1.1.1/24", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 40, + "priority": 99 + } + }, + "r2": { + "ipv4": "17.1.1.3/24", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 40, + "priority": 0 + } + }, + "r3": { + "ipv4": "17.1.1.4/24", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 40, + "priority": 0 + } + } + } + } + }, + "routers": { + "r0": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + } + }, + "ospf": { + "router_id": "100.1.1.0", + "neighbors": { + "r1": {}, + "r2": {}, + "r3": {} + } + } + }, + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + } + }, + "ospf": { + "router_id": "1.1.1.1", + "neighbors": { + "r0": {}, + "r2": {}, + "r3": {} + } + }, + "opq_lsa_hex": "01005e00000570708bd051ef080045c0005cc18b0000015904f711010101e00000050204004801010101000000001e8d0000000000000000000000000001000102090300000001010101800000013bd1002c000100040000070800020001010000000003000411010101" + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + } + }, + "ospf": { + "router_id": "100.1.1.2", + "neighbors": { + "r1": {}, + "r0": {} + } + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + } + }, + "ospf": { + "router_id": "100.1.1.3", + "neighbors": { + "r0": {}, + "r1": {} + } + } + } + } +}
\ No newline at end of file diff --git a/tests/topotests/ospf_gr_helper/test_ospf_gr_helper.py b/tests/topotests/ospf_gr_helper/test_ospf_gr_helper.py new file mode 100644 index 0000000000..5363822134 --- /dev/null +++ b/tests/topotests/ospf_gr_helper/test_ospf_gr_helper.py @@ -0,0 +1,752 @@ +#!/usr/bin/python + +# +# Copyright (c) 2021 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. +# ("NetDEF") in this file. +# +# 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 VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE 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. +# + + +"""OSPF Basic Functionality Automation.""" +import os +import sys +import time +import pytest +import json +from time import sleep +from copy import deepcopy +import ipaddress + +# 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 mininet.topo import Topo +from lib.topogen import Topogen, get_topogen + +# Import topoJson from lib, to create topology and initial configuration +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + reset_config_on_routers, + verify_rib, + create_static_routes, + step, + create_route_maps, + shutdown_bringup_interface, + create_interfaces_cfg, + topo_daemons, + scapy_send_raw_packet +) + +from lib.topolog import logger +from lib.topojson import build_topo_from_json, build_config_from_json + +from lib.ospf import ( + verify_ospf_neighbor, + clear_ospf, + verify_ospf_gr_helper, + create_router_ospf, + verify_ospf_interface, + verify_ospf_database, +) + +# Global variables +topo = None +Iters = 5 +sw_name = None +intf = None +intf1 = None +pkt = None + +# Reading the data from JSON File for topology creation +jsonFile = "{}/ospf_gr_helper.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +""" +Topology: + + Please view in a fixed-width font such as Courier. + Topo : Broadcast Networks + DUT - HR RR + +---+ +---+ +---+ +---+ + |R0 + +R1 + +R2 + +R3 | + +-+-+ +-+-+ +-+-+ +-+-+ + | | | | + | | | | + --+-----------+--------------+---------------+----- + Ethernet Segment + +Testcases: + +TC1. Verify by default helper support is disabled for FRR ospf +TC2. OSPF GR on Broadcast : Verify DUT enters Helper mode when neighbor + sends grace lsa, helps RR to restart gracefully (RR = DR) +TC3. OSPF GR on Broadcast : Verify DUT enters Helper mode when neighbor + sends grace lsa, helps RR to restart gracefully (RR = BDR) +TC4. OSPF GR on Broadcast : Verify DUT enters Helper mode when neighbor + sends grace lsa, helps RR to restart gracefully (RR = DRother) +TC5. OSPF GR on P2P : Verify DUT enters Helper mode when neighbor sends + grace lsa, helps RR to restart gracefully. +TC6. Verify all the show commands newly introducted as part of ospf + helper support - Json Key verification wrt to show commands. +TC7. Verify helper when grace lsa is received with different configured + value in process level (higher, lower, grace lsa timer above 1800) +TC8. Verify helper functionality when dut is helping RR and new grace lsa + is received from RR. +""" + + +class CreateTopo(Topo): + """ + Test topology builder. + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function.""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + global topo, intf, intf1, sw_name, pkt + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(CreateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # get list of daemons needs to be started for this suite. + daemons = topo_daemons(tgen, topo) + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen, daemons) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + sw_name = topo["switches"].keys()[0] + intf = topo["routers"]["r0"]["links"][sw_name]["interface"] + intf1 = topo["routers"]["r1"]["links"][sw_name]["interface"] + pkt = topo["routers"]["r1"]["opq_lsa_hex"] + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + try: + # Stop toplogy and Remove tmp files + tgen.stop_topology + + except OSError: + # OSError exception is raised when mininet tries to stop switch + # though switch is stopped once but mininet tries to stop same + # switch again, where it ended up with exception + pass + + +def delete_ospf(): + """delete ospf process after each test""" + tgen = get_topogen() + step("Delete ospf process") + for rtr in topo["routers"]: + ospf_del = {rtr: {"ospf": {"delete": True}}} + result = create_router_ospf(tgen, topo, ospf_del) + assert result is True, "Testcase: Failed \n Error: {}".format(result) + + +# ################################## +# Test cases start here. +# ################################## + + +def test_ospf_gr_helper_tc1_p0(request): + """Verify by default helper support is disabled for FRR ospf""" + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + global topo, intf, intf1, pkt + + step("Bring up the base config as per the topology") + reset_config_on_routers(tgen) + ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True) + assert ( + ospf_covergence is True + ), "OSPF is not after reset config \n Error:" " {}".format(ospf_covergence) + + step("Verify that GR helper route is disabled by default to the in" "the DUT.") + input_dict = { + "helperSupport": "Disabled", + "strictLsaCheck": "Enabled", + "restartSupoort": "Planned and Unplanned Restarts", + "supportedGracePeriod": 1800, + } + dut = "r0" + result = verify_ospf_gr_helper(tgen, topo, dut, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify that DUT does not enter helper mode upon receiving the " "grace lsa.") + + # send grace lsa + scapy_send_raw_packet(tgen, topo, "r1", intf1, pkt) + + input_dict = {"activeRestarterCnt": 1} + dut = "r0" + result = verify_ospf_gr_helper(tgen, topo, dut, input_dict, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed. DUT entered helper role " " \n Error: {}".format( + tc_name, result + ) + + step("Configure graceful restart in the DUT") + ospf_gr_r0 = { + "r0": {"ospf": {"graceful-restart": {"helper-only": [], "opaque": True}}} + } + result = create_router_ospf(tgen, topo, ospf_gr_r0) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify that GR helper route is enabled in the DUT.") + input_dict = { + "helperSupport": "Enabled", + "strictLsaCheck": "Enabled", + "restartSupoort": "Planned and Unplanned Restarts", + "supportedGracePeriod": 1800, + } + dut = "r0" + result = verify_ospf_gr_helper(tgen, topo, dut, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + ospf_gr_r1 = { + "r1": {"ospf": {"graceful-restart": {"helper-only": [], "opaque": True}}} + } + result = create_router_ospf(tgen, topo, ospf_gr_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Perform GR in RR.") + step("Verify that DUT does enter helper mode upon receiving" " the grace lsa.") + input_dict = {"activeRestarterCnt": 1} + gracelsa_sent = False + repeat = 0 + dut = "r0" + while not gracelsa_sent and repeat < Iters: + gracelsa_sent = scapy_send_raw_packet(tgen, topo, "r1", intf1, pkt) + result = verify_ospf_gr_helper(tgen, topo, dut, input_dict) + if isinstance(result, str): + repeat += 1 + gracelsa_sent = False + + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Unconfigure the GR helper command.") + ospf_gr_r0 = { + "r0": { + "ospf": { + "graceful-restart": {"helper-only": [], "opaque": True, "delete": True} + } + } + } + result = create_router_ospf(tgen, topo, ospf_gr_r0) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + input_dict = {"helperSupport": "Disabled"} + dut = "r0" + result = verify_ospf_gr_helper(tgen, topo, dut, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure gr helper using the router id") + ospf_gr_r0 = { + "r0": { + "ospf": {"graceful-restart": {"helper-only": ["1.1.1.1"], "opaque": True}} + } + } + result = create_router_ospf(tgen, topo, ospf_gr_r0) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify that DUT does enter helper mode upon receiving" " the grace lsa.") + input_dict = {"activeRestarterCnt": 1} + gracelsa_sent = False + repeat = 0 + dut = "r0" + while not gracelsa_sent and repeat < Iters: + gracelsa_sent = scapy_send_raw_packet(tgen, topo, "r1", intf1, pkt) + result = verify_ospf_gr_helper(tgen, topo, dut, input_dict) + if isinstance(result, str): + repeat += 1 + gracelsa_sent = False + + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Un Configure gr helper using the router id") + ospf_gr_r0 = { + "r0": { + "ospf": { + "graceful-restart": { + "helper-only": ["1.1.1.1"], + "opaque": True, + "delete": True, + } + } + } + } + result = create_router_ospf(tgen, topo, ospf_gr_r0) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify that GR helper router is disabled in the DUT for" " router id x.x.x.x") + input_dict = {"enabledRouterIds": [{"routerId": "1.1.1.1"}]} + dut = "r0" + result = verify_ospf_gr_helper(tgen, topo, dut, input_dict, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed, Helper role enabled for RR\n Error: {}".format( + tc_name, result + ) + delete_ospf() + write_test_footer(tc_name) + + +def test_ospf_gr_helper_tc2_p0(request): + """ + OSPF GR on Broadcast : Verify DUT enters Helper mode when neighbor + sends grace lsa, helps RR to restart gracefully (RR = DR) + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + global topo, intf, intf1, pkt + + step("Bring up the base config as per the topology") + step( + "Configure DR priority as 99 in RR , DUT dr priority = 98 " + "& reset ospf process in all the routers" + ) + reset_config_on_routers(tgen) + ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True) + assert ( + ospf_covergence is True + ), "OSPF is not after reset config \n Error:" " {}".format(ospf_covergence) + ospf_gr_r0 = { + "r0": {"ospf": {"graceful-restart": {"helper-only": [], "opaque": True}}} + } + result = create_router_ospf(tgen, topo, ospf_gr_r0) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + ospf_gr_r1 = { + "r1": {"ospf": {"graceful-restart": {"helper-only": [], "opaque": True}}} + } + result = create_router_ospf(tgen, topo, ospf_gr_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify that DUT enters into helper mode.") + + input_dict = {"activeRestarterCnt": 1} + gracelsa_sent = False + repeat = 0 + dut = "r0" + while not gracelsa_sent and repeat < Iters: + gracelsa_sent = scapy_send_raw_packet(tgen, topo, "r1", intf1, pkt) + result = verify_ospf_gr_helper(tgen, topo, dut, input_dict) + if isinstance(result, str): + repeat += 1 + gracelsa_sent = False + + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + delete_ospf() + write_test_footer(tc_name) + + +def test_ospf_gr_helper_tc3_p1(request): + """ + OSPF GR on Broadcast : Verify DUT enters Helper mode when neighbor + sends grace lsa, helps RR to restart gracefully (RR = BDR) + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + global topo, intf, intf1, pkt + + step("Bring up the base config as per the topology") + step( + "Configure DR priority as 99 in RR , DUT dr priority = 98 " + "& reset ospf process in all the routers" + ) + reset_config_on_routers(tgen) + ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True) + assert ( + ospf_covergence is True + ), "OSPF is not after reset config \n Error:" " {}".format(ospf_covergence) + step( + "Configure DR pririty 100 on R0 and clear ospf neighbors " "on all the routers." + ) + + input_dict = { + "r0": { + "links": { + sw_name: { + "interface": topo["routers"]["r0"]["links"][sw_name]["interface"], + "ospf": {"priority": 100}, + } + } + } + } + + result = create_interfaces_cfg(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Clear ospf neighbours in all routers") + for rtr in topo["routers"]: + clear_ospf(tgen, rtr) + + step("Verify that DR election is triggered and R0 is elected as DR") + input_dict = { + "r0": { + "ospf": { + "neighbors": { + "r1": {"state": "Full", "role": "Backup"}, + "r2": {"state": "Full", "role": "DROther"}, + "r3": {"state": "Full", "role": "DROther"}, + } + } + } + } + dut = "r0" + result = verify_ospf_neighbor(tgen, topo, dut, input_dict, lan=True) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + ospf_gr_r0 = { + "r0": {"ospf": {"graceful-restart": {"helper-only": [], "opaque": True}}} + } + result = create_router_ospf(tgen, topo, ospf_gr_r0) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + ospf_gr_r1 = { + "r1": {"ospf": {"graceful-restart": {"helper-only": [], "opaque": True}}} + } + result = create_router_ospf(tgen, topo, ospf_gr_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify that DUT enters into helper mode.") + + input_dict = {"activeRestarterCnt": 1} + gracelsa_sent = False + repeat = 0 + dut = "r0" + while not gracelsa_sent and repeat < Iters: + gracelsa_sent = scapy_send_raw_packet(tgen, topo, "r1", intf1, pkt) + result = verify_ospf_gr_helper(tgen, topo, dut, input_dict) + if isinstance(result, str): + repeat += 1 + gracelsa_sent = False + + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + delete_ospf() + write_test_footer(tc_name) + + +def test_ospf_gr_helper_tc4_p1(request): + """ + OSPF GR on Broadcast : Verify DUT enters Helper mode when neighbor + sends grace lsa, helps RR to restart gracefully (RR = DRother) + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + global topo, intf, intf1, pkt + + step("Bring up the base config as per the topology") + step( + "Configure DR priority as 99 in RR , DUT dr priority = 98 " + "& reset ospf process in all the routers" + ) + reset_config_on_routers(tgen) + ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True) + assert ( + ospf_covergence is True + ), "OSPF is not after reset config \n Error:" " {}".format(ospf_covergence) + step( + "Configure DR pririty 100 on R0 and clear ospf neighbors " "on all the routers." + ) + + input_dict = { + "r0": { + "links": { + sw_name: { + "interface": topo["routers"]["r0"]["links"][sw_name]["interface"], + "ospf": {"priority": 0}, + } + } + } + } + + result = create_interfaces_cfg(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Clear ospf neighbours in all routers") + for rtr in topo["routers"]: + clear_ospf(tgen, rtr) + + step("Verify that DR election is triggered and R0 is elected as 2-Way") + input_dict = { + "r0": { + "ospf": { + "neighbors": { + "r1": {"state": "Full", "role": "DR"}, + "r2": {"state": "2-Way", "role": "DROther"}, + "r3": {"state": "2-Way", "role": "DROther"}, + } + } + } + } + dut = "r0" + result = verify_ospf_neighbor(tgen, topo, dut, input_dict, lan=True) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + ospf_gr_r0 = { + "r0": {"ospf": {"graceful-restart": {"helper-only": [], "opaque": True}}} + } + result = create_router_ospf(tgen, topo, ospf_gr_r0) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + ospf_gr_r1 = { + "r1": {"ospf": {"graceful-restart": {"helper-only": [], "opaque": True}}} + } + result = create_router_ospf(tgen, topo, ospf_gr_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify that DUT enters into helper mode.") + + input_dict = {"activeRestarterCnt": 1} + gracelsa_sent = False + repeat = 0 + dut = "r0" + while not gracelsa_sent and repeat < Iters: + gracelsa_sent = scapy_send_raw_packet(tgen, topo, "r1", intf1, pkt) + result = verify_ospf_gr_helper(tgen, topo, dut, input_dict) + if isinstance(result, str): + repeat += 1 + gracelsa_sent = False + + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + delete_ospf() + + write_test_footer(tc_name) + + +def test_ospf_gr_helper_tc7_p1(request): + """ + Test ospf gr helper + Verify helper when grace lsa is received with different configured + value in process level (higher, lower, grace lsa timer above 1800) + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + global topo, intf, intf1, pkt + + step("Bring up the base config as per the topology") + step( + "Configure DR priority as 99 in RR , DUT dr priority = 98 " + "& reset ospf process in all the routers" + ) + step( + "Enable GR on RR and DUT with grace period on RR = 333" + "and grace period on DUT = 300" + ) + reset_config_on_routers(tgen) + ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True) + assert ( + ospf_covergence is True + ), "OSPF is not after reset config \n Error:" " {}".format(ospf_covergence) + ospf_gr_r0 = { + "r0": {"ospf": {"graceful-restart": {"helper-only": [], "opaque": True}}} + } + result = create_router_ospf(tgen, topo, ospf_gr_r0) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + ospf_gr_r1 = { + "r1": {"ospf": {"graceful-restart": {"helper-only": [], "opaque": True}}} + } + result = create_router_ospf(tgen, topo, ospf_gr_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + input_dict = {"supportedGracePeriod": 1800} + dut = "r0" + result = verify_ospf_gr_helper(tgen, topo, dut, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure grace period = 1801 on RR and restart ospf .") + grace_period_1801 = "01005e00000570708bd051ef080045c0005cbeb10000015907d111010101e00000050204004801010101000000009714000000000000000000000000000100010209030000000101010180000001c8e9002c000100040000016800020001010000000003000411010101" + gracelsa_sent = scapy_send_raw_packet(tgen, topo, "r1", intf1, grace_period_1801) + + step("Verify R0 does not enter helper mode.") + input_dict = {"activeRestarterCnt": 1} + dut = "r0" + result = verify_ospf_gr_helper(tgen, topo, dut, input_dict, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed. DUT entered helper role " " \n Error: {}".format( + tc_name, result + ) + + delete_ospf() + + write_test_footer(tc_name) + + +def test_ospf_gr_helper_tc8_p1(request): + """ + Test ospf gr helper + + Verify helper functionality when dut is helping RR and new grace lsa + is received from RR. + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + global topo, intf, intf1, pkt + + step("Bring up the base config as per the topology") + step("Enable GR") + reset_config_on_routers(tgen) + ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True) + assert ( + ospf_covergence is True + ), "OSPF is not after reset config \n Error:" " {}".format(ospf_covergence) + ospf_gr_r0 = { + "r0": {"ospf": {"graceful-restart": {"helper-only": [], "opaque": True}}} + } + result = create_router_ospf(tgen, topo, ospf_gr_r0) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + ospf_gr_r1 = { + "r1": {"ospf": {"graceful-restart": {"helper-only": [], "opaque": True}}} + } + result = create_router_ospf(tgen, topo, ospf_gr_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + input_dict = {"supportedGracePeriod": 1800} + dut = "r0" + result = verify_ospf_gr_helper(tgen, topo, dut, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify that DUT enters into helper mode.") + + input_dict = {"activeRestarterCnt": 1} + gracelsa_sent = False + repeat = 0 + dut = "r0" + while not gracelsa_sent and repeat < Iters: + gracelsa_sent = scapy_send_raw_packet(tgen, topo, "r1", intf1, pkt) + result = verify_ospf_gr_helper(tgen, topo, dut, input_dict) + if isinstance(result, str): + repeat += 1 + gracelsa_sent = False + + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Send the Grace LSA again to DUT when RR is in GR.") + input_dict = {"activeRestarterCnt": 1} + gracelsa_sent = False + repeat = 0 + dut = "r0" + while not gracelsa_sent and repeat < Iters: + gracelsa_sent = scapy_send_raw_packet(tgen, topo, "r1", intf1, pkt) + result = verify_ospf_gr_helper(tgen, topo, dut, input_dict) + if isinstance(result, str): + repeat += 1 + gracelsa_sent = False + + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + delete_ospf() + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/ospf-sr-te-topo1/dst/zebra.conf b/tests/topotests/ospf_sr_te_topo1/dst/zebra.conf index 4cb50fdb27..4cb50fdb27 100644 --- a/tests/topotests/ospf-sr-te-topo1/dst/zebra.conf +++ b/tests/topotests/ospf_sr_te_topo1/dst/zebra.conf diff --git a/tests/topotests/ospf-sr-te-topo1/rt1/bgpd.conf b/tests/topotests/ospf_sr_te_topo1/rt1/bgpd.conf index efc03701b5..efc03701b5 100644 --- a/tests/topotests/ospf-sr-te-topo1/rt1/bgpd.conf +++ b/tests/topotests/ospf_sr_te_topo1/rt1/bgpd.conf diff --git a/tests/topotests/ospf-sr-te-topo1/rt1/ospfd.conf b/tests/topotests/ospf_sr_te_topo1/rt1/ospfd.conf index 225ac93528..225ac93528 100644 --- a/tests/topotests/ospf-sr-te-topo1/rt1/ospfd.conf +++ b/tests/topotests/ospf_sr_te_topo1/rt1/ospfd.conf diff --git a/tests/topotests/ospf-sr-te-topo1/rt1/pathd.conf b/tests/topotests/ospf_sr_te_topo1/rt1/pathd.conf index 55d5857f5d..55d5857f5d 100644 --- a/tests/topotests/ospf-sr-te-topo1/rt1/pathd.conf +++ b/tests/topotests/ospf_sr_te_topo1/rt1/pathd.conf diff --git a/tests/topotests/ospf-sr-te-topo1/rt1/step2/show_operational_data.ref b/tests/topotests/ospf_sr_te_topo1/rt1/step2/show_operational_data.ref index 4ef8d946f2..4ef8d946f2 100644 --- a/tests/topotests/ospf-sr-te-topo1/rt1/step2/show_operational_data.ref +++ b/tests/topotests/ospf_sr_te_topo1/rt1/step2/show_operational_data.ref diff --git a/tests/topotests/ospf-sr-te-topo1/rt1/step2/show_operational_data_with_candidate.ref b/tests/topotests/ospf_sr_te_topo1/rt1/step2/show_operational_data_with_candidate.ref index 9b28f6a42b..9b28f6a42b 100644 --- a/tests/topotests/ospf-sr-te-topo1/rt1/step2/show_operational_data_with_candidate.ref +++ b/tests/topotests/ospf_sr_te_topo1/rt1/step2/show_operational_data_with_candidate.ref diff --git a/tests/topotests/ospf-sr-te-topo1/rt1/step3/show_operational_data_with_single_candidate.ref b/tests/topotests/ospf_sr_te_topo1/rt1/step3/show_operational_data_with_single_candidate.ref index 9b28f6a42b..9b28f6a42b 100644 --- a/tests/topotests/ospf-sr-te-topo1/rt1/step3/show_operational_data_with_single_candidate.ref +++ b/tests/topotests/ospf_sr_te_topo1/rt1/step3/show_operational_data_with_single_candidate.ref diff --git a/tests/topotests/ospf-sr-te-topo1/rt1/step3/show_operational_data_with_two_candidates.ref b/tests/topotests/ospf_sr_te_topo1/rt1/step3/show_operational_data_with_two_candidates.ref index 249117198a..249117198a 100644 --- a/tests/topotests/ospf-sr-te-topo1/rt1/step3/show_operational_data_with_two_candidates.ref +++ b/tests/topotests/ospf_sr_te_topo1/rt1/step3/show_operational_data_with_two_candidates.ref diff --git a/tests/topotests/ospf-sr-te-topo1/rt1/zebra.conf b/tests/topotests/ospf_sr_te_topo1/rt1/zebra.conf index dd686ea3da..dd686ea3da 100644 --- a/tests/topotests/ospf-sr-te-topo1/rt1/zebra.conf +++ b/tests/topotests/ospf_sr_te_topo1/rt1/zebra.conf diff --git a/tests/topotests/ospf-sr-te-topo1/rt2/ospfd.conf b/tests/topotests/ospf_sr_te_topo1/rt2/ospfd.conf index f6a7bbb621..f6a7bbb621 100644 --- a/tests/topotests/ospf-sr-te-topo1/rt2/ospfd.conf +++ b/tests/topotests/ospf_sr_te_topo1/rt2/ospfd.conf diff --git a/tests/topotests/ospf-sr-te-topo1/rt2/zebra.conf b/tests/topotests/ospf_sr_te_topo1/rt2/zebra.conf index ddd50ba520..ddd50ba520 100644 --- a/tests/topotests/ospf-sr-te-topo1/rt2/zebra.conf +++ b/tests/topotests/ospf_sr_te_topo1/rt2/zebra.conf diff --git a/tests/topotests/ospf-sr-te-topo1/rt3/ospfd.conf b/tests/topotests/ospf_sr_te_topo1/rt3/ospfd.conf index 5f71cd8484..5f71cd8484 100644 --- a/tests/topotests/ospf-sr-te-topo1/rt3/ospfd.conf +++ b/tests/topotests/ospf_sr_te_topo1/rt3/ospfd.conf diff --git a/tests/topotests/ospf-sr-te-topo1/rt3/zebra.conf b/tests/topotests/ospf_sr_te_topo1/rt3/zebra.conf index 0825b5c8bf..0825b5c8bf 100644 --- a/tests/topotests/ospf-sr-te-topo1/rt3/zebra.conf +++ b/tests/topotests/ospf_sr_te_topo1/rt3/zebra.conf diff --git a/tests/topotests/ospf-sr-te-topo1/rt4/ospfd.conf b/tests/topotests/ospf_sr_te_topo1/rt4/ospfd.conf index d4862cd233..d4862cd233 100644 --- a/tests/topotests/ospf-sr-te-topo1/rt4/ospfd.conf +++ b/tests/topotests/ospf_sr_te_topo1/rt4/ospfd.conf diff --git a/tests/topotests/ospf-sr-te-topo1/rt4/zebra.conf b/tests/topotests/ospf_sr_te_topo1/rt4/zebra.conf index c6d1f4f40e..c6d1f4f40e 100644 --- a/tests/topotests/ospf-sr-te-topo1/rt4/zebra.conf +++ b/tests/topotests/ospf_sr_te_topo1/rt4/zebra.conf diff --git a/tests/topotests/ospf-sr-te-topo1/rt5/ospfd.conf b/tests/topotests/ospf_sr_te_topo1/rt5/ospfd.conf index fdc0dcfdb7..fdc0dcfdb7 100644 --- a/tests/topotests/ospf-sr-te-topo1/rt5/ospfd.conf +++ b/tests/topotests/ospf_sr_te_topo1/rt5/ospfd.conf diff --git a/tests/topotests/ospf-sr-te-topo1/rt5/zebra.conf b/tests/topotests/ospf_sr_te_topo1/rt5/zebra.conf index 96b732d398..96b732d398 100644 --- a/tests/topotests/ospf-sr-te-topo1/rt5/zebra.conf +++ b/tests/topotests/ospf_sr_te_topo1/rt5/zebra.conf diff --git a/tests/topotests/ospf-sr-te-topo1/rt6/bgpd.conf b/tests/topotests/ospf_sr_te_topo1/rt6/bgpd.conf index e72ee52fce..e72ee52fce 100644 --- a/tests/topotests/ospf-sr-te-topo1/rt6/bgpd.conf +++ b/tests/topotests/ospf_sr_te_topo1/rt6/bgpd.conf diff --git a/tests/topotests/ospf-sr-te-topo1/rt6/ospfd.conf b/tests/topotests/ospf_sr_te_topo1/rt6/ospfd.conf index c06565be0b..c06565be0b 100644 --- a/tests/topotests/ospf-sr-te-topo1/rt6/ospfd.conf +++ b/tests/topotests/ospf_sr_te_topo1/rt6/ospfd.conf diff --git a/tests/topotests/ospf-sr-te-topo1/rt6/pathd.conf b/tests/topotests/ospf_sr_te_topo1/rt6/pathd.conf index 696df2214b..696df2214b 100644 --- a/tests/topotests/ospf-sr-te-topo1/rt6/pathd.conf +++ b/tests/topotests/ospf_sr_te_topo1/rt6/pathd.conf diff --git a/tests/topotests/ospf-sr-te-topo1/rt6/step2/show_operational_data.ref b/tests/topotests/ospf_sr_te_topo1/rt6/step2/show_operational_data.ref index 241c80bdd7..241c80bdd7 100644 --- a/tests/topotests/ospf-sr-te-topo1/rt6/step2/show_operational_data.ref +++ b/tests/topotests/ospf_sr_te_topo1/rt6/step2/show_operational_data.ref diff --git a/tests/topotests/ospf-sr-te-topo1/rt6/step2/show_operational_data_with_candidate.ref b/tests/topotests/ospf_sr_te_topo1/rt6/step2/show_operational_data_with_candidate.ref index 20ea69e386..20ea69e386 100644 --- a/tests/topotests/ospf-sr-te-topo1/rt6/step2/show_operational_data_with_candidate.ref +++ b/tests/topotests/ospf_sr_te_topo1/rt6/step2/show_operational_data_with_candidate.ref diff --git a/tests/topotests/ospf-sr-te-topo1/rt6/step3/show_operational_data_with_single_candidate.ref b/tests/topotests/ospf_sr_te_topo1/rt6/step3/show_operational_data_with_single_candidate.ref index 20ea69e386..20ea69e386 100644 --- a/tests/topotests/ospf-sr-te-topo1/rt6/step3/show_operational_data_with_single_candidate.ref +++ b/tests/topotests/ospf_sr_te_topo1/rt6/step3/show_operational_data_with_single_candidate.ref diff --git a/tests/topotests/ospf-sr-te-topo1/rt6/step3/show_operational_data_with_two_candidates.ref b/tests/topotests/ospf_sr_te_topo1/rt6/step3/show_operational_data_with_two_candidates.ref index 10cafe9091..10cafe9091 100644 --- a/tests/topotests/ospf-sr-te-topo1/rt6/step3/show_operational_data_with_two_candidates.ref +++ b/tests/topotests/ospf_sr_te_topo1/rt6/step3/show_operational_data_with_two_candidates.ref diff --git a/tests/topotests/ospf-sr-te-topo1/rt6/zebra.conf b/tests/topotests/ospf_sr_te_topo1/rt6/zebra.conf index 360837c4ca..360837c4ca 100644 --- a/tests/topotests/ospf-sr-te-topo1/rt6/zebra.conf +++ b/tests/topotests/ospf_sr_te_topo1/rt6/zebra.conf diff --git a/tests/topotests/ospf-sr-te-topo1/test_ospf_sr_te_topo1.py b/tests/topotests/ospf_sr_te_topo1/test_ospf_sr_te_topo1.py index 6c1122ab72..6c1122ab72 100755 --- a/tests/topotests/ospf-sr-te-topo1/test_ospf_sr_te_topo1.py +++ b/tests/topotests/ospf_sr_te_topo1/test_ospf_sr_te_topo1.py diff --git a/tests/topotests/ospf_topo1_vrf/r1/ospfd.conf b/tests/topotests/ospf_topo1_vrf/r1/ospfd.conf index 9a68635568..e1e2bfb99a 100644 --- a/tests/topotests/ospf_topo1_vrf/r1/ospfd.conf +++ b/tests/topotests/ospf_topo1_vrf/r1/ospfd.conf @@ -3,7 +3,7 @@ hostname r1 password zebra log file /tmp/r1-ospfd.log ! -router ospf vrf r1-cust1 +router ospf vrf r1-ospf-cust1 ospf router-id 10.0.255.1 redistribute kernel redistribute connected diff --git a/tests/topotests/ospf_topo1_vrf/r1/ospfroute.txt b/tests/topotests/ospf_topo1_vrf/r1/ospfroute.txt index 134a10a454..d617ab36d9 100644 --- a/tests/topotests/ospf_topo1_vrf/r1/ospfroute.txt +++ b/tests/topotests/ospf_topo1_vrf/r1/ospfroute.txt @@ -1,4 +1,4 @@ -VRF Name: r1-cust1 +VRF Name: r1-ospf-cust1 ============ OSPF network routing table ============ N 10.0.1.0/24 [10] area: 0.0.0.0 directly attached to r1-eth0 diff --git a/tests/topotests/ospf_topo1_vrf/r1/ospfroute_down.txt b/tests/topotests/ospf_topo1_vrf/r1/ospfroute_down.txt index 083d77126c..4f7fd699cf 100644 --- a/tests/topotests/ospf_topo1_vrf/r1/ospfroute_down.txt +++ b/tests/topotests/ospf_topo1_vrf/r1/ospfroute_down.txt @@ -1,4 +1,4 @@ -VRF Name: r1-cust1 +VRF Name: r1-ospf-cust1 ============ OSPF network routing table ============ N 10.0.1.0/24 [10] area: 0.0.0.0 directly attached to r1-eth0 diff --git a/tests/topotests/ospf_topo1_vrf/r1/zebra.conf b/tests/topotests/ospf_topo1_vrf/r1/zebra.conf index e826793657..e100d3b121 100644 --- a/tests/topotests/ospf_topo1_vrf/r1/zebra.conf +++ b/tests/topotests/ospf_topo1_vrf/r1/zebra.conf @@ -7,10 +7,10 @@ hostname r1 password zebra log file /tmp/r1-zebra.log ! -interface r1-eth0 vrf r1-cust1 +interface r1-eth0 vrf r1-ospf-cust1 ip address 10.0.1.1/24 ! -interface r1-eth1 vrf r1-cust1 +interface r1-eth1 vrf r1-ospf-cust1 ip address 10.0.3.2/24 ! ip forwarding diff --git a/tests/topotests/ospf_topo1_vrf/r1/zebraroute.txt b/tests/topotests/ospf_topo1_vrf/r1/zebraroute.txt index d72aa3b8e5..979af20c59 100644 --- a/tests/topotests/ospf_topo1_vrf/r1/zebraroute.txt +++ b/tests/topotests/ospf_topo1_vrf/r1/zebraroute.txt @@ -1,4 +1,4 @@ -VRF r1-cust1: +VRF r1-ospf-cust1: O 10.0.1.0/24 [110/10] is directly connected, r1-eth0, weight 1, XX:XX:XX C>* 10.0.1.0/24 is directly connected, r1-eth0, XX:XX:XX O>* 10.0.2.0/24 [110/20] via 10.0.3.3, r1-eth1, weight 1, XX:XX:XX diff --git a/tests/topotests/ospf_topo1_vrf/r1/zebraroutedown.txt b/tests/topotests/ospf_topo1_vrf/r1/zebraroutedown.txt index 5ea6bdc04d..ec99fad762 100644 --- a/tests/topotests/ospf_topo1_vrf/r1/zebraroutedown.txt +++ b/tests/topotests/ospf_topo1_vrf/r1/zebraroutedown.txt @@ -1,4 +1,4 @@ -VRF r1-cust1: +VRF r1-ospf-cust1: O 10.0.1.0/24 [110/10] is directly connected, r1-eth0, weight 1, XX:XX:XX C>* 10.0.1.0/24 is directly connected, r1-eth0, XX:XX:XX O>* 10.0.2.0/24 [110/20] via 10.0.3.3, r1-eth1, weight 1, XX:XX:XX diff --git a/tests/topotests/ospf_topo1_vrf/r2/ospfd.conf b/tests/topotests/ospf_topo1_vrf/r2/ospfd.conf index ad481a996d..c1984276f4 100644 --- a/tests/topotests/ospf_topo1_vrf/r2/ospfd.conf +++ b/tests/topotests/ospf_topo1_vrf/r2/ospfd.conf @@ -4,7 +4,7 @@ password zebra log file /tmp/r2-ospfd.log ! ! -router ospf vrf r2-cust1 +router ospf vrf r2-ospf-cust1 ospf router-id 10.0.255.2 redistribute kernel redistribute connected diff --git a/tests/topotests/ospf_topo1_vrf/r2/ospfroute.txt b/tests/topotests/ospf_topo1_vrf/r2/ospfroute.txt index a49cb77249..89763ff733 100644 --- a/tests/topotests/ospf_topo1_vrf/r2/ospfroute.txt +++ b/tests/topotests/ospf_topo1_vrf/r2/ospfroute.txt @@ -1,4 +1,4 @@ -VRF Name: r2-cust1 +VRF Name: r2-ospf-cust1 ============ OSPF network routing table ============ N 10.0.1.0/24 [20] area: 0.0.0.0 via 10.0.3.2, r2-eth1 diff --git a/tests/topotests/ospf_topo1_vrf/r2/ospfroute_down.txt b/tests/topotests/ospf_topo1_vrf/r2/ospfroute_down.txt index 2227bedf07..d946f02dfd 100644 --- a/tests/topotests/ospf_topo1_vrf/r2/ospfroute_down.txt +++ b/tests/topotests/ospf_topo1_vrf/r2/ospfroute_down.txt @@ -1,4 +1,4 @@ -VRF Name: r2-cust1 +VRF Name: r2-ospf-cust1 ============ OSPF network routing table ============ N 10.0.1.0/24 [20] area: 0.0.0.0 via 10.0.3.2, r2-eth1 diff --git a/tests/topotests/ospf_topo1_vrf/r2/zebra.conf b/tests/topotests/ospf_topo1_vrf/r2/zebra.conf index 8dcb713da6..6ff72d1267 100644 --- a/tests/topotests/ospf_topo1_vrf/r2/zebra.conf +++ b/tests/topotests/ospf_topo1_vrf/r2/zebra.conf @@ -3,10 +3,10 @@ hostname r2 password zebra log file /tmp/r2-zebra.log ! -interface r2-eth0 vrf r2-cust1 +interface r2-eth0 vrf r2-ospf-cust1 ip address 10.0.2.1/24 ! -interface r2-eth1 vrf r2-cust1 +interface r2-eth1 vrf r2-ospf-cust1 ip address 10.0.3.3/24 ! ip forwarding diff --git a/tests/topotests/ospf_topo1_vrf/r2/zebraroute.txt b/tests/topotests/ospf_topo1_vrf/r2/zebraroute.txt index ce5e5f3bab..df66e92abc 100644 --- a/tests/topotests/ospf_topo1_vrf/r2/zebraroute.txt +++ b/tests/topotests/ospf_topo1_vrf/r2/zebraroute.txt @@ -1,4 +1,4 @@ -VRF r2-cust1: +VRF r2-ospf-cust1: O>* 10.0.1.0/24 [110/20] via 10.0.3.2, r2-eth1, weight 1, XX:XX:XX O 10.0.2.0/24 [110/10] is directly connected, r2-eth0, weight 1, XX:XX:XX C>* 10.0.2.0/24 is directly connected, r2-eth0, XX:XX:XX diff --git a/tests/topotests/ospf_topo1_vrf/r2/zebraroutedown.txt b/tests/topotests/ospf_topo1_vrf/r2/zebraroutedown.txt index 157811ec77..4afc354ca7 100644 --- a/tests/topotests/ospf_topo1_vrf/r2/zebraroutedown.txt +++ b/tests/topotests/ospf_topo1_vrf/r2/zebraroutedown.txt @@ -1,4 +1,4 @@ -VRF r2-cust1: +VRF r2-ospf-cust1: O>* 10.0.1.0/24 [110/20] via 10.0.3.2, r2-eth1, weight 1, XX:XX:XX O 10.0.2.0/24 [110/10] is directly connected, r2-eth0, weight 1, XX:XX:XX C>* 10.0.2.0/24 is directly connected, r2-eth0, XX:XX:XX diff --git a/tests/topotests/ospf_topo1_vrf/r3/ospfd.conf b/tests/topotests/ospf_topo1_vrf/r3/ospfd.conf index d5214f734e..b73d547e3e 100644 --- a/tests/topotests/ospf_topo1_vrf/r3/ospfd.conf +++ b/tests/topotests/ospf_topo1_vrf/r3/ospfd.conf @@ -4,7 +4,7 @@ password zebra log file /tmp/r3-ospfd.log ! ! -router ospf vrf r3-cust1 +router ospf vrf r3-ospf-cust1 ospf router-id 10.0.255.3 redistribute kernel redistribute connected diff --git a/tests/topotests/ospf_topo1_vrf/r3/ospfroute.txt b/tests/topotests/ospf_topo1_vrf/r3/ospfroute.txt index 3b16bfbd55..917702b14c 100644 --- a/tests/topotests/ospf_topo1_vrf/r3/ospfroute.txt +++ b/tests/topotests/ospf_topo1_vrf/r3/ospfroute.txt @@ -1,4 +1,4 @@ -VRF Name: r3-cust1 +VRF Name: r3-ospf-cust1 ============ OSPF network routing table ============ N 10.0.1.0/24 [20] area: 0.0.0.0 via 10.0.3.2, r3-eth0 diff --git a/tests/topotests/ospf_topo1_vrf/r3/ospfroute_down.txt b/tests/topotests/ospf_topo1_vrf/r3/ospfroute_down.txt index 39beac7a73..966185e495 100644 --- a/tests/topotests/ospf_topo1_vrf/r3/ospfroute_down.txt +++ b/tests/topotests/ospf_topo1_vrf/r3/ospfroute_down.txt @@ -1,4 +1,4 @@ -VRF Name: r3-cust1 +VRF Name: r3-ospf-cust1 ============ OSPF network routing table ============ N 10.0.10.0/24 [10] area: 0.0.0.0 directly attached to r3-eth1 diff --git a/tests/topotests/ospf_topo1_vrf/r3/zebra.conf b/tests/topotests/ospf_topo1_vrf/r3/zebra.conf index b548694330..1534150048 100644 --- a/tests/topotests/ospf_topo1_vrf/r3/zebra.conf +++ b/tests/topotests/ospf_topo1_vrf/r3/zebra.conf @@ -3,10 +3,10 @@ hostname r3 password zebra log file /tmp/r3-zebra.log ! -interface r3-eth0 vrf r3-cust1 +interface r3-eth0 vrf r3-ospf-cust1 ip address 10.0.3.1/24 ! -interface r3-eth1 vrf r3-cust1 +interface r3-eth1 vrf r3-ospf-cust1 ip address 10.0.10.1/24 ! ip forwarding diff --git a/tests/topotests/ospf_topo1_vrf/r3/zebraroute.txt b/tests/topotests/ospf_topo1_vrf/r3/zebraroute.txt index f40b7b09af..b435c2ebe5 100644 --- a/tests/topotests/ospf_topo1_vrf/r3/zebraroute.txt +++ b/tests/topotests/ospf_topo1_vrf/r3/zebraroute.txt @@ -1,4 +1,4 @@ -VRF r3-cust1: +VRF r3-ospf-cust1: O>* 10.0.1.0/24 [110/20] via 10.0.3.2, r3-eth0, weight 1, XX:XX:XX O>* 10.0.2.0/24 [110/20] via 10.0.3.3, r3-eth0, weight 1, XX:XX:XX O 10.0.3.0/24 [110/10] is directly connected, r3-eth0, weight 1, XX:XX:XX diff --git a/tests/topotests/ospf_topo1_vrf/r3/zebraroutedown.txt b/tests/topotests/ospf_topo1_vrf/r3/zebraroutedown.txt index 89cd6f56c4..f30a4be6c6 100644 --- a/tests/topotests/ospf_topo1_vrf/r3/zebraroutedown.txt +++ b/tests/topotests/ospf_topo1_vrf/r3/zebraroutedown.txt @@ -1,4 +1,4 @@ -VRF r3-cust1: +VRF r3-ospf-cust1: O 10.0.10.0/24 [110/10] is directly connected, r3-eth1, weight 1, XX:XX:XX C>* 10.0.10.0/24 is directly connected, r3-eth1, XX:XX:XX diff --git a/tests/topotests/ospf_topo1_vrf/test_ospf_topo1_vrf.py b/tests/topotests/ospf_topo1_vrf/test_ospf_topo1_vrf.py index e2cb7bff03..713a65a812 100644 --- a/tests/topotests/ospf_topo1_vrf/test_ospf_topo1_vrf.py +++ b/tests/topotests/ospf_topo1_vrf/test_ospf_topo1_vrf.py @@ -100,17 +100,17 @@ def setup_module(mod): logger.info("Testing with VRF Namespace support") cmds = [ - "if [ -e /var/run/netns/{0}-cust1 ] ; then ip netns del {0}-cust1 ; fi", - "ip netns add {0}-cust1", - "ip link set dev {0}-eth0 netns {0}-cust1", - "ip netns exec {0}-cust1 ifconfig {0}-eth0 up", - "ip link set dev {0}-eth1 netns {0}-cust1", - "ip netns exec {0}-cust1 ifconfig {0}-eth1 up", + "if [ -e /var/run/netns/{0}-ospf-cust1 ] ; then ip netns del {0}-ospf-cust1 ; fi", + "ip netns add {0}-ospf-cust1", + "ip link set dev {0}-eth0 netns {0}-ospf-cust1", + "ip netns exec {0}-ospf-cust1 ip link set {0}-eth0 up", + "ip link set dev {0}-eth1 netns {0}-ospf-cust1", + "ip netns exec {0}-ospf-cust1 ip link set {0}-eth1 up", ] for rname, router in router_list.items(): - # create VRF rx-cust1 and link rx-eth0 to rx-cust1 + # create VRF rx-ospf-cust1 and link rx-eth0 to rx-ospf-cust1 for cmd in cmds: output = tgen.net[rname].cmd(cmd.format(rname)) @@ -137,9 +137,9 @@ def teardown_module(mod): # move back rx-eth0 to default VRF # delete rx-vrf cmds = [ - "ip netns exec {0}-cust1 ip link set {0}-eth0 netns 1", - "ip netns exec {0}-cust1 ip link set {0}-eth1 netns 1", - "ip netns delete {0}-cust1", + "ip netns exec {0}-ospf-cust1 ip link set {0}-eth0 netns 1", + "ip netns exec {0}-ospf-cust1 ip link set {0}-eth1 netns 1", + "ip netns delete {0}-ospf-cust1", ] router_list = tgen.routers() @@ -152,11 +152,11 @@ def teardown_module(mod): # Shared test function to validate expected output. def compare_show_ip_route_vrf(rname, expected): """ - Calls 'show ip ospf vrf [rname]-cust1 route' for router `rname` and compare the obtained + Calls 'show ip ospf vrf [rname]-ospf-cust1 route' for router `rname` and compare the obtained result with the expected output. """ tgen = get_topogen() - vrf_name = "{0}-cust1".format(rname) + vrf_name = "{0}-ospf-cust1".format(rname) current = topotest.ip4_route_zebra(tgen.gears[rname], vrf_name) ret = topotest.difflines( current, expected, title1="Current output", title2="Expected output" @@ -182,7 +182,7 @@ def test_ospf_convergence(): test_func = partial( topotest.router_output_cmp, router, - "show ip ospf vrf {0}-cust1 route".format(rname), + "show ip ospf vrf {0}-ospf-cust1 route".format(rname), expected, ) result, diff = topotest.run_and_expect(test_func, "", count=160, wait=0.5) @@ -220,13 +220,13 @@ def test_ospf_json(): for rname, router in tgen.routers().items(): logger.info( - 'Comparing router "%s" "show ip ospf vrf %s-cust1 json" output', + 'Comparing router "%s" "show ip ospf vrf %s-ospf-cust1 json" output', router.name, router.name, ) expected = { - "{}-cust1".format(router.name): { - "vrfName": "{}-cust1".format(router.name), + "{}-ospf-cust1".format(router.name): { + "vrfName": "{}-ospf-cust1".format(router.name), "routerId": "10.0.255.{}".format(rname[1:]), "tosRoutesOnly": True, "rfc2328Conform": True, @@ -244,7 +244,7 @@ def test_ospf_json(): } # Area specific additional checks if router.name == "r1" or router.name == "r2" or router.name == "r3": - expected["{}-cust1".format(router.name)]["areas"]["0.0.0.0"] = { + expected["{}-ospf-cust1".format(router.name)]["areas"]["0.0.0.0"] = { "areaIfActiveCounter": 2, "areaIfTotalCounter": 2, "authentication": "authenticationNone", @@ -263,7 +263,7 @@ def test_ospf_json(): test_func = partial( topotest.router_json_cmp, router, - "show ip ospf vrf {0}-cust1 json".format(rname), + "show ip ospf vrf {0}-ospf-cust1 json".format(rname), expected, ) _, diff = topotest.run_and_expect(test_func, None, count=10, wait=0.5) @@ -281,7 +281,7 @@ def test_ospf_link_down(): # Simulate a network down event on router3 switch3 interface. router3 = tgen.gears["r3"] topotest.interface_set_status( - router3, "r3-eth0", ifaceaction=False, vrf_name="r3-cust1" + router3, "r3-eth0", ifaceaction=False, vrf_name="r3-ospf-cust1" ) # Expect convergence on all routers @@ -295,7 +295,7 @@ def test_ospf_link_down(): test_func = partial( topotest.router_output_cmp, router, - "show ip ospf vrf {0}-cust1 route".format(rname), + "show ip ospf vrf {0}-ospf-cust1 route".format(rname), expected, ) result, diff = topotest.run_and_expect(test_func, "", count=140, wait=0.5) @@ -316,7 +316,7 @@ def test_ospf_link_down_kernel_route(): 'Checking OSPF IPv4 kernel routes in "%s" after link down', router.name ) - str = "{0}-cust1".format(router.name) + str = "{0}-ospf-cust1".format(router.name) reffile = os.path.join(CWD, "{}/zebraroutedown.txt".format(router.name)) expected = open(reffile).read() # Run test function until we get an result. Wait at most 60 seconds. diff --git a/tests/topotests/zebra_rib/test_zebra_rib.py b/tests/topotests/zebra_rib/test_zebra_rib.py index 56d112b7c3..778a710ee3 100644 --- a/tests/topotests/zebra_rib/test_zebra_rib.py +++ b/tests/topotests/zebra_rib/test_zebra_rib.py @@ -104,13 +104,19 @@ def test_zebra_kernel_admin_distance(): r1 = tgen.gears["r1"] # Route with 255/8192 metric - r1.run("ip route add 4.5.1.0/24 via 192.168.210.2 dev r1-eth0 metric 4278198272") + + distance = 255 + metric = 8192 + def makekmetric(dist, metric): + return (dist << 24) + metric + + r1.run("ip route add 4.5.1.0/24 via 192.168.210.2 dev r1-eth0 metric " + str(makekmetric(255, 8192))) # Route with 1/1 metric - r1.run("ip route add 4.5.2.0/24 via 192.168.211.2 dev r1-eth1 metric 16777217") + r1.run("ip route add 4.5.2.0/24 via 192.168.211.2 dev r1-eth1 metric " + str(makekmetric(1, 1))) # Route with 10/1 metric - r1.run("ip route add 4.5.3.0/24 via 192.168.212.2 dev r1-eth2 metric 167772161") + r1.run("ip route add 4.5.3.0/24 via 192.168.212.2 dev r1-eth2 metric " + str(makekmetric(10, 1))) # Same route with a 160/1 metric - r1.run("ip route add 4.5.3.0/24 via 192.168.213.2 dev r1-eth3 metric 2684354561") + r1.run("ip route add 4.5.3.0/24 via 192.168.213.2 dev r1-eth3 metric " + str(makekmetric(160, 1))) # Currently I believe we have a bug here with the same route and different # metric. That needs to be properly resolved. Making a note for @@ -194,93 +200,69 @@ def test_route_map_usage(): static_rmapfile = "%s/r1/static_rmap.ref" % (thisDir) expected = open(static_rmapfile).read().rstrip() expected = ("\n".join(expected.splitlines()) + "\n").rstrip() - actual = r1.vtysh_cmd("show route-map static") - actual = ("\n".join(actual.splitlines()) + "\n").rstrip() logger.info( "Does the show route-map static command run the correct number of times" ) - - diff = topotest.get_textdiff( - actual, - expected, - title1="Actual Route-map output", - title2="Expected Route-map output", - ) - if diff: - logger.info("Actual:") - logger.info(actual) - logger.info("Expected:") - logger.info(expected) - srun = r1.vtysh_cmd("show run") - srun = ("\n".join(srun.splitlines()) + "\n").rstrip() - logger.info("Show run") - logger.info(srun) - assert 0, "r1 static route processing:\n" + def check_static_map_correct_runs(): + actual = r1.vtysh_cmd("show route-map static") + actual = ("\n".join(actual.splitlines()) + "\n").rstrip() + return topotest.get_textdiff( + actual, + expected, + title1="Actual Route-map output", + title2="Expected Route-map output", + ) + ok, result = topotest.run_and_expect(check_static_map_correct_runs, "", count=5, wait=1) + assert ok, result sharp_rmapfile = "%s/r1/sharp_rmap.ref" % (thisDir) expected = open(sharp_rmapfile).read().rstrip() expected = ("\n".join(expected.splitlines()) + "\n").rstrip() - actual = r1.vtysh_cmd("show route-map sharp") - actual = ("\n".join(actual.splitlines()) + "\n").rstrip() logger.info("Does the show route-map sharp command run the correct number of times") - - diff = topotest.get_textdiff( - actual, - expected, - title1="Actual Route-map output", - title2="Expected Route-map output", - ) - if diff: - logger.info("Actual:") - logger.info(actual) - logger.info("Expected:") - logger.info(expected) - srun = r1.vtysh_cmd("show run") - srun = ("\n".join(srun.splitlines()) + "\n").rstrip() - logger.info("Show run:") - logger.info(srun) - assert 0, "r1 sharp route-map processing:\n" + def check_sharp_map_correct_runs(): + actual = r1.vtysh_cmd("show route-map sharp") + actual = ("\n".join(actual.splitlines()) + "\n").rstrip() + return topotest.get_textdiff( + actual, + expected, + title1="Actual Route-map output", + title2="Expected Route-map output", + ) + ok, result = topotest.run_and_expect(check_sharp_map_correct_runs, "", count=5, wait=1) + assert ok, result logger.info( "Add a extension to the static route-map to see the static route go away" + " and test that the routes installed are correct" ) + r1.vtysh_cmd("conf\nroute-map sharp deny 5\nmatch ip address 5") - sleep(2) # we are only checking the kernel here as that this will give us the implied # testing of both the route-map and staticd withdrawing the route # let's spot check that the routes were installed correctly # in the kernel - logger.info("Test that the routes installed are correct") sharp_ipfile = "%s/r1/iproute.ref" % (thisDir) expected = open(sharp_ipfile).read().rstrip() expected = ("\n".join(expected.splitlines()) + "\n").rstrip() - actual = r1.run("ip route show") - actual = ("\n".join(actual.splitlines()) + "\n").rstrip() - actual = re.sub(r" nhid [0-9][0-9]", "", actual) - actual = re.sub(r" proto sharp", " proto XXXX", actual) - actual = re.sub(r" proto static", " proto XXXX", actual) - actual = re.sub(r" proto 194", " proto XXXX", actual) - actual = re.sub(r" proto 196", " proto XXXX", actual) - actual = re.sub(r" proto kernel", " proto XXXX", actual) - actual = re.sub(r" proto 2", " proto XXXX", actual) - # Some platforms have double spaces? Why?????? - actual = re.sub(r" proto XXXX ", " proto XXXX ", actual) - actual = re.sub(r" metric", " metric", actual) - actual = re.sub(r" link ", " link ", actual) - diff = topotest.get_textdiff( - actual, expected, title1="Actual ip route show", title2="Expected ip route show" - ) - - if diff: - logger.info("Actual:") - logger.info(actual) - logger.info("Expected:") - logger.info(expected) - srun = r1.vtysh_cmd("show run") - srun = ("\n".join(srun.splitlines()) + "\n").rstrip() - logger.info("Show run:") - logger.info(srun) - assert 0, "r1 ip route show is not correct:" + def check_routes_installed(): + actual = r1.run("ip route show") + actual = ("\n".join(actual.splitlines()) + "\n").rstrip() + actual = re.sub(r" nhid [0-9][0-9]", "", actual) + actual = re.sub(r" proto sharp", " proto XXXX", actual) + actual = re.sub(r" proto static", " proto XXXX", actual) + actual = re.sub(r" proto 194", " proto XXXX", actual) + actual = re.sub(r" proto 196", " proto XXXX", actual) + actual = re.sub(r" proto kernel", " proto XXXX", actual) + actual = re.sub(r" proto 2", " proto XXXX", actual) + # Some platforms have double spaces? Why?????? + actual = re.sub(r" proto XXXX ", " proto XXXX ", actual) + actual = re.sub(r" metric", " metric", actual) + actual = re.sub(r" link ", " link ", actual) + return topotest.get_textdiff( + actual, expected, title1="Actual ip route show", title2="Expected ip route show" + ) + ok, result = topotest.run_and_expect(check_routes_installed, "", count=5, wait=1) + assert ok, result def test_memory_leak(): diff --git a/tools/etc/frr/support_bundle_commands.conf b/tools/etc/frr/support_bundle_commands.conf index 732470f828..e223eb2743 100644 --- a/tools/etc/frr/support_bundle_commands.conf +++ b/tools/etc/frr/support_bundle_commands.conf @@ -83,9 +83,45 @@ show version CMD_LIST_END # OSPF Support Bundle Command List -# PROC_NAME:ospf -# CMD_LIST_START -# CMD_LIST_END +PROC_NAME:ospf +CMD_LIST_START +show ip ospf vrf all +show ip ospf vrfs + +show ip ospf vrf all interface +show ip ospf vrf all interface traffic +show ip ospf vrf all neighbor +show ip ospf vrf all neighbor detail all +show ip ospf vrf all graceful-restart helper detail + +show ip ospf vrf all border-routers +show ip ospf vrf all summary-address detail + +show ip ospf vrf all database +show ip ospf vrf all database router +show ip ospf vrf all database network +show ip ospf vrf all database summary +show ip ospf vrf all database asbr-summary +show ip ospf vrf all database external +show ip ospf vrf all database opaque-area +show ip ospf vrf all database opaque-as +show ip ospf vrf all database opaque-link +show ip ospf vrf all database nssa-external +show ip ospf database segment-routing +show ip ospf vrf all database max-age +show ip ospf vrf all database self-originate +show ip ospf vrf all route + +show ip ospf mpls ldp-sync +show ip ospf mpls ldp-sync interface all + +show ip ospf vrf all mpls-te interface +show ip ospf mpls-te database verbose +show ip ospf mpls-te router + +show ip ospf router-info +show ip ospf router-info pce +CMD_LIST_END # RIP Support Bundle Command List # PROC_NAME:rip @@ -130,3 +166,43 @@ show ip pim state show ip pim statistics show ip pim rpf CMD_LIST_END + +# OSPFv3 Support Bundle Command List +PROC_NAME:ospf6 +CMD_LIST_START +show ipv6 ospf6 vrf all +show ipv6 ospf6 vrfs +show ipv6 ospf6 vrf all border-routers +show ipv6 ospf6 vrf all border-routers detail +show ipv6 ospf6 vrf all database +show ipv6 ospf6 vrf all database detail +show ipv6 ospf6 vrf all database dump +show ipv6 ospf6 vrf all database internal +show ipv6 ospf6 vrf all database router detail +show ipv6 ospf6 vrf all database network detail +show ipv6 ospf6 vrf all database inter-prefix detail +show ipv6 ospf6 vrf all database inter-router detail +show ipv6 ospf6 vrf all database intra-prefix detail +show ipv6 ospf6 vrf all database link detail +show ipv6 ospf6 vrf all database as-external detail +show ipv6 ospf6 vrf all database self-originate detail +show ipv6 ospf6 vrf all database type-7 detail +show ipv6 ospf6 vrf all interface +show ipv6 ospf6 vrf all interface prefix +show ipv6 ospf6 vrf all interface traffic +show ipv6 ospf6 vrf all linkstate detail +show ipv6 ospf6 vrf all neighbor +show ipv6 ospf6 vrf all neighbor drchoice +show ipv6 ospf6 vrf all neighbor detail +show ipv6 ospf6 vrf all redistribute +show ipv6 ospf6 vrf all route +show ipv6 ospf6 vrf all route external-1 +show ipv6 ospf6 vrf all route external-2 +show ipv6 ospf6 vrf all route inter-area +show ipv6 ospf6 vrf all route intra-area +show ipv6 ospf6 vrf all route detail +show ipv6 ospf6 vrf all route summary +show ipv6 ospf6 vrf all spf tree +show ipv6 ospf6 vrf all summary-address detail +show ipv6 ospf6 zebra +CMD_LIST_END diff --git a/tools/frr-reload.py b/tools/frr-reload.py index 9d41305ec3..7c6a83a51d 100755 --- a/tools/frr-reload.py +++ b/tools/frr-reload.py @@ -513,9 +513,6 @@ class Config(object): Parse the configuration and create contexts for each appropriate block """ - current_context_lines = [] - ctx_keys = [] - """ The end of a context is flagged via the 'end' keyword: @@ -574,42 +571,80 @@ end # key of the context. So "router bgp 10" is the key for the non-address # family part of bgp, "router bgp 10, address-family ipv6 unicast" is # the key for the subcontext and so on. + + # This dictionary contains a tree of all commands that we know start a + # new multi-line context. All other commands are treated either as + # commands inside a multi-line context or as single-line contexts. This + # dictionary should be updated whenever a new node is added to FRR. + ctx_keywords = { + "router bgp ": { + "address-family ": { + "vni ": {}, + }, + "vnc ": {}, + "vrf-policy ": {}, + "bmp ": {}, + "segment-routing srv6": {}, + }, + "router rip": {}, + "router ripng": {}, + "router isis ": {}, + "router openfabric ": {}, + "router ospf": {}, + "router ospf6": {}, + "router eigrp ": {}, + "router babel": {}, + "mpls ldp": { + "address-family ": { + "interface ": {} + } + }, + "l2vpn ": { + "member pseudowire ": {} + }, + "key chain ": { + "key ": {} + }, + "vrf ": {}, + "interface ": { + "link-params": {} + }, + "pseudowire ": {}, + "segment-routing": { + "traffic-eng": { + "segment-list ": {}, + "policy ": { + "candidate-path ": {} + }, + "pcep": { + "pcc": {}, + "pce ": {}, + "pce-config ": {} + } + }, + "srv6": { + "locators": { + "locator ": {} + } + } + }, + "nexthop-group ": {}, + "route-map ": {}, + "pbr-map ": {}, + "rpki": {}, + "bfd": { + "peer ": {}, + "profile ": {} + }, + "line vty": {} + } + + # stack of context keys ctx_keys = [] - main_ctx_key = [] - new_ctx = True - - # the keywords that we know are single line contexts. bgp in this case - # is not the main router bgp block, but enabling multi-instance - oneline_ctx_keywords = ( - "access-list ", - "agentx", - "allow-external-route-update", - "bgp ", - "debug ", - "domainname ", - "dump ", - "enable ", - "frr ", - "fpm ", - "hostname ", - "ip ", - "ipv6 ", - "log ", - "mac access-list ", - "mpls lsp", - "mpls label", - "no ", - "password ", - "pbr ", - "ptm-enable", - "router-id ", - "service ", - "table ", - "username ", - "zebra ", - "vrrp autoconfigure", - "evpn mh", - ) + # stack of context keywords + cur_ctx_keywords = [ctx_keywords] + # list of stored commands + cur_ctx_lines = [] for line in self.lines: @@ -619,357 +654,77 @@ end if line.startswith("!") or line.startswith("#"): continue - if ( - len(ctx_keys) == 2 - and ctx_keys[0].startswith("bfd") - and ctx_keys[1].startswith("profile ") - and line == "end" - ): - log.debug("LINE %-50s: popping from sub context, %-50s", line, ctx_keys) - - if main_ctx_key: - self.save_contexts(ctx_keys, current_context_lines) - ctx_keys = copy.deepcopy(main_ctx_key) - current_context_lines = [] + if line.startswith("exit"): + # ignore on top level + if len(ctx_keys) == 0: continue - # one line contexts - # there is one exception though: ldpd accepts a 'router-id' clause - # as part of its 'mpls ldp' config context. If we are processing - # ldp configuration and encounter a router-id we should NOT switch - # to a new context - if ( - new_ctx is True - and any(line.startswith(keyword) for keyword in oneline_ctx_keywords) - and not ( - ctx_keys - and ctx_keys[0].startswith("mpls ldp") - and line.startswith("router-id ") - ) - ): - self.save_contexts(ctx_keys, current_context_lines) - - # Start a new context - main_ctx_key = [] - ctx_keys = [ - line, - ] - current_context_lines = [] - - log.debug("LINE %-50s: entering new context, %-50s", line, ctx_keys) - self.save_contexts(ctx_keys, current_context_lines) - new_ctx = True - - elif line == "end": - self.save_contexts(ctx_keys, current_context_lines) - log.debug("LINE %-50s: exiting old context, %-50s", line, ctx_keys) - - # Start a new context - new_ctx = True - main_ctx_key = [] - ctx_keys = [] - current_context_lines = [] - - elif line == "exit" and ctx_keys[0].startswith("rpki"): - self.save_contexts(ctx_keys, current_context_lines) - log.debug("LINE %-50s: exiting old context, %-50s", line, ctx_keys) - - # Start a new context - new_ctx = True - main_ctx_key = [] - ctx_keys = [] - current_context_lines = [] - - elif line == "exit-vrf": - self.save_contexts(ctx_keys, current_context_lines) - current_context_lines.append(line) - log.debug( - "LINE %-50s: append to current_context_lines, %-50s", line, ctx_keys - ) + # save current context + self.save_contexts(ctx_keys, cur_ctx_lines) - # Start a new context - new_ctx = True - main_ctx_key = [] - ctx_keys = [] - current_context_lines = [] + # exit current context + log.debug("LINE %-50s: exit context %-50s", line, ctx_keys) - elif ( - line == "exit" - and len(ctx_keys) > 1 - and ctx_keys[0].startswith("segment-routing") - ): - self.save_contexts(ctx_keys, current_context_lines) - - # Start a new context - ctx_keys = ctx_keys[:-1] - current_context_lines = [] - log.debug( - "LINE %-50s: popping segment routing sub-context to ctx%-50s", - line, - ctx_keys, - ) - - elif line in ["exit-address-family", "exit", "exit-vnc"]: - # if this exit is for address-family ipv4 unicast, ignore the pop - if main_ctx_key: - self.save_contexts(ctx_keys, current_context_lines) - - # Start a new context - ctx_keys = copy.deepcopy(main_ctx_key) - current_context_lines = [] - log.debug( - "LINE %-50s: popping from subcontext to ctx%-50s", - line, - ctx_keys, - ) + ctx_keys.pop() + cur_ctx_keywords.pop() + cur_ctx_lines = [] - elif line in ["exit-vni", "exit-ldp-if"]: - if sub_main_ctx_key: - self.save_contexts(ctx_keys, current_context_lines) - - # Start a new context - ctx_keys = copy.deepcopy(sub_main_ctx_key) - current_context_lines = [] - log.debug( - "LINE %-50s: popping from sub-subcontext to ctx%-50s", - line, - ctx_keys, - ) + continue - elif new_ctx is True: - if not main_ctx_key: - ctx_keys = [ - line, - ] - else: - ctx_keys = copy.deepcopy(main_ctx_key) - main_ctx_key = [] + if line.startswith("end"): + # exit all contexts + while len(ctx_keys) > 0: + # save current context + self.save_contexts(ctx_keys, cur_ctx_lines) - current_context_lines = [] - new_ctx = False - log.debug("LINE %-50s: entering new context, %-50s", line, ctx_keys) + # exit current context + log.debug("LINE %-50s: exit context %-50s", line, ctx_keys) - elif ( - line.startswith("address-family ") - or line.startswith("vnc defaults") - or line.startswith("vnc l2-group") - or line.startswith("vnc nve-group") - or line.startswith("peer") - or line.startswith("key ") - or line.startswith("member pseudowire") - ): - main_ctx_key = [] + ctx_keys.pop() + cur_ctx_keywords.pop() + cur_ctx_lines = [] - # Save old context first - self.save_contexts(ctx_keys, current_context_lines) - current_context_lines = [] - main_ctx_key = copy.deepcopy(ctx_keys) - log.debug("LINE %-50s: entering sub-context, append to ctx_keys", line) + continue - if line == "address-family ipv6" and not ctx_keys[0].startswith( - "mpls ldp" - ): - ctx_keys.append("address-family ipv6 unicast") - elif line == "address-family ipv4" and not ctx_keys[0].startswith( - "mpls ldp" - ): - ctx_keys.append("address-family ipv4 unicast") - elif line == "address-family evpn": - ctx_keys.append("address-family l2vpn evpn") - else: + new_ctx = False + + # check if the line is a context-entering keyword + for k, v in cur_ctx_keywords[-1].items(): + if line.startswith(k): + # candidate-path is a special case. It may be a node and + # may be a single-line command. The distinguisher is the + # word "dynamic" or "explicit" at the middle of the line. + # It was perhaps not the best choice by the pathd authors + # but we have what we have. + if k == "candidate-path " and "explicit" in line: + # this is a single-line command + break + + # save current context + self.save_contexts(ctx_keys, cur_ctx_lines) + + # enter new context + new_ctx = True ctx_keys.append(line) + cur_ctx_keywords.append(v) + cur_ctx_lines = [] - elif ( - line.startswith("vni ") - and len(ctx_keys) == 2 - and ctx_keys[0].startswith("router bgp") - and ctx_keys[1] == "address-family l2vpn evpn" - ): - - # Save old context first - self.save_contexts(ctx_keys, current_context_lines) - current_context_lines = [] - sub_main_ctx_key = copy.deepcopy(ctx_keys) - log.debug( - "LINE %-50s: entering sub-sub-context, append to ctx_keys", line - ) - ctx_keys.append(line) - - elif ( - line.startswith("interface ") - and len(ctx_keys) == 2 - and ctx_keys[0].startswith("mpls ldp") - and ctx_keys[1].startswith("address-family") - ): - - # Save old context first - self.save_contexts(ctx_keys, current_context_lines) - current_context_lines = [] - sub_main_ctx_key = copy.deepcopy(ctx_keys) - log.debug( - "LINE %-50s: entering sub-sub-context, append to ctx_keys", line - ) - ctx_keys.append(line) - - elif ( - line.startswith("traffic-eng") - and len(ctx_keys) == 1 - and ctx_keys[0].startswith("segment-routing") - ): - - # Save old context first - self.save_contexts(ctx_keys, current_context_lines) - current_context_lines = [] - log.debug( - "LINE %-50s: entering segment routing sub-context, append to ctx_keys", - line, - ) - ctx_keys.append(line) - - elif ( - line.startswith("segment-list ") - and len(ctx_keys) == 2 - and ctx_keys[0].startswith("segment-routing") - and ctx_keys[1].startswith("traffic-eng") - ): - - # Save old context first - self.save_contexts(ctx_keys, current_context_lines) - current_context_lines = [] - log.debug( - "LINE %-50s: entering segment routing sub-context, append to ctx_keys", - line, - ) - ctx_keys.append(line) - - elif ( - line.startswith("policy ") - and len(ctx_keys) == 2 - and ctx_keys[0].startswith("segment-routing") - and ctx_keys[1].startswith("traffic-eng") - ): - - # Save old context first - self.save_contexts(ctx_keys, current_context_lines) - current_context_lines = [] - log.debug( - "LINE %-50s: entering segment routing sub-context, append to ctx_keys", - line, - ) - ctx_keys.append(line) - - elif ( - line.startswith("candidate-path ") - and line.endswith(" dynamic") - and len(ctx_keys) == 3 - and ctx_keys[0].startswith("segment-routing") - and ctx_keys[1].startswith("traffic-eng") - and ctx_keys[2].startswith("policy") - ): - - # Save old context first - self.save_contexts(ctx_keys, current_context_lines) - current_context_lines = [] - main_ctx_key = copy.deepcopy(ctx_keys) - log.debug( - "LINE %-50s: entering candidate-path sub-context, append to ctx_keys", - line, - ) - ctx_keys.append(line) - - elif ( - line.startswith("pcep") - and len(ctx_keys) == 2 - and ctx_keys[0].startswith("segment-routing") - and ctx_keys[1].startswith("traffic-eng") - ): - - # Save old context first - self.save_contexts(ctx_keys, current_context_lines) - current_context_lines = [] - main_ctx_key = copy.deepcopy(ctx_keys) - log.debug( - "LINE %-50s: entering pcep sub-context, append to ctx_keys", line - ) - ctx_keys.append(line) - - elif ( - line.startswith("pce-config ") - and len(ctx_keys) == 3 - and ctx_keys[0].startswith("segment-routing") - and ctx_keys[1].startswith("traffic-eng") - and ctx_keys[2].startswith("pcep") - ): - - # Save old context first - self.save_contexts(ctx_keys, current_context_lines) - current_context_lines = [] - main_ctx_key = copy.deepcopy(ctx_keys) - log.debug( - "LINE %-50s: entering pce-config sub-context, append to ctx_keys", - line, - ) - ctx_keys.append(line) - - elif ( - line.startswith("pce ") - and len(ctx_keys) == 3 - and ctx_keys[0].startswith("segment-routing") - and ctx_keys[1].startswith("traffic-eng") - and ctx_keys[2].startswith("pcep") - ): - - # Save old context first - self.save_contexts(ctx_keys, current_context_lines) - current_context_lines = [] - main_ctx_key = copy.deepcopy(ctx_keys) - log.debug( - "LINE %-50s: entering pce sub-context, append to ctx_keys", line - ) - ctx_keys.append(line) - - elif ( - line.startswith("pcc") - and len(ctx_keys) == 3 - and ctx_keys[0].startswith("segment-routing") - and ctx_keys[1].startswith("traffic-eng") - and ctx_keys[2].startswith("pcep") - ): - - # Save old context first - self.save_contexts(ctx_keys, current_context_lines) - current_context_lines = [] - main_ctx_key = copy.deepcopy(ctx_keys) - log.debug( - "LINE %-50s: entering pcc sub-context, append to ctx_keys", line - ) - ctx_keys.append(line) - - elif ( - line.startswith("profile ") - and len(ctx_keys) == 1 - and ctx_keys[0].startswith("bfd") - ): + log.debug("LINE %-50s: enter context %-50s", line, ctx_keys) + break - # Save old context first - self.save_contexts(ctx_keys, current_context_lines) - current_context_lines = [] - main_ctx_key = copy.deepcopy(ctx_keys) - log.debug( - "LINE %-50s: entering BFD profile sub-context, append to ctx_keys", - line, - ) - ctx_keys.append(line) + if new_ctx: + continue + if len(ctx_keys) == 0: + log.debug("LINE %-50s: single-line context", line) + self.save_contexts([line], []) else: - # Continuing in an existing context, add non-commented lines to it - current_context_lines.append(line) - log.debug( - "LINE %-50s: append to current_context_lines, %-50s", line, ctx_keys - ) + log.debug("LINE %-50s: add to current context %-50s", line, ctx_keys) + cur_ctx_lines.append(line) # Save the context of the last one - self.save_contexts(ctx_keys, current_context_lines) + if len(ctx_keys) > 0: + self.save_contexts(ctx_keys, cur_ctx_lines) def lines_to_config(ctx_keys, line, delete): diff --git a/tools/releasedate.py b/tools/releasedate.py new file mode 100644 index 0000000000..37780501c3 --- /dev/null +++ b/tools/releasedate.py @@ -0,0 +1,59 @@ +#!/usr/bin/python3 +# +# print FRR release schedule dates + +from datetime import datetime, date, timedelta + +w2 = timedelta(days=14) + + +def year_gen(year): + for month in [3, 7, 11]: + d = date(year, month, 1) + if d.weekday() == 0: + d += timedelta(days=1) + elif d.weekday() >= 2: + d += timedelta(days=8 - d.weekday()) + yield d + + +def calc(refdate): + year = refdate.year + + prev = list(year_gen(year - 1))[-1] + releases = list(year_gen(year)) + list(year_gen(year + 1)) + + while refdate > releases[0]: + prev = releases.pop(0) + + return (prev, releases) + + +if __name__ == "__main__": + now = datetime.now().date() + last, upcoming = calc(now) + + print("Last release was (scheduled) on %s" % last.isoformat()) + + rel = upcoming.pop(0) + freeze, rc1, rc2 = rel - w2 * 3, rel - w2 * 2, rel - w2 + + if now == rel: + print("It's release day! 🎉") + elif now >= rc2: + print( + "%d days until release! (rc2 since %s)" + % ((rel - now).days, rc2.isoformat()) + ) + elif now >= rc1: + print("%d days until rc2. (rc1 since %s)" % ((rc2 - now).days, rc1.isoformat())) + elif now >= freeze: + print( + "%d days until rc1, master is frozen since %s" + % ((rc1 - now).days, freeze.isoformat()) + ) + else: + print( + "%d days of hacking time left! (Freeze on %s)" + % ((freeze - now).days, freeze.isoformat()) + ) diff --git a/vrrpd/vrrp_main.c b/vrrpd/vrrp_main.c index a5ad37aa0c..990fa9e382 100644 --- a/vrrpd/vrrp_main.c +++ b/vrrpd/vrrp_main.c @@ -148,7 +148,6 @@ int main(int argc, char **argv, char **envp) break; default: frr_help_exit(1); - break; } } diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index 1904e936cc..91ff6fe28e 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -762,7 +762,7 @@ void vrrp_vty_init(void) { install_node(&debug_node); install_node(&vrrp_node); - vrf_cmd_init(NULL, &vrrp_privs); + vrf_cmd_init(NULL); if_cmd_init(vrrp_config_write_interface); install_element(VIEW_NODE, &vrrp_vrid_show_cmd); diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index 5cee0aaa3f..b47cca76f5 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -40,7 +40,6 @@ #include "vtysh/vtysh.h" #include "vtysh/vtysh_daemons.h" #include "log.h" -#include "ns.h" #include "vrf.h" #include "libfrr.h" #include "command_graph.h" @@ -510,51 +509,8 @@ static int vtysh_execute_func(const char *line, int pager) */ if (ret == CMD_SUCCESS || ret == CMD_SUCCESS_DAEMON || ret == CMD_WARNING) { - if ((saved_node == BGP_VPNV4_NODE - || saved_node == BGP_VPNV6_NODE - || saved_node == BGP_IPV4_NODE - || saved_node == BGP_IPV6_NODE - || saved_node == BGP_FLOWSPECV4_NODE - || saved_node == BGP_FLOWSPECV6_NODE - || saved_node == BGP_IPV4M_NODE - || saved_node == BGP_IPV4L_NODE - || saved_node == BGP_IPV6L_NODE - || saved_node == BGP_IPV6M_NODE - || saved_node == BGP_EVPN_NODE - || saved_node == LDP_IPV4_NODE - || saved_node == LDP_IPV6_NODE) - && (tried == 1)) { - vtysh_execute("exit-address-family"); - } else if ((saved_node == BGP_EVPN_VNI_NODE) && (tried == 1)) { - vtysh_execute("exit-vni"); - } else if (saved_node == BGP_VRF_POLICY_NODE && (tried == 1)) { - vtysh_execute("exit-vrf-policy"); - } else if ((saved_node == BGP_VNC_DEFAULTS_NODE - || saved_node == BGP_VNC_NVE_GROUP_NODE - || saved_node == BGP_VNC_L2_GROUP_NODE) - && (tried == 1)) { - vtysh_execute("exit-vnc"); - } else if (saved_node == VRF_NODE && (tried == 1)) { - vtysh_execute("exit-vrf"); - } else if ((saved_node == KEYCHAIN_KEY_NODE - || saved_node == LDP_PSEUDOWIRE_NODE - || saved_node == LDP_IPV4_IFACE_NODE - || saved_node == LDP_IPV6_IFACE_NODE) - && (tried == 1)) { + while (tried-- > 0) vtysh_execute("exit"); - } else if ((saved_node == SR_SEGMENT_LIST_NODE - || saved_node == SR_POLICY_NODE - || saved_node == SR_CANDIDATE_DYN_NODE - || saved_node == PCEP_NODE - || saved_node == PCEP_PCE_CONFIG_NODE - || saved_node == PCEP_PCE_NODE - || saved_node == PCEP_PCC_NODE) - && (tried > 0)) { - vtysh_execute("exit"); - } else if (tried) { - vtysh_execute("end"); - vtysh_execute("configure"); - } } /* * If command didn't succeed in any node, continue with return value @@ -702,7 +658,6 @@ int vtysh_mark_file(const char *filename) int ret; vector vline; int tried = 0; - bool ending; const struct cmd_element *cmd; int saved_ret, prev_node; int lineno = 0; @@ -735,35 +690,6 @@ int vtysh_mark_file(const char *filename) strlcpy(vty_buf_copy, vty->buf, VTY_BUFSIZ); vty_buf_trimmed = trim(vty_buf_copy); - switch (vty->node) { - case LDP_IPV4_IFACE_NODE: - if (strncmp(vty_buf_copy, " ", 3)) { - vty_out(vty, " exit-ldp-if\n"); - vty->node = LDP_IPV4_NODE; - } - break; - case LDP_IPV6_IFACE_NODE: - if (strncmp(vty_buf_copy, " ", 3)) { - vty_out(vty, " exit-ldp-if\n"); - vty->node = LDP_IPV6_NODE; - } - break; - case LDP_PSEUDOWIRE_NODE: - if (strncmp(vty_buf_copy, " ", 2)) { - vty_out(vty, " exit\n"); - vty->node = LDP_L2VPN_NODE; - } - break; - case SR_CANDIDATE_DYN_NODE: - if (strncmp(vty_buf_copy, " ", 2)) { - vty_out(vty, " exit\n"); - vty->node = SR_POLICY_NODE; - } - break; - default: - break; - } - if (vty_buf_trimmed[0] == '!' || vty_buf_trimmed[0] == '#') { vty_out(vty, "%s", vty->buf); continue; @@ -811,56 +737,8 @@ int vtysh_mark_file(const char *filename) */ if (ret == CMD_SUCCESS || ret == CMD_SUCCESS_DAEMON || ret == CMD_WARNING) { - if ((prev_node == BGP_VPNV4_NODE - || prev_node == BGP_VPNV6_NODE - || prev_node == BGP_IPV4_NODE - || prev_node == BGP_IPV6_NODE - || prev_node == BGP_FLOWSPECV4_NODE - || prev_node == BGP_FLOWSPECV6_NODE - || prev_node == BGP_IPV4L_NODE - || prev_node == BGP_IPV6L_NODE - || prev_node == BGP_IPV4M_NODE - || prev_node == BGP_IPV6M_NODE - || prev_node == BGP_EVPN_NODE) - && (tried == 1)) { - vty_out(vty, "exit-address-family\n"); - } else if ((prev_node == BGP_EVPN_VNI_NODE) - && (tried == 1)) { - vty_out(vty, "exit-vni\n"); - } else if ((prev_node == KEYCHAIN_KEY_NODE) - && (tried == 1)) { - vty_out(vty, "exit\n"); - } else if ((prev_node == BFD_PEER_NODE) - && (tried == 1)) { + while (tried-- > 0) vty_out(vty, "exit\n"); - } else if (((prev_node == SEGMENT_ROUTING_NODE) - || (prev_node == SR_TRAFFIC_ENG_NODE) - || (prev_node == SR_SEGMENT_LIST_NODE) - || (prev_node == SR_POLICY_NODE) - || (prev_node == SR_CANDIDATE_DYN_NODE) - || (prev_node == PCEP_NODE) - || (prev_node == PCEP_PCE_CONFIG_NODE) - || (prev_node == PCEP_PCE_NODE) - || (prev_node == PCEP_PCC_NODE)) - && (tried > 0)) { - ending = (vty->node != SEGMENT_ROUTING_NODE) - && (vty->node != SR_TRAFFIC_ENG_NODE) - && (vty->node != SR_SEGMENT_LIST_NODE) - && (vty->node != SR_POLICY_NODE) - && (vty->node != SR_CANDIDATE_DYN_NODE) - && (vty->node != PCEP_NODE) - && (vty->node != PCEP_PCE_CONFIG_NODE) - && (vty->node != PCEP_PCE_NODE) - && (vty->node != PCEP_PCC_NODE); - if (ending) - tried--; - while (tried-- > 0) - vty_out(vty, "exit\n"); - if (ending) - vty_out(vty, "end\n"); - } else if (tried) { - vty_out(vty, "end\n"); - } } /* * If command didn't succeed in any node, continue with return @@ -1691,7 +1569,7 @@ DEFUNSH(VTYSH_REALLYALL, vtysh_end_all, vtysh_end_all_cmd, "end", return vtysh_end(); } -DEFUNSH(VTYSH_SR, srv6, srv6_cmd, +DEFUNSH(VTYSH_ZEBRA, srv6, srv6_cmd, "srv6", "Segment-Routing SRv6 configration\n") { @@ -1699,7 +1577,7 @@ DEFUNSH(VTYSH_SR, srv6, srv6_cmd, return CMD_SUCCESS; } -DEFUNSH(VTYSH_SR, srv6_locators, srv6_locators_cmd, +DEFUNSH(VTYSH_ZEBRA, srv6_locators, srv6_locators_cmd, "locators", "Segment-Routing SRv6 locators configration\n") { @@ -1707,7 +1585,7 @@ DEFUNSH(VTYSH_SR, srv6_locators, srv6_locators_cmd, return CMD_SUCCESS; } -DEFUNSH(VTYSH_SR, srv6_locator, srv6_locator_cmd, +DEFUNSH(VTYSH_ZEBRA, srv6_locator, srv6_locator_cmd, "locator WORD", "Segment Routing SRv6 locator\n" "Specify locator-name\n") @@ -2237,8 +2115,7 @@ DEFUNSH(VTYSH_PATHD, pcep, pcep_cmd, } DEFUNSH(VTYSH_PATHD, pcep_cli_pcc, pcep_cli_pcc_cmd, - "[no] pcc", - NO_STR + "pcc", "PCC configuration\n") { vty->node = PCEP_PCC_NODE; @@ -2246,8 +2123,7 @@ DEFUNSH(VTYSH_PATHD, pcep_cli_pcc, pcep_cli_pcc_cmd, } DEFUNSH(VTYSH_PATHD, pcep_cli_pce, pcep_cli_pce_cmd, - "[no] pce WORD", - NO_STR + "pce WORD", "PCE configuration\n" "Peer name\n") { @@ -2256,8 +2132,7 @@ DEFUNSH(VTYSH_PATHD, pcep_cli_pce, pcep_cli_pce_cmd, } DEFUNSH(VTYSH_PATHD, pcep_cli_pcep_pce_config, pcep_cli_pcep_pce_config_cmd, - "[no] pce-config WORD", - NO_STR + "pce-config WORD", "PCEP peer Configuration Group\n" "PCEP peer Configuration Group name\n") { @@ -2456,7 +2331,7 @@ DEFUNSH(VTYSH_VRF, exit_vrf_config, exit_vrf_config_cmd, "exit-vrf", return CMD_SUCCESS; } -DEFUNSH(VTYSH_SR, exit_srv6_config, exit_srv6_config_cmd, "exit", +DEFUNSH(VTYSH_ZEBRA, exit_srv6_config, exit_srv6_config_cmd, "exit", "Exit from SRv6 configuration mode\n") { if (vty->node == SRV6_NODE) @@ -2464,7 +2339,7 @@ DEFUNSH(VTYSH_SR, exit_srv6_config, exit_srv6_config_cmd, "exit", return CMD_SUCCESS; } -DEFUNSH(VTYSH_SR, exit_srv6_locs_config, exit_srv6_locs_config_cmd, "exit", +DEFUNSH(VTYSH_ZEBRA, exit_srv6_locs_config, exit_srv6_locs_config_cmd, "exit", "Exit from SRv6-locator configuration mode\n") { if (vty->node == SRV6_LOCS_NODE) @@ -2472,7 +2347,7 @@ DEFUNSH(VTYSH_SR, exit_srv6_locs_config, exit_srv6_locs_config_cmd, "exit", return CMD_SUCCESS; } -DEFUNSH(VTYSH_SR, exit_srv6_loc_config, exit_srv6_loc_config_cmd, "exit", +DEFUNSH(VTYSH_ZEBRA, exit_srv6_loc_config, exit_srv6_loc_config_cmd, "exit", "Exit from SRv6-locators configuration mode\n") { if (vty->node == SRV6_LOC_NODE) @@ -2746,17 +2621,6 @@ DEFUNSH(VTYSH_VRF, vtysh_vrf, vtysh_vrf_cmd, "vrf NAME", return CMD_SUCCESS; } -DEFSH(VTYSH_ZEBRA, vtysh_vrf_netns_cmd, - "netns NAME", - "Attach VRF to a Namespace\n" - "The file name in " NS_RUN_DIR ", or a full pathname\n") - -DEFSH(VTYSH_ZEBRA, vtysh_no_vrf_netns_cmd, - "no netns [NAME]", - NO_STR - "Detach VRF from a Namespace\n" - "The file name in " NS_RUN_DIR ", or a full pathname\n") - DEFUNSH(VTYSH_VRF, vtysh_exit_vrf, vtysh_exit_vrf_cmd, "exit", "Exit current mode and down to previous mode\n") { @@ -4474,8 +4338,6 @@ void vtysh_init_vty(void) install_node(&vrf_node); install_element(CONFIG_NODE, &vtysh_vrf_cmd); - install_element(VRF_NODE, &vtysh_vrf_netns_cmd); - install_element(VRF_NODE, &vtysh_no_vrf_netns_cmd); install_element(VRF_NODE, &exit_vrf_config_cmd); install_element(VRF_NODE, &vtysh_end_all_cmd); install_element(VRF_NODE, &vtysh_exit_vrf_cmd); diff --git a/vtysh/vtysh_config.c b/vtysh/vtysh_config.c index d22ec3113f..2e1d7c5bad 100644 --- a/vtysh/vtysh_config.c +++ b/vtysh/vtysh_config.c @@ -272,16 +272,11 @@ void vtysh_config_parse_line(void *arg, const char *line) strlen(" ip igmp query-interval")) == 0) { config_add_line_uniq_end(config->line, line); } else if (config->index == LINK_PARAMS_NODE - && strncmp(line, " exit-link-params", - strlen(" exit")) + && strncmp(line, " exit-link-params", + strlen(" exit")) == 0) { config_add_line(config->line, line); config->index = INTERFACE_NODE; - } else if (config->index == VRF_NODE - && strncmp(line, " exit-vrf", - strlen(" exit-vrf")) - == 0) { - config_add_line_uniq_end(config->line, line); } else if (!strncmp(line, " vrrp", strlen(" vrrp")) || !strncmp(line, " no vrrp", strlen(" no vrrp"))) { @@ -300,7 +295,10 @@ void vtysh_config_parse_line(void *arg, const char *line) config_add_line(config_top, line); break; default: - if (strncmp(line, "interface", strlen("interface")) == 0) + if (strncmp(line, "exit", strlen("exit")) == 0) { + if (config) + config_add_line_uniq_end(config->line, line); + } else if (strncmp(line, "interface", strlen("interface")) == 0) config = config_get(INTERFACE_NODE, line); else if (strncmp(line, "pseudowire", strlen("pseudowire")) == 0) config = config_get(PW_NODE, line); @@ -496,7 +494,9 @@ void vtysh_config_dump(void) * are not under the VRF node. */ if (config->index == INTERFACE_NODE - && list_isempty(config->line)) { + && (listcount(config->line) == 1) + && (line = listnode_head(config->line)) + && strmatch(line, "exit")) { config_del(config); continue; } diff --git a/yang/frr-filter.yang b/yang/frr-filter.yang index 9a864213ee..46a9100ab2 100644 --- a/yang/frr-filter.yang +++ b/yang/frr-filter.yang @@ -145,6 +145,7 @@ module frr-filter { leaf ipv4-prefix { description "Configure IPv4 prefix to match"; type inet:ipv4-prefix; + mandatory true; } leaf ipv4-exact-match { @@ -216,6 +217,7 @@ module frr-filter { leaf ipv6-prefix { description "Configure IPv6 prefix to match"; type inet:ipv6-prefix; + mandatory true; } leaf ipv6-exact-match { @@ -277,7 +279,7 @@ module frr-filter { key "sequence"; leaf sequence { - description "Access list sequence value"; + description "Prefix list sequence value"; type access-list-sequence; } @@ -295,6 +297,7 @@ module frr-filter { leaf ipv4-prefix { description "Configure IPv4 prefix to match"; type inet:ipv4-prefix; + mandatory true; } leaf ipv4-prefix-length-greater-or-equal { @@ -319,6 +322,7 @@ module frr-filter { leaf ipv6-prefix { description "Configure IPv6 prefix to match"; type inet:ipv6-prefix; + mandatory true; } leaf ipv6-prefix-length-greater-or-equal { diff --git a/yang/frr-igmp.yang b/yang/frr-igmp.yang index e2971dc5cf..8d151e430d 100644 --- a/yang/frr-igmp.yang +++ b/yang/frr-igmp.yang @@ -84,9 +84,10 @@ module frr-igmp { leaf query-interval { type uint16 { - range "1..1800"; + range "1..max"; } units seconds; + must ". * 10 >= ../query-max-response-time"; default "125"; description "The Query Interval is the interval between General Queries @@ -94,10 +95,11 @@ module frr-igmp { } leaf query-max-response-time { - type uint8 { - range "10..250"; + type uint16 { + range "1..max"; } units deciseconds; + must ". <= ../query-interval * 10"; default "100"; description "Query maximum response time specifies the maximum time @@ -105,8 +107,8 @@ module frr-igmp { } leaf last-member-query-interval { - type uint8 { - range "1..255"; + type uint16 { + range "1..max"; } units deciseconds; default "10"; @@ -117,7 +119,7 @@ module frr-igmp { leaf robustness-variable { type uint8 { - range "1..7"; + range "1..max"; } default "2"; description diff --git a/yang/frr-pim.yang b/yang/frr-pim.yang index e846ffa1f8..109ce22309 100644 --- a/yang/frr-pim.yang +++ b/yang/frr-pim.yang @@ -97,7 +97,7 @@ module frr-pim { leaf keep-alive-timer { type uint16 { - range "31..60000"; + range "1..max"; } default "210"; description @@ -106,7 +106,7 @@ module frr-pim { leaf rp-keep-alive-timer { type uint16 { - range "31..60000"; + range "1..max"; } default "210"; description @@ -116,36 +116,34 @@ module frr-pim { grouping msdp-timers { leaf hold-time { - type uint32 { - range 3..600; + type uint16 { + range "1..max"; } units seconds; default 75; description "Hold period is started at the MSDP peer connection establishment and is reset every new message. When the period expires the - connection is closed. - - This value needs to be greater than `keep-alive-period`."; + connection is closed. This value should be greater than the + remote keep-alive time."; } leaf keep-alive { - type uint32 { - range 2..600; + type uint16 { + range "1..max"; } units seconds; default 60; description "To maintain a connection established it is necessary to send keep alive messages in a certain frequency and this allows its - configuration. - - This value needs to be lesser than `hold-time-period`."; + configuration. This value should be less than the remote + hold time."; } leaf connection-retry { - type uint32 { - range 1..600; + type uint16 { + range "1..max"; } units seconds; default 30; @@ -343,7 +341,7 @@ module frr-pim { leaf hello-interval { type uint8 { - range "1..180"; + range "1..max"; } default "30"; description @@ -352,7 +350,7 @@ module frr-pim { leaf hello-holdtime { type uint16 { - range "1..630"; + range "1..max"; } must ". > ./../hello-interval" { error-message "HoldTime must be greater than Hello"; @@ -367,7 +365,7 @@ module frr-pim { leaf min-rx-interval { type uint16 { - range "50..60000"; + range "1..max"; } default "300"; description @@ -376,7 +374,7 @@ module frr-pim { leaf min-tx-interval { type uint16 { - range "50..60000"; + range "1..max"; } default "300"; description @@ -422,7 +420,7 @@ module frr-pim { leaf dr-priority { type uint32 { - range "1..4294967295"; + range "1..max"; } default 1; description @@ -521,7 +519,7 @@ module frr-pim { "PIM router parameters."; leaf packets { type uint8 { - range "1..100"; + range "1..max"; } default "3"; description @@ -529,7 +527,7 @@ module frr-pim { } leaf join-prune-interval { type uint16 { - range "5..600"; + range "1..max"; } default "60"; description @@ -537,7 +535,7 @@ module frr-pim { } leaf register-suppress-time { type uint16 { - range "5..60000"; + range "1..max"; } default "60"; description diff --git a/zebra/debug.c b/zebra/debug.c index 88a3d98815..525180d4ee 100644 --- a/zebra/debug.c +++ b/zebra/debug.c @@ -244,6 +244,7 @@ DEFUN (debug_zebra_kernel, return CMD_SUCCESS; } +#if defined(HAVE_NETLINK) DEFUN (debug_zebra_kernel_msgdump, debug_zebra_kernel_msgdump_cmd, "debug zebra kernel msgdump [<recv|send>]", @@ -267,6 +268,7 @@ DEFUN (debug_zebra_kernel_msgdump, return CMD_SUCCESS; } +#endif DEFUN (debug_zebra_rib, debug_zebra_rib_cmd, @@ -465,6 +467,7 @@ DEFUN (no_debug_zebra_kernel, return CMD_SUCCESS; } +#if defined(HAVE_NETLINK) DEFUN (no_debug_zebra_kernel_msgdump, no_debug_zebra_kernel_msgdump_cmd, "no debug zebra kernel msgdump [<recv|send>]", @@ -489,6 +492,7 @@ DEFUN (no_debug_zebra_kernel_msgdump, return CMD_SUCCESS; } +#endif DEFUN (no_debug_zebra_rib, no_debug_zebra_rib_cmd, @@ -721,7 +725,9 @@ void zebra_debug_init(void) install_element(ENABLE_NODE, &debug_zebra_pw_cmd); install_element(ENABLE_NODE, &debug_zebra_packet_cmd); install_element(ENABLE_NODE, &debug_zebra_kernel_cmd); +#if defined(HAVE_NETLINK) install_element(ENABLE_NODE, &debug_zebra_kernel_msgdump_cmd); +#endif install_element(ENABLE_NODE, &debug_zebra_rib_cmd); install_element(ENABLE_NODE, &debug_zebra_fpm_cmd); install_element(ENABLE_NODE, &debug_zebra_dplane_cmd); @@ -734,7 +740,9 @@ void zebra_debug_init(void) install_element(ENABLE_NODE, &no_debug_zebra_vxlan_cmd); install_element(ENABLE_NODE, &no_debug_zebra_packet_cmd); install_element(ENABLE_NODE, &no_debug_zebra_kernel_cmd); +#if defined(HAVE_NETLINK) install_element(ENABLE_NODE, &no_debug_zebra_kernel_msgdump_cmd); +#endif install_element(ENABLE_NODE, &no_debug_zebra_rib_cmd); install_element(ENABLE_NODE, &no_debug_zebra_fpm_cmd); install_element(ENABLE_NODE, &no_debug_zebra_dplane_cmd); @@ -748,7 +756,9 @@ void zebra_debug_init(void) install_element(CONFIG_NODE, &debug_zebra_pw_cmd); install_element(CONFIG_NODE, &debug_zebra_packet_cmd); install_element(CONFIG_NODE, &debug_zebra_kernel_cmd); +#if defined(HAVE_NETLINK) install_element(CONFIG_NODE, &debug_zebra_kernel_msgdump_cmd); +#endif install_element(CONFIG_NODE, &debug_zebra_rib_cmd); install_element(CONFIG_NODE, &debug_zebra_fpm_cmd); install_element(CONFIG_NODE, &debug_zebra_dplane_cmd); @@ -761,7 +771,9 @@ void zebra_debug_init(void) install_element(CONFIG_NODE, &no_debug_zebra_vxlan_cmd); install_element(CONFIG_NODE, &no_debug_zebra_packet_cmd); install_element(CONFIG_NODE, &no_debug_zebra_kernel_cmd); +#if defined(HAVE_NETLINK) install_element(CONFIG_NODE, &no_debug_zebra_kernel_msgdump_cmd); +#endif install_element(CONFIG_NODE, &no_debug_zebra_rib_cmd); install_element(CONFIG_NODE, &no_debug_zebra_fpm_cmd); install_element(CONFIG_NODE, &no_debug_zebra_dplane_cmd); diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index a51e0b82cb..2a9fff2666 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -1527,6 +1527,14 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) return -1; name = (char *)RTA_DATA(tb[IFLA_IFNAME]); + /* Must be valid string. */ + len = RTA_PAYLOAD(tb[IFLA_IFNAME]); + if (len < 2 || name[len - 1] != '\0') { + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("%s: invalid intf name", __func__); + return -1; + } + if (tb[IFLA_LINKINFO]) { netlink_parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb[IFLA_LINKINFO]); diff --git a/zebra/interface.c b/zebra/interface.c index 21eeb20543..18f7503f82 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -4096,7 +4096,7 @@ static int link_params_config_write(struct vty *vty, struct interface *ifp) if (IS_PARAM_SET(iflp, LP_RMT_AS)) vty_out(vty, " neighbor %pI4 as %u\n", &iflp->rmt_ip, iflp->rmt_as); - vty_out(vty, " exit-link-params\n"); + vty_out(vty, " exit-link-params\n"); return 0; } @@ -4188,7 +4188,7 @@ static int if_config_write(struct vty *vty) zebra_evpn_mh_if_write(vty, ifp); link_params_config_write(vty, ifp); - vty_endframe(vty, "!\n"); + vty_endframe(vty, "exit\n!\n"); } return 0; } diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c index 011883649d..effec24c1f 100644 --- a/zebra/kernel_netlink.c +++ b/zebra/kernel_netlink.c @@ -350,21 +350,9 @@ static int netlink_information_fetch(struct nlmsghdr *h, ns_id_t ns_id, case RTM_DELADDR: return netlink_interface_addr(h, ns_id, startup); case RTM_NEWNEIGH: - return netlink_neigh_change(h, ns_id); case RTM_DELNEIGH: - return netlink_neigh_change(h, ns_id); case RTM_GETNEIGH: - /* - * Kernel in some situations when it expects - * user space to resolve arp entries, we will - * receive this notification. As we don't - * need this notification and as that - * we don't want to spam the log file with - * below messages, just ignore. - */ - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("Received RTM_GETNEIGH, ignoring"); - break; + return netlink_neigh_change(h, ns_id); case RTM_NEWRULE: return netlink_rule_change(h, ns_id, startup); case RTM_DELRULE: diff --git a/zebra/main.c b/zebra/main.c index bded50149f..6162d36b43 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -384,7 +384,6 @@ int main(int argc, char **argv) #endif /* HAVE_NETLINK */ default: frr_help_exit(1); - break; } } diff --git a/zebra/rib.h b/zebra/rib.h index 31d9dfd265..8887053a4c 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -371,9 +371,6 @@ extern void _route_entry_dump(const char *func, union prefixconstptr pp, union prefixconstptr src_pp, const struct route_entry *re); -extern void rib_lookup_and_dump(struct prefix_ipv4 *p, vrf_id_t vrf_id); -extern void rib_lookup_and_pushup(struct prefix_ipv4 *p, vrf_id_t vrf_id); - #define ZEBRA_RIB_LOOKUP_ERROR -1 #define ZEBRA_RIB_FOUND_EXACT 0 #define ZEBRA_RIB_FOUND_NOGATE 1 @@ -403,6 +400,11 @@ extern int rib_add(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, extern int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p, struct prefix_ipv6 *src_p, struct route_entry *re, struct nexthop_group *ng); +/* + * -1 -> some sort of error + * 0 -> an add + * 1 -> an update + */ extern int rib_add_multipath_nhe(afi_t afi, safi_t safi, struct prefix *p, struct prefix_ipv6 *src_p, struct route_entry *re, diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index a64ec52dda..a8b4b54d29 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -861,6 +861,12 @@ static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id, } memcpy(&src_p.prefix, src, 16); src_p.prefixlen = rtm->rtm_src_len; + } else { + /* We only handle the AFs we handle... */ + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("%s: unknown address-family %u", __func__, + rtm->rtm_family); + return 0; } /* @@ -3652,6 +3658,15 @@ static void netlink_handle_5549(struct ndmsg *ndm, struct zebra_if *zif, #define NUD_LOCAL_ACTIVE \ (NUD_PERMANENT | NUD_NOARP | NUD_REACHABLE) +static int netlink_nbr_entry_state_to_zclient(int nbr_state) +{ + /* an exact match is done between + * - netlink neighbor state values: NDM_XXX (see in linux/neighbour.h) + * - zclient neighbor state values: ZEBRA_NEIGH_STATE_XXX + * (see in lib/zclient.h) + */ + return nbr_state; +} static int netlink_ipneigh_change(struct nlmsghdr *h, int len, ns_id_t ns_id) { struct ndmsg *ndm; @@ -3741,8 +3756,10 @@ static int netlink_ipneigh_change(struct nlmsghdr *h, int len, ns_id_t ns_id) &mac, l2_len); } else sockunion_family(&link_layer_ipv4) = AF_UNSPEC; - zsend_nhrp_neighbor_notify(cmd, ifp, &ip, ndm->ndm_state, - &link_layer_ipv4); + zsend_nhrp_neighbor_notify( + cmd, ifp, &ip, + netlink_nbr_entry_state_to_zclient(ndm->ndm_state), + &link_layer_ipv4); } if (h->nlmsg_type == RTM_GETNEIGH) diff --git a/zebra/subdir.am b/zebra/subdir.am index 731f0c9ad1..c3d8a73aaa 100644 --- a/zebra/subdir.am +++ b/zebra/subdir.am @@ -19,6 +19,7 @@ vtysh_scan += \ zebra/zebra_routemap.c \ zebra/zebra_vty.c \ zebra/zserv.c \ + zebra/zebra_vrf.c \ # end # can be loaded as DSO - always include for vtysh @@ -132,6 +133,7 @@ clippy_scan += \ zebra/zebra_routemap.c \ zebra/zebra_vty.c \ zebra/zebra_srv6_vty.c \ + zebra/zebra_vrf.c \ # end noinst_HEADERS += \ diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index 27fb5d7c22..6666b3525e 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -867,7 +867,7 @@ void zsend_rule_notify_owner(const struct zebra_dplane_ctx *ctx, } void zsend_iptable_notify_owner(const struct zebra_dplane_ctx *ctx, - uint16_t note) + enum zapi_iptable_notify_owner note) { struct listnode *node; struct zserv *client; @@ -901,7 +901,8 @@ void zsend_iptable_notify_owner(const struct zebra_dplane_ctx *ctx, zserv_send_message(client, s); } -void zsend_ipset_notify_owner(const struct zebra_dplane_ctx *ctx, uint16_t note) +void zsend_ipset_notify_owner(const struct zebra_dplane_ctx *ctx, + enum zapi_ipset_notify_owner note) { struct listnode *node; struct zserv *client; @@ -936,7 +937,7 @@ void zsend_ipset_notify_owner(const struct zebra_dplane_ctx *ctx, uint16_t note) } void zsend_ipset_entry_notify_owner(const struct zebra_dplane_ctx *ctx, - uint16_t note) + enum zapi_ipset_entry_notify_owner note) { struct listnode *node; struct zserv *client; @@ -996,7 +997,8 @@ void zsend_nhrp_neighbor_notify(int cmd, struct interface *ifp, continue; s = stream_new(ZEBRA_MAX_PACKET_SIZ); - zclient_neigh_ip_encode(s, cmd, &ip, link_layer_ipv4, ifp); + zclient_neigh_ip_encode(s, cmd, &ip, link_layer_ipv4, ifp, + ndm_state); stream_putw_at(s, 0, stream_get_endp(s)); zserv_send_message(client, s); } @@ -1937,6 +1939,11 @@ static void zread_nhg_add(ZAPI_HANDLER_ARGS) flog_warn(EC_ZEBRA_NEXTHOP_CREATION_FAILED, "%s: Nexthop Group Creation failed", __func__); + + /* Free any local allocations */ + nexthop_group_delete(&nhg); + zebra_nhg_backup_free(&bnhg); + return; } @@ -2110,6 +2117,15 @@ static void zread_route_add(ZAPI_HANDLER_ARGS) ret = rib_add_multipath_nhe(afi, api.safi, &api.prefix, src_p, re, &nhe); + /* + * rib_add_multipath_nhe only fails in a couple spots + * and in those spots we have not freed memory + */ + if (ret == -1) { + client->error_cnt++; + XFREE(MTYPE_RE, re); + } + /* At this point, these allocations are not needed: 're' has been * retained or freed, and if 're' still exists, it is using * a reference to a shared group object. @@ -2121,15 +2137,15 @@ static void zread_route_add(ZAPI_HANDLER_ARGS) /* Stats */ switch (api.prefix.family) { case AF_INET: - if (ret > 0) + if (ret == 0) client->v4_route_add_cnt++; - else if (ret < 0) + else if (ret == 1) client->v4_route_upd8_cnt++; break; case AF_INET6: - if (ret > 0) + if (ret == 0) client->v6_route_add_cnt++; - else if (ret < 0) + else if (ret == 1) client->v6_route_upd8_cnt++; break; } diff --git a/zebra/zapi_msg.h b/zebra/zapi_msg.h index e991dca4f3..dad40c200d 100644 --- a/zebra/zapi_msg.h +++ b/zebra/zapi_msg.h @@ -87,11 +87,12 @@ extern void zsend_rule_notify_owner(const struct zebra_dplane_ctx *ctx, enum zapi_rule_notify_owner note); extern void zsend_iptable_notify_owner(const struct zebra_dplane_ctx *ctx, - uint16_t note); + enum zapi_iptable_notify_owner note); extern void zsend_ipset_notify_owner(const struct zebra_dplane_ctx *ctx, - uint16_t note); -extern void zsend_ipset_entry_notify_owner(const struct zebra_dplane_ctx *ctx, - uint16_t note); + enum zapi_ipset_notify_owner note); +extern void +zsend_ipset_entry_notify_owner(const struct zebra_dplane_ctx *ctx, + enum zapi_ipset_entry_notify_owner note); extern bool zserv_nexthop_num_warn(const char *caller, const struct prefix *p, const unsigned int nexthop_num); diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index 0760b2ebb3..a547a97c24 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -3495,18 +3495,6 @@ enum zebra_dplane_result dplane_intf_addr_set(const struct interface *ifp, return ZEBRA_DPLANE_REQUEST_FAILURE; } - - /* Ensure that no existing installed v4 route conflicts with - * the new interface prefix. This check must be done in the - * zebra pthread context, and any route delete (if needed) - * is enqueued before the interface address programming attempt. - */ - if (ifc->address->family == AF_INET) { - struct prefix_ipv4 *p; - - p = (struct prefix_ipv4 *)ifc->address; - rib_lookup_and_pushup(p, ifp->vrf_id); - } #endif return intf_addr_update_internal(ifp, ifc, DPLANE_OP_ADDR_INSTALL); diff --git a/zebra/zebra_evpn_mh.c b/zebra/zebra_evpn_mh.c index f44b19b781..c0cc57fc69 100644 --- a/zebra/zebra_evpn_mh.c +++ b/zebra/zebra_evpn_mh.c @@ -3920,8 +3920,18 @@ void zebra_evpn_proc_remote_nh(ZAPI_HANDLER_ARGS) struct ipaddr nh; struct ethaddr rmac; struct prefix_evpn dummy_prefix; + size_t min_len = 4 + sizeof(nh); s = msg; + + /* + * Ensure that the stream sent to us is long enough + */ + if (hdr->command == ZEBRA_EVPN_REMOTE_NH_ADD) + min_len += sizeof(rmac); + if (hdr->length < min_len) + return; + vrf_id = stream_getl(s); stream_get(&nh, s, sizeof(nh)); @@ -3929,17 +3939,20 @@ void zebra_evpn_proc_remote_nh(ZAPI_HANDLER_ARGS) dummy_prefix.family = AF_EVPN; dummy_prefix.prefixlen = (sizeof(struct evpn_addr) * 8); dummy_prefix.prefix.route_type = 1; /* XXX - fixup to type-1 def */ + dummy_prefix.prefix.ead_addr.ip.ipa_type = nh.ipa_type; if (hdr->command == ZEBRA_EVPN_REMOTE_NH_ADD) { stream_get(&rmac, s, sizeof(rmac)); if (IS_ZEBRA_DEBUG_EVPN_MH_ES) - zlog_debug("evpn remote nh %d %pIA rmac %pEA add", - vrf_id, &nh, &rmac); + zlog_debug( + "evpn remote nh %d %pIA rmac %pEA add pfx %pFX", + vrf_id, &nh, &rmac, &dummy_prefix); zebra_rib_queue_evpn_route_add(vrf_id, &rmac, &nh, (struct prefix *)&dummy_prefix); } else { if (IS_ZEBRA_DEBUG_EVPN_MH_ES) - zlog_debug("evpn remote nh %d %pIA del", vrf_id, &nh); + zlog_debug("evpn remote nh %d %pIA del pfx %pFX", + vrf_id, &nh, &dummy_prefix); zebra_rib_queue_evpn_route_del(vrf_id, &nh, (struct prefix *)&dummy_prefix); } diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c index 66d2d6b4ba..c9450541e8 100644 --- a/zebra/zebra_mpls.c +++ b/zebra/zebra_mpls.c @@ -752,7 +752,7 @@ static int nhlfe_nexthop_active(zebra_nhlfe_t *nhlfe) } break; - default: + case NEXTHOP_TYPE_BLACKHOLE: break; } @@ -1183,7 +1183,7 @@ static char *nhlfe2str(const zebra_nhlfe_t *nhlfe, char *buf, int size) break; case NEXTHOP_TYPE_IFINDEX: snprintf(buf, size, "Ifindex: %u", nexthop->ifindex); - default: + case NEXTHOP_TYPE_BLACKHOLE: break; } @@ -1224,7 +1224,7 @@ static int nhlfe_nhop_match(zebra_nhlfe_t *nhlfe, enum nexthop_types_t gtype, case NEXTHOP_TYPE_IFINDEX: cmp = !(nhop->ifindex == ifindex); break; - default: + case NEXTHOP_TYPE_BLACKHOLE: break; } @@ -1294,7 +1294,10 @@ static zebra_nhlfe_t *nhlfe_alloc(zebra_lsp_t *lsp, enum lsp_types_t lsp_type, case NEXTHOP_TYPE_IFINDEX: nexthop->ifindex = ifindex; break; - default: + case NEXTHOP_TYPE_BLACKHOLE: + if (IS_ZEBRA_DEBUG_MPLS) + zlog_debug("%s: invalid: blackhole nexthop", __func__); + nexthop_free(nexthop); XFREE(MTYPE_NHLFE, nhlfe); return NULL; @@ -1319,10 +1322,21 @@ static zebra_nhlfe_t *nhlfe_add(zebra_lsp_t *lsp, enum lsp_types_t lsp_type, if (!lsp) return NULL; + /* Must have labels */ + if (num_labels == 0 || labels == NULL) { + if (IS_ZEBRA_DEBUG_MPLS) + zlog_debug("%s: invalid nexthop: no labels", __func__); + + return NULL; + } + /* Allocate new object */ nhlfe = nhlfe_alloc(lsp, lsp_type, gtype, gate, ifindex, num_labels, labels); + if (!nhlfe) + return NULL; + /* Enqueue to LSP: primaries at head of list, backups at tail */ if (is_backup) { SET_FLAG(nhlfe->flags, NHLFE_FLAG_IS_BACKUP); @@ -1527,7 +1541,13 @@ static json_object *nhlfe_json(zebra_nhlfe_t *nhlfe) ifindex2ifname(nexthop->ifindex, nexthop->vrf_id)); break; - default: + case NEXTHOP_TYPE_IFINDEX: + if (nexthop->ifindex) + json_object_string_add(json_nhlfe, "interface", + ifindex2ifname(nexthop->ifindex, + nexthop->vrf_id)); + break; + case NEXTHOP_TYPE_BLACKHOLE: break; } @@ -1588,7 +1608,13 @@ static void nhlfe_print(zebra_nhlfe_t *nhlfe, struct vty *vty, ifindex2ifname(nexthop->ifindex, nexthop->vrf_id)); break; - default: + case NEXTHOP_TYPE_IFINDEX: + if (nexthop->ifindex) + vty_out(vty, " dev %s", + ifindex2ifname(nexthop->ifindex, + nexthop->vrf_id)); + break; + case NEXTHOP_TYPE_BLACKHOLE: break; } vty_out(vty, "%s", @@ -2830,8 +2856,21 @@ static bool ftn_update_znh(bool add_p, enum lsp_types_t type, break; success = true; break; - default: + case NEXTHOP_TYPE_IFINDEX: + if (znh->type != NEXTHOP_TYPE_IFINDEX) + continue; + if (nexthop->ifindex != znh->ifindex) + continue; + + found = true; + + if (!ftn_update_nexthop(add_p, nexthop, type, znh)) + break; + success = true; break; + case NEXTHOP_TYPE_BLACKHOLE: + /* Not valid */ + continue; } if (found) @@ -3752,7 +3791,7 @@ void zebra_mpls_print_lsp_table(struct vty *vty, struct zebra_vrf *zvrf, inet_ntop(AF_INET6, &nexthop->gate.ipv6, nh_buf, sizeof(nh_buf)); break; - default: + case NEXTHOP_TYPE_BLACKHOLE: break; } @@ -3795,7 +3834,11 @@ static char *nhlfe_config_str(const zebra_nhlfe_t *nhlfe, char *buf, int size) buf[0] = '\0'; switch (nh->type) { case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: inet_ntop(AF_INET, &nh->gate.ipv4, buf, size); + if (nh->ifindex) + strlcat(buf, ifindex2ifname(nh->ifindex, VRF_DEFAULT), + size); break; case NEXTHOP_TYPE_IPV6: case NEXTHOP_TYPE_IPV6_IFINDEX: @@ -3805,7 +3848,13 @@ static char *nhlfe_config_str(const zebra_nhlfe_t *nhlfe, char *buf, int size) ifindex2ifname(nh->ifindex, VRF_DEFAULT), size); break; - default: + case NEXTHOP_TYPE_IFINDEX: + if (nh->ifindex) + strlcat(buf, + ifindex2ifname(nh->ifindex, VRF_DEFAULT), + size); + break; + case NEXTHOP_TYPE_BLACKHOLE: break; } diff --git a/zebra/zebra_mpls_vty.c b/zebra/zebra_mpls_vty.c index d789f20071..fd9b1ae387 100644 --- a/zebra/zebra_mpls_vty.c +++ b/zebra/zebra_mpls_vty.c @@ -91,11 +91,11 @@ static int zebra_mpls_transit_lsp(struct vty *vty, int add_cmd, if (gate_str) { /* Gateway is a IPv4 or IPv6 nexthop. */ ret = inet_pton(AF_INET6, gate_str, &gate.ipv6); - if (ret) + if (ret == 1) gtype = NEXTHOP_TYPE_IPV6; else { ret = inet_pton(AF_INET, gate_str, &gate.ipv4); - if (ret) + if (ret == 1) gtype = NEXTHOP_TYPE_IPV4; else { vty_out(vty, "%% Invalid nexthop\n"); @@ -131,7 +131,7 @@ static int zebra_mpls_transit_lsp(struct vty *vty, int add_cmd, ret = zebra_mpls_static_lsp_del(zvrf, in_label, gtype, &gate, 0); - if (ret) { + if (ret != 0) { vty_out(vty, "%% LSP cannot be %s\n", add_cmd ? "added" : "deleted"); return CMD_WARNING_CONFIG_FAILED; diff --git a/zebra/zebra_pw.c b/zebra/zebra_pw.c index 6b4a815151..d5083d4cbe 100644 --- a/zebra/zebra_pw.c +++ b/zebra/zebra_pw.c @@ -836,6 +836,7 @@ static int zebra_pw_config(struct vty *vty) if (!(pw->flags & F_PSEUDOWIRE_CWORD)) vty_out(vty, " control-word exclude\n"); + vty_out(vty, "exit\n"); vty_out(vty, "!\n"); write = 1; } diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index c51dd759a6..1fb4e5e6fc 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -605,12 +605,9 @@ void rib_install_kernel(struct route_node *rn, struct route_entry *re, break; case ZEBRA_DPLANE_REQUEST_FAILURE: { - char str[SRCDEST2STR_BUFFER]; - - srcdest_rnode2str(rn, str, sizeof(str)); flog_err(EC_ZEBRA_DP_INSTALL_FAIL, - "%u:%u:%s: Failed to enqueue dataplane install", - re->vrf_id, re->table, str); + "%u:%u:%pRN: Failed to enqueue dataplane install", + re->vrf_id, re->table, rn); break; } case ZEBRA_DPLANE_REQUEST_SUCCESS: @@ -648,15 +645,10 @@ void rib_uninstall_kernel(struct route_node *rn, struct route_entry *re) zvrf->removals_queued++; break; case ZEBRA_DPLANE_REQUEST_FAILURE: - { - char str[SRCDEST2STR_BUFFER]; - - srcdest_rnode2str(rn, str, sizeof(str)); flog_err(EC_ZEBRA_DP_INSTALL_FAIL, - "%u:%s: Failed to enqueue dataplane uninstall", - re->vrf_id, str); + "%u:%pRN: Failed to enqueue dataplane uninstall", + re->vrf_id, rn); break; - } case ZEBRA_DPLANE_REQUEST_SUCCESS: if (zvrf) zvrf->removals++; @@ -666,46 +658,6 @@ void rib_uninstall_kernel(struct route_node *rn, struct route_entry *re) return; } -/* Uninstall the route from kernel. */ -static void rib_uninstall(struct route_node *rn, struct route_entry *re) -{ - struct rib_table_info *info = srcdest_rnode_table_info(rn); - rib_dest_t *dest = rib_dest_from_rnode(rn); - struct nexthop *nexthop; - - if (dest && dest->selected_fib == re) { - if (info->safi == SAFI_UNICAST) - hook_call(rib_update, rn, "rib_uninstall"); - - /* If labeled-unicast route, uninstall transit LSP. */ - if (zebra_rib_labeled_unicast(re)) - zebra_mpls_lsp_uninstall(info->zvrf, rn, re); - - rib_uninstall_kernel(rn, re); - - dest->selected_fib = NULL; - - /* Free FIB nexthop group, if present */ - if (re->fib_ng.nexthop) { - nexthops_free(re->fib_ng.nexthop); - re->fib_ng.nexthop = NULL; - } - UNSET_FLAG(re->status, ROUTE_ENTRY_USE_FIB_NHG); - - for (ALL_NEXTHOPS(re->nhe->nhg, nexthop)) - UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); - } - - if (CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED)) { - const struct prefix *p, *src_p; - - srcdest_rnode_prefixes(rn, &p, &src_p); - - redistribute_delete(p, src_p, re, NULL); - UNSET_FLAG(re->flags, ZEBRA_FLAG_SELECTED); - } -} - /* * rib_can_delete_dest * @@ -753,15 +705,12 @@ void zebra_rib_evaluate_rn_nexthops(struct route_node *rn, uint32_t seq) * would match a more specific route */ while (rn) { - if (IS_ZEBRA_DEBUG_NHT_DETAILED) { - char buf[PREFIX_STRLEN]; - + if (IS_ZEBRA_DEBUG_NHT_DETAILED) zlog_debug( - "%s: %s Being examined for Nexthop Tracking Count: %zd", - __func__, - srcdest_rnode2str(rn, buf, sizeof(buf)), + "%s: %pRN Being examined for Nexthop Tracking Count: %zd", + __func__, rn, dest ? rnh_list_count(&dest->nht) : 0); - } + if (!dest) { rn = rn->parent; if (rn) @@ -779,17 +728,12 @@ void zebra_rib_evaluate_rn_nexthops(struct route_node *rn, uint32_t seq) zebra_vrf_lookup_by_id(rnh->vrf_id); struct prefix *p = &rnh->node->p; - if (IS_ZEBRA_DEBUG_NHT_DETAILED) { - char buf1[PREFIX_STRLEN]; - + if (IS_ZEBRA_DEBUG_NHT_DETAILED) zlog_debug( - "%s(%u):%s has Nexthop(%pFX) Type: %s depending on it, evaluating %u:%u", + "%s(%u):%pRN has Nexthop(%pFX) Type: %s depending on it, evaluating %u:%u", zvrf_name(zvrf), zvrf_id(zvrf), - srcdest_rnode2str(rn, buf1, - sizeof(buf1)), - p, rnh_type2str(rnh->type), seq, + rn, p, rnh_type2str(rnh->type), seq, rnh->seqno); - } /* * If we have evaluated this node on this pass @@ -890,13 +834,10 @@ static void rib_process_add_fib(struct zebra_vrf *zvrf, struct route_node *rn, return; } - if (IS_ZEBRA_DEBUG_RIB) { - char buf[SRCDEST2STR_BUFFER]; - srcdest_rnode2str(rn, buf, sizeof(buf)); - zlog_debug("%s(%u:%u):%s: Adding route rn %p, re %p (%s)", - zvrf_name(zvrf), zvrf_id(zvrf), new->table, buf, rn, + if (IS_ZEBRA_DEBUG_RIB) + zlog_debug("%s(%u:%u):%pRN: Adding route rn %p, re %p (%s)", + zvrf_name(zvrf), zvrf_id(zvrf), new->table, rn, rn, new, zebra_route_string(new->type)); - } /* If labeled-unicast route, install transit LSP. */ if (zebra_rib_labeled_unicast(new)) @@ -913,13 +854,10 @@ static void rib_process_del_fib(struct zebra_vrf *zvrf, struct route_node *rn, hook_call(rib_update, rn, "removing existing route"); /* Uninstall from kernel. */ - if (IS_ZEBRA_DEBUG_RIB) { - char buf[SRCDEST2STR_BUFFER]; - srcdest_rnode2str(rn, buf, sizeof(buf)); - zlog_debug("%s(%u:%u):%s: Deleting route rn %p, re %p (%s)", - zvrf_name(zvrf), zvrf_id(zvrf), old->table, buf, rn, + if (IS_ZEBRA_DEBUG_RIB) + zlog_debug("%s(%u:%u):%pRN: Deleting route rn %p, re %p (%s)", + zvrf_name(zvrf), zvrf_id(zvrf), old->table, rn, rn, old, zebra_route_string(old->type)); - } /* If labeled-unicast route, uninstall transit LSP. */ if (zebra_rib_labeled_unicast(old)) @@ -965,22 +903,19 @@ static void rib_process_update_fib(struct zebra_vrf *zvrf, */ if (nh_active) { if (IS_ZEBRA_DEBUG_RIB) { - char buf[SRCDEST2STR_BUFFER]; - - srcdest_rnode2str(rn, buf, sizeof(buf)); if (new != old) zlog_debug( - "%s(%u:%u):%s: Updating route rn %p, re %p (%s) old %p (%s)", + "%s(%u:%u):%pRN: Updating route rn %p, re %p (%s) old %p (%s)", zvrf_name(zvrf), zvrf_id(zvrf), - new->table, buf, rn, new, + new->table, rn, rn, new, zebra_route_string(new->type), old, zebra_route_string(old->type)); else zlog_debug( - "%s(%u:%u):%s: Updating route rn %p, re %p (%s)", + "%s(%u:%u):%pRN: Updating route rn %p, re %p (%s)", zvrf_name(zvrf), zvrf_id(zvrf), - new->table, buf, rn, new, + new->table, rn, rn, new, zebra_route_string(new->type)); } @@ -1006,21 +941,19 @@ static void rib_process_update_fib(struct zebra_vrf *zvrf, */ if (!nh_active) { if (IS_ZEBRA_DEBUG_RIB) { - char buf[SRCDEST2STR_BUFFER]; - srcdest_rnode2str(rn, buf, sizeof(buf)); if (new != old) zlog_debug( - "%s(%u:%u):%s: Deleting route rn %p, re %p (%s) old %p (%s) - nexthop inactive", + "%s(%u:%u):%pRN: Deleting route rn %p, re %p (%s) old %p (%s) - nexthop inactive", zvrf_name(zvrf), zvrf_id(zvrf), - new->table, buf, rn, new, + new->table, rn, rn, new, zebra_route_string(new->type), old, zebra_route_string(old->type)); else zlog_debug( - "%s(%u:%u):%s: Deleting route rn %p, re %p (%s) - nexthop inactive", + "%s(%u:%u):%pRN: Deleting route rn %p, re %p (%s) - nexthop inactive", zvrf_name(zvrf), zvrf_id(zvrf), - new->table, buf, rn, new, + new->table, rn, rn, new, zebra_route_string(new->type)); } @@ -1137,7 +1070,6 @@ static void rib_process(struct route_node *rn) struct route_entry *old_fib = NULL; struct route_entry *new_fib = NULL; struct route_entry *best = NULL; - char buf[SRCDEST2STR_BUFFER]; rib_dest_t *dest; struct zebra_vrf *zvrf = NULL; struct vrf *vrf; @@ -1149,15 +1081,17 @@ static void rib_process(struct route_node *rn) assert(rn); dest = rib_dest_from_rnode(rn); - if (dest) { - zvrf = rib_dest_vrf(dest); - vrf_id = zvrf_id(zvrf); - } + /* + * We have an enqueued node with nothing to process here + * let's just finish up and return; + */ + if (!dest) + return; - vrf = vrf_lookup_by_id(vrf_id); + zvrf = rib_dest_vrf(dest); + vrf_id = zvrf_id(zvrf); - if (IS_ZEBRA_DEBUG_RIB) - srcdest_rnode2str(rn, buf, sizeof(buf)); + vrf = vrf_lookup_by_id(vrf_id); /* * we can have rn's that have a NULL info pointer @@ -1165,26 +1099,24 @@ static void rib_process(struct route_node *rn) * additionally we know RNODE_FOREACH_RE_SAFE * will not iterate so we are ok. */ - if (dest) { - if (IS_ZEBRA_DEBUG_RIB_DETAILED) { - struct route_entry *re = re_list_first(&dest->routes); - - zlog_debug("%s(%u:%u):%s: Processing rn %p", - VRF_LOGNAME(vrf), vrf_id, re->table, buf, - rn); - } + if (IS_ZEBRA_DEBUG_RIB_DETAILED) { + struct route_entry *re = re_list_first(&dest->routes); - old_fib = dest->selected_fib; + zlog_debug("%s(%u:%u):%pRN: Processing rn %p", + VRF_LOGNAME(vrf), vrf_id, re->table, rn, + rn); } + old_fib = dest->selected_fib; + RNODE_FOREACH_RE_SAFE (rn, re, next) { if (IS_ZEBRA_DEBUG_RIB_DETAILED) { char flags_buf[128]; char status_buf[128]; zlog_debug( - "%s(%u:%u):%s: Examine re %p (%s) status: %sflags: %sdist %d metric %d", - VRF_LOGNAME(vrf), vrf_id, re->table, buf, re, + "%s(%u:%u):%pRN: Examine re %p (%s) status: %sflags: %sdist %d metric %d", + VRF_LOGNAME(vrf), vrf_id, re->table, rn, re, zebra_route_string(re->type), _dump_re_status(re, status_buf, sizeof(status_buf)), @@ -1234,11 +1166,11 @@ static void rib_process(struct route_node *rn) if (re != old_selected) { if (IS_ZEBRA_DEBUG_RIB) zlog_debug( - "%s: %s(%u):%s: imported via import-table but denied by the ip protocol table route-map", + "%s: %s(%u):%pRN: imported via import-table but denied by the ip protocol table route-map", __func__, VRF_LOGNAME( vrf), - vrf_id, buf); + vrf_id, rn); rib_unlink(rn, re); } else SET_FLAG(re->status, @@ -1313,8 +1245,8 @@ static void rib_process(struct route_node *rn) : new_fib ? new_fib : NULL; zlog_debug( - "%s(%u:%u):%s: After processing: old_selected %p new_selected %p old_fib %p new_fib %p", - VRF_LOGNAME(vrf), vrf_id, entry ? entry->table : 0, buf, + "%s(%u:%u):%pRN: After processing: old_selected %p new_selected %p old_fib %p new_fib %p", + VRF_LOGNAME(vrf), vrf_id, entry ? entry->table : 0, rn, (void *)old_selected, (void *)new_selected, (void *)old_fib, (void *)new_fib); } @@ -2469,7 +2401,6 @@ static void process_subq_route(struct listnode *lnode, uint8_t qindex) if (IS_ZEBRA_DEBUG_RIB_DETAILED) { struct route_entry *re = NULL; - char buf[SRCDEST2STR_BUFFER]; /* * rib_process may have freed the dest @@ -2480,10 +2411,9 @@ static void process_subq_route(struct listnode *lnode, uint8_t qindex) if (dest) re = re_list_first(&dest->routes); - srcdest_rnode2str(rnode, buf, sizeof(buf)); - zlog_debug("%s(%u:%u):%s: rn %p dequeued from sub-queue %u", + zlog_debug("%s(%u:%u):%pRN rn %p dequeued from sub-queue %u", zvrf_name(zvrf), zvrf_id(zvrf), re ? re->table : 0, - buf, rnode, qindex); + rnode, rnode, qindex); } if (rnode->info) @@ -3190,13 +3120,10 @@ void rib_delnode(struct route_node *rn, struct route_entry *re) zebra_del_import_table_entry(zvrf, rn, re); /* Just clean up if non main table */ - if (IS_ZEBRA_DEBUG_RIB) { - char buf[SRCDEST2STR_BUFFER]; - srcdest_rnode2str(rn, buf, sizeof(buf)); - zlog_debug("%s(%u):%s: Freeing route rn %p, re %p (%s)", - vrf_id_to_name(re->vrf_id), re->vrf_id, buf, + if (IS_ZEBRA_DEBUG_RIB) + zlog_debug("%s(%u):%pRN: Freeing route rn %p, re %p (%s)", + vrf_id_to_name(re->vrf_id), re->vrf_id, rn, rn, re, zebra_route_string(re->type)); - } rib_unlink(rn, re); } else { @@ -3347,112 +3274,15 @@ void _route_entry_dump(const char *func, union prefixconstptr pp, } /* - * This is an exported helper to rtm_read() to dump the strange - * RE entry found by rib_lookup_ipv4_route() - */ -void rib_lookup_and_dump(struct prefix_ipv4 *p, vrf_id_t vrf_id) -{ - struct route_table *table; - struct route_node *rn; - struct route_entry *re; - struct vrf *vrf; - - vrf = vrf_lookup_by_id(vrf_id); - - /* Lookup table. */ - table = zebra_vrf_table(AFI_IP, SAFI_UNICAST, vrf_id); - if (!table) { - flog_err(EC_ZEBRA_TABLE_LOOKUP_FAILED, - "%s:%s(%u) zebra_vrf_table() returned NULL", __func__, - VRF_LOGNAME(vrf), vrf_id); - return; - } - - /* Scan the RIB table for exactly matching RE entry. */ - rn = route_node_lookup(table, (struct prefix *)p); - - /* No route for this prefix. */ - if (!rn) { - zlog_debug("%s:%s(%u) lookup failed for %pFX", __func__, - VRF_LOGNAME(vrf), vrf_id, (struct prefix *)p); - return; - } - - /* Unlock node. */ - route_unlock_node(rn); - - /* let's go */ - RNODE_FOREACH_RE (rn, re) { - zlog_debug("%s:%s(%u) rn %p, re %p: %s, %s", __func__, - VRF_LOGNAME(vrf), vrf_id, (void *)rn, (void *)re, - (CHECK_FLAG(re->status, ROUTE_ENTRY_REMOVED) - ? "removed" - : "NOT removed"), - (CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED) - ? "selected" - : "NOT selected")); - route_entry_dump(p, NULL, re); - } -} - -/* Check if requested address assignment will fail due to another - * route being installed by zebra in FIB already. Take necessary - * actions, if needed: remove such a route from FIB and deSELECT - * corresponding RE entry. Then put affected RN into RIBQ head. - */ -void rib_lookup_and_pushup(struct prefix_ipv4 *p, vrf_id_t vrf_id) -{ - struct route_table *table; - struct route_node *rn; - rib_dest_t *dest; - - if (NULL == (table = zebra_vrf_table(AFI_IP, SAFI_UNICAST, vrf_id))) { - struct vrf *vrf = vrf_lookup_by_id(vrf_id); - - flog_err(EC_ZEBRA_TABLE_LOOKUP_FAILED, - "%s:%s(%u) zebra_vrf_table() returned NULL", __func__, - VRF_LOGNAME(vrf), vrf_id); - return; - } - - /* No matches would be the simplest case. */ - if (NULL == (rn = route_node_lookup(table, (struct prefix *)p))) - return; - - /* Unlock node. */ - route_unlock_node(rn); - - dest = rib_dest_from_rnode(rn); - /* Check all RE entries. In case any changes have to be done, requeue - * the RN into RIBQ head. If the routing message about the new connected - * route (generated by the IP address we are going to assign very soon) - * comes before the RIBQ is processed, the new RE entry will join - * RIBQ record already on head. This is necessary for proper - * revalidation - * of the rest of the RE. - */ - if (dest->selected_fib) { - if (IS_ZEBRA_DEBUG_RIB) { - struct vrf *vrf = - vrf_lookup_by_id(dest->selected_fib->vrf_id); - - zlog_debug( - "%s(%u):%pFX: freeing way for connected prefix", - VRF_LOGNAME(vrf), dest->selected_fib->vrf_id, - &rn->p); - route_entry_dump(&rn->p, NULL, dest->selected_fib); - } - rib_uninstall(rn, dest->selected_fib); - rib_queue_add(rn); - } -} - -/* * Internal route-add implementation; there are a couple of different public * signatures. Callers in this path are responsible for the memory they * allocate: if they allocate a nexthop_group or backup nexthop info, they * must free those objects. If this returns < 0, an error has occurred and the * route_entry 're' has not been captured; the caller should free that also. + * + * -1 -> error + * 0 -> Add + * 1 -> update */ int rib_add_multipath_nhe(afi_t afi, safi_t safi, struct prefix *p, struct prefix_ipv6 *src_p, struct route_entry *re, @@ -3567,11 +3397,12 @@ int rib_add_multipath_nhe(afi_t afi, safi_t safi, struct prefix *p, SET_FLAG(re->status, ROUTE_ENTRY_CHANGED); rib_addnode(rn, re, 1); - ret = 1; /* Free implicit route.*/ - if (same) + if (same) { + ret = 1; rib_delnode(rn, same); + } /* See if we can remove some RE entries that are queued for * removal, but won't be considered in rib processing. diff --git a/zebra/zebra_srv6_vty.c b/zebra/zebra_srv6_vty.c index 97935f126e..d2b91b6c07 100644 --- a/zebra/zebra_srv6_vty.c +++ b/zebra/zebra_srv6_vty.c @@ -320,10 +320,14 @@ static int zebra_sr_config(struct vty *vty) vty_out(vty, " locator %s\n", locator->name); vty_out(vty, " prefix %s/%u\n", str, locator->prefix.prefixlen); + vty_out(vty, " exit\n"); vty_out(vty, " !\n"); } + vty_out(vty, " exit\n"); vty_out(vty, " !\n"); + vty_out(vty, " exit\n"); vty_out(vty, " !\n"); + vty_out(vty, "exit\n"); vty_out(vty, "!\n"); } return 0; diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c index 2430b51989..4fbcc6f596 100644 --- a/zebra/zebra_vrf.c +++ b/zebra/zebra_vrf.c @@ -41,6 +41,9 @@ #include "zebra/zebra_vxlan.h" #include "zebra/zebra_netns_notify.h" #include "zebra/zebra_routemap.h" +#ifndef VTYSH_EXTRACT_PL +#include "zebra/zebra_vrf_clippy.c" +#endif static void zebra_vrf_table_create(struct zebra_vrf *zvrf, afi_t afi, safi_t safi); @@ -521,18 +524,81 @@ static int vrf_config_write(struct vty *vty) router_id_write(vty, zvrf); if (zvrf_id(zvrf) != VRF_DEFAULT) - vty_endframe(vty, " exit-vrf\n!\n"); + vty_endframe(vty, "exit-vrf\n!\n"); else vty_out(vty, "!\n"); } return 0; } +DEFPY (vrf_netns, + vrf_netns_cmd, + "netns NAME$netns_name", + "Attach VRF to a Namespace\n" + "The file name in " NS_RUN_DIR ", or a full pathname\n") +{ + char *pathname = ns_netns_pathname(vty, netns_name); + int ret; + + VTY_DECLVAR_CONTEXT(vrf, vrf); + + if (!pathname) + return CMD_WARNING_CONFIG_FAILED; + + frr_with_privs(&zserv_privs) { + ret = vrf_netns_handler_create(vty, vrf, pathname, + NS_UNKNOWN, + NS_UNKNOWN, + NS_UNKNOWN); + } + + return ret; +} + +DEFUN (no_vrf_netns, + no_vrf_netns_cmd, + "no netns [NAME]", + NO_STR + "Detach VRF from a Namespace\n" + "The file name in " NS_RUN_DIR ", or a full pathname\n") +{ + struct ns *ns = NULL; + + VTY_DECLVAR_CONTEXT(vrf, vrf); + + if (!vrf_is_backend_netns()) { + vty_out(vty, "VRF backend is not Netns. Aborting\n"); + return CMD_WARNING_CONFIG_FAILED; + } + if (!vrf->ns_ctxt) { + vty_out(vty, "VRF %s(%u) is not configured with NetNS\n", + vrf->name, vrf->vrf_id); + return CMD_WARNING_CONFIG_FAILED; + } + + ns = (struct ns *)vrf->ns_ctxt; + + ns->vrf_ctxt = NULL; + vrf_disable(vrf); + /* vrf ID from VRF is necessary for Zebra + * so that propagate to other clients is done + */ + ns_delete(ns); + vrf->ns_ctxt = NULL; + return CMD_SUCCESS; +} + /* Zebra VRF initialization. */ void zebra_vrf_init(void) { vrf_init(zebra_vrf_new, zebra_vrf_enable, zebra_vrf_disable, zebra_vrf_delete, zebra_vrf_update); - vrf_cmd_init(vrf_config_write, &zserv_privs); + vrf_cmd_init(vrf_config_write); + + if (vrf_is_backend_netns() && ns_have_netns()) { + /* Install NS commands. */ + install_element(VRF_NODE, &vrf_netns_cmd); + install_element(VRF_NODE, &no_vrf_netns_cmd); + } } diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index 1660792221..2fcaefdfbf 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -150,9 +150,28 @@ static int host_rb_entry_compare(const struct host_rb_entry *hle1, return memcmp(&hle1->p.u.prefix6, &hle2->p.u.prefix6, IPV6_MAX_BYTELEN); } else if (hle1->p.family == AF_EVPN) { - /* a single dummy prefix of route_type BGP_EVPN_AD_ROUTE is - * used for all nexthops associated with a non-zero ESI + uint8_t family1; + uint8_t family2; + + /* two (v4/v6) dummy prefixes of route_type BGP_EVPN_AD_ROUTE + * are used for all nexthops associated with a non-zero ESI */ + family1 = is_evpn_prefix_ipaddr_v4( + (const struct prefix_evpn *)&hle1->p) + ? AF_INET + : AF_INET6; + family2 = is_evpn_prefix_ipaddr_v4( + (const struct prefix_evpn *)&hle2->p) + ? AF_INET + : AF_INET6; + + + if (family1 < family2) + return -1; + + if (family1 > family2) + return 1; + return 0; } else { zlog_debug("%s: Unexpected family type: %d", __func__, |
