diff options
451 files changed, 19906 insertions, 837 deletions
diff --git a/bgpd/bgp_bfd.c b/bgpd/bgp_bfd.c index 67b8018c8e..1e1c97c2d9 100644 --- a/bgpd/bgp_bfd.c +++ b/bgpd/bgp_bfd.c @@ -361,7 +361,7 @@ static void bgp_bfd_peer_status_update(struct peer *peer, int status, if ((status == BFD_STATUS_UP) && (old_status == BFD_STATUS_DOWN) && peer->status != Established) { if (!BGP_PEER_START_SUPPRESSED(peer)) { - bgp_fsm_event_update(peer, 1); + bgp_fsm_nht_update(peer, true); BGP_EVENT_ADD(peer, BGP_Start); } } diff --git a/bgpd/bgp_clist.c b/bgpd/bgp_clist.c index 5b3908442c..247d758f8c 100644 --- a/bgpd/bgp_clist.c +++ b/bgpd/bgp_clist.c @@ -337,18 +337,42 @@ static void community_list_entry_delete(struct community_list_master *cm, community_list_delete(cm, list); } +/* + * Replace community-list entry in the list. Note that entry is the new one + * and replace is one one being replaced. + */ +static void community_list_entry_replace(struct community_list *list, + struct community_entry *replace, + struct community_entry *entry) +{ + if (replace->next) { + entry->next = replace->next; + replace->next->prev = entry; + } else { + entry->next = NULL; + list->tail = entry; + } + + if (replace->prev) { + entry->prev = replace->prev; + replace->prev->next = entry; + } else { + entry->prev = NULL; + list->head = entry; + } + + community_entry_free(replace); +} + /* Add community-list entry to the list. */ static void community_list_entry_add(struct community_list *list, struct community_entry *entry, struct community_list_handler *ch, int master) { - struct community_list_master *cm = NULL; struct community_entry *replace; struct community_entry *point; - cm = community_list_master_lookup(ch, master); - /* Automatic assignment of seq no. */ if (entry->seq == COMMUNITY_SEQ_NUMBER_AUTO) entry->seq = bgp_clist_new_seq_get(list); @@ -357,8 +381,10 @@ static void community_list_entry_add(struct community_list *list, point = NULL; else { replace = bgp_clist_seq_check(list, entry->seq); - if (replace) - community_list_entry_delete(cm, list, entry); + if (replace) { + community_list_entry_replace(list, replace, entry); + return; + } /* Check insert point. */ for (point = list->head; point; point = point->next) diff --git a/bgpd/bgp_damp.c b/bgpd/bgp_damp.c index 565d0b8e19..9992168182 100644 --- a/bgpd/bgp_damp.c +++ b/bgpd/bgp_damp.c @@ -36,6 +36,8 @@ #include "bgpd/bgp_attr.h" #include "bgpd/bgp_advertise.h" +const char *get_afi_safi_str(afi_t afi, safi_t safi, bool for_json); + /* Global variable to access damping configuration */ static struct bgp_damp_config damp[AFI_MAX][SAFI_MAX]; @@ -653,16 +655,9 @@ const char *bgp_damp_reuse_time_vty(struct vty *vty, struct bgp_path_info *path, json); } -int bgp_show_dampening_parameters(struct vty *vty, afi_t afi, safi_t safi) +static int bgp_print_dampening_parameters(struct bgp *bgp, struct vty *vty, + afi_t afi, safi_t safi) { - struct bgp *bgp; - bgp = bgp_get_default(); - - if (bgp == NULL) { - vty_out(vty, "No BGP process is configured\n"); - return CMD_WARNING; - } - if (CHECK_FLAG(bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING)) { vty_out(vty, "Half-life time: %lld min\n", (long long)damp[afi][safi].half_life / 60); @@ -677,7 +672,52 @@ int bgp_show_dampening_parameters(struct vty *vty, afi_t afi, safi_t safi) vty_out(vty, "\n"); } else vty_out(vty, "dampening not enabled for %s\n", - afi == AFI_IP ? "IPv4" : "IPv6"); + get_afi_safi_str(afi, safi, false)); + + return CMD_SUCCESS; +} + +int bgp_show_dampening_parameters(struct vty *vty, afi_t afi, safi_t safi, + uint8_t show_flags) +{ + struct bgp *bgp; + bgp = bgp_get_default(); + + if (bgp == NULL) { + vty_out(vty, "No BGP process is configured\n"); + return CMD_WARNING; + } + + if (!CHECK_FLAG(show_flags, BGP_SHOW_OPT_AFI_ALL)) + return bgp_print_dampening_parameters(bgp, vty, afi, safi); + + if (CHECK_FLAG(show_flags, BGP_SHOW_OPT_AFI_IP) + || CHECK_FLAG(show_flags, BGP_SHOW_OPT_AFI_IP6)) { + afi = CHECK_FLAG(show_flags, BGP_SHOW_OPT_AFI_IP) ? AFI_IP + : AFI_IP6; + FOREACH_SAFI (safi) { + if (strmatch(get_afi_safi_str(afi, safi, true), + "Unknown")) + continue; + if (!CHECK_FLAG(show_flags, BGP_SHOW_OPT_JSON)) + vty_out(vty, "\nFor address family: %s\n\n", + get_afi_safi_str(afi, safi, false)); + + bgp_print_dampening_parameters(bgp, vty, afi, safi); + } + } else { + FOREACH_AFI_SAFI (afi, safi) { + if (strmatch(get_afi_safi_str(afi, safi, true), + "Unknown")) + continue; + + if (!CHECK_FLAG(show_flags, BGP_SHOW_OPT_JSON)) + vty_out(vty, "\nFor address family: %s\n", + get_afi_safi_str(afi, safi, false)); + + bgp_print_dampening_parameters(bgp, vty, afi, safi); + } + } return CMD_SUCCESS; } diff --git a/bgpd/bgp_damp.h b/bgpd/bgp_damp.h index 4ab38326e2..604706300b 100644 --- a/bgpd/bgp_damp.h +++ b/bgpd/bgp_damp.h @@ -151,6 +151,7 @@ extern const char *bgp_damp_reuse_time_vty(struct vty *vty, char *timebuf, size_t len, afi_t afi, safi_t safi, bool use_json, json_object *json); -extern int bgp_show_dampening_parameters(struct vty *vty, afi_t, safi_t); +extern int bgp_show_dampening_parameters(struct vty *vty, afi_t, safi_t, + uint8_t); #endif /* _QUAGGA_BGP_DAMP_H */ diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index 8c3e54566e..0703853354 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -519,6 +519,8 @@ static void form_auto_rt(struct bgp *bgp, vni_t vni, struct list *rtl) if (!ecom_found) listnode_add_sort(rtl, ecomadd); + else + ecommunity_free(&ecomadd); } /* diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c index 14dcf2b593..d7df707a36 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -2037,24 +2037,24 @@ static int bgp_fsm_exeption(struct peer *peer) return (bgp_stop(peer)); } -void bgp_fsm_event_update(struct peer *peer, int valid) +void bgp_fsm_nht_update(struct peer *peer, bool has_valid_nexthops) { if (!peer) return; switch (peer->status) { case Idle: - if (valid) + if (has_valid_nexthops) BGP_EVENT_ADD(peer, BGP_Start); break; case Connect: - if (!valid) { + if (!has_valid_nexthops) { BGP_TIMER_OFF(peer->t_connect); BGP_EVENT_ADD(peer, TCP_fatal_error); } break; case Active: - if (valid) { + if (has_valid_nexthops) { BGP_TIMER_OFF(peer->t_connect); BGP_EVENT_ADD(peer, ConnectRetry_timer_expired); } @@ -2062,7 +2062,8 @@ void bgp_fsm_event_update(struct peer *peer, int valid) case OpenSent: case OpenConfirm: case Established: - if (!valid && (peer->gtsm_hops == BGP_GTSM_HOPS_CONNECTED)) + if (!has_valid_nexthops + && (peer->gtsm_hops == BGP_GTSM_HOPS_CONNECTED)) BGP_EVENT_ADD(peer, TCP_fatal_error); case Clearing: case Deleted: diff --git a/bgpd/bgp_fsm.h b/bgpd/bgp_fsm.h index 2fd5f6fc47..85c0eccc26 100644 --- a/bgpd/bgp_fsm.h +++ b/bgpd/bgp_fsm.h @@ -109,7 +109,11 @@ && !CHECK_FLAG(peer->cap, PEER_CAP_RESTART_BIT_ADV)) /* Prototypes. */ -extern void bgp_fsm_event_update(struct peer *peer, int valid); + +/* + * Update FSM for peer based on whether we have valid nexthops or not. + */ +extern void bgp_fsm_nht_update(struct peer *peer, bool has_valid_nexthops); extern int bgp_event(struct thread *); extern int bgp_event_update(struct peer *, enum bgp_fsm_events event); extern int bgp_stop(struct peer *peer); diff --git a/bgpd/bgp_nexthop.c b/bgpd/bgp_nexthop.c index ed026a2fff..0d8214e4d6 100644 --- a/bgpd/bgp_nexthop.c +++ b/bgpd/bgp_nexthop.c @@ -563,7 +563,7 @@ bool bgp_nexthop_self(struct bgp *bgp, afi_t afi, uint8_t type, if (addr) return true; - if (new_afi == AF_INET) { + if (new_afi == AF_INET && hashcount(bgp->tip_hash)) { memset(&tmp_tip, 0, sizeof(struct tip_addr)); tmp_tip.addr = attr->nexthop; diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c index 9573d118e5..c89978e91b 100644 --- a/bgpd/bgp_nht.c +++ b/bgpd/bgp_nht.c @@ -846,7 +846,7 @@ static void evaluate_paths(struct bgp_nexthop_cache *bnc) "%s: Updating peer (%s(%s)) status with NHT", __func__, peer->host, peer->bgp->name_pretty); - bgp_fsm_event_update(peer, valid_nexthops); + bgp_fsm_nht_update(peer, !!valid_nexthops); SET_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED); } } diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index 15dba37667..ee3580edf1 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -1775,10 +1775,6 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size) peer->update_time = bgp_clock(); - /* Rearm holdtime timer */ - BGP_TIMER_OFF(peer->t_holdtime); - bgp_timer_set(peer); - return Receive_UPDATE_message; } diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 8eaee36c2e..626f766987 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -9757,15 +9757,14 @@ static int bgp_show_regexp(struct vty *vty, struct bgp *bgp, const char *regstr, bool use_json); static int bgp_show_community(struct vty *vty, struct bgp *bgp, const char *comstr, int exact, afi_t afi, - safi_t safi, bool use_json); + safi_t safi, uint8_t show_flags); static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, struct bgp_table *table, enum bgp_show_type type, - void *output_arg, bool use_json, char *rd, - int is_last, unsigned long *output_cum, - unsigned long *total_cum, - unsigned long *json_header_depth, bool wide) + void *output_arg, char *rd, int is_last, + unsigned long *output_cum, unsigned long *total_cum, + unsigned long *json_header_depth, uint8_t show_flags) { struct bgp_path_info *pi; struct bgp_dest *dest; @@ -9776,13 +9775,23 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, struct prefix *p; json_object *json_paths = NULL; int first = 1; + bool use_json = CHECK_FLAG(show_flags, BGP_SHOW_OPT_JSON); + bool wide = CHECK_FLAG(show_flags, BGP_SHOW_OPT_WIDE); + bool all = CHECK_FLAG(show_flags, BGP_SHOW_OPT_AFI_ALL); if (output_cum && *output_cum != 0) header = 0; if (use_json && !*json_header_depth) { + if (all) + *json_header_depth = 1; + else { + vty_out(vty, "{\n"); + *json_header_depth = 2; + } + vty_out(vty, - "{\n \"vrfId\": %d,\n \"vrfName\": \"%s\",\n \"tableVersion\": %" PRId64",\n \"routerId\": \"%s\",\n \"defaultLocPrf\": %u,\n" + " \"vrfId\": %d,\n \"vrfName\": \"%s\",\n \"tableVersion\": %" PRId64",\n \"routerId\": \"%s\",\n \"defaultLocPrf\": %u,\n" " \"localAS\": %u,\n \"routes\": { ", bgp->vrf_id == VRF_UNKNOWN ? -1 : (int)bgp->vrf_id, bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT @@ -9790,7 +9799,6 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, : bgp->name, table->version, inet_ntoa(bgp->router_id), bgp->default_local_pref, bgp->as); - *json_header_depth = 2; if (rd) { vty_out(vty, " \"routeDistinguishers\" : {"); ++*json_header_depth; @@ -10071,7 +10079,8 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, unsigned long i; for (i = 0; i < *json_header_depth; ++i) vty_out(vty, " } "); - vty_out(vty, "\n"); + if (!all) + vty_out(vty, "\n"); } } else { if (is_last) { @@ -10101,9 +10110,13 @@ int bgp_show_table_rd(struct vty *vty, struct bgp *bgp, safi_t safi, unsigned long json_header_depth = 0; struct bgp_table *itable; bool show_msg; + uint8_t show_flags = 0; show_msg = (!use_json && type == bgp_show_type_normal); + if (use_json) + SET_FLAG(show_flags, BGP_SHOW_OPT_JSON); + for (dest = bgp_table_top(table); dest; dest = next) { const struct prefix *dest_p = bgp_dest_get_prefix(dest); @@ -10119,8 +10132,9 @@ int bgp_show_table_rd(struct vty *vty, struct bgp *bgp, safi_t safi, memcpy(&prd, dest_p, sizeof(struct prefix_rd)); prefix_rd2str(&prd, rd, sizeof(rd)); bgp_show_table(vty, bgp, safi, itable, type, output_arg, - use_json, rd, next == NULL, &output_cum, - &total_cum, &json_header_depth, false); + rd, next == NULL, &output_cum, + &total_cum, &json_header_depth, + show_flags); if (next == NULL) show_msg = false; } @@ -10137,11 +10151,12 @@ int bgp_show_table_rd(struct vty *vty, struct bgp *bgp, safi_t safi, return CMD_SUCCESS; } static int bgp_show(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, - enum bgp_show_type type, void *output_arg, bool use_json, - bool wide) + enum bgp_show_type type, void *output_arg, + uint8_t show_flags) { struct bgp_table *table; unsigned long json_header_depth = 0; + bool use_json = CHECK_FLAG(show_flags, BGP_SHOW_OPT_JSON); if (bgp == NULL) { bgp = bgp_get_default(); @@ -10171,18 +10186,18 @@ static int bgp_show(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, else if (safi == SAFI_LABELED_UNICAST) safi = SAFI_UNICAST; - return bgp_show_table(vty, bgp, safi, table, type, output_arg, use_json, - NULL, 1, NULL, NULL, &json_header_depth, wide); + return bgp_show_table(vty, bgp, safi, table, type, output_arg, NULL, 1, + NULL, NULL, &json_header_depth, show_flags); } static void bgp_show_all_instances_routes_vty(struct vty *vty, afi_t afi, - safi_t safi, bool use_json, - bool wide) + safi_t safi, uint8_t show_flags) { struct listnode *node, *nnode; struct bgp *bgp; int is_first = 1; bool route_output = false; + bool use_json = CHECK_FLAG(show_flags, BGP_SHOW_OPT_JSON); if (use_json) vty_out(vty, "{\n"); @@ -10206,7 +10221,7 @@ static void bgp_show_all_instances_routes_vty(struct vty *vty, afi_t afi, : bgp->name); } bgp_show(vty, bgp, afi, safi, bgp_show_type_normal, NULL, - use_json, wide); + show_flags); } if (use_json) @@ -10667,6 +10682,10 @@ static int bgp_show_lcommunity(struct vty *vty, struct bgp *bgp, int argc, int i; char *str; int first = 0; + uint8_t show_flags = 0; + + if (uj) + SET_FLAG(show_flags, BGP_SHOW_OPT_JSON); b = buffer_new(1024); for (i = 0; i < argc; i++) { @@ -10694,7 +10713,7 @@ static int bgp_show_lcommunity(struct vty *vty, struct bgp *bgp, int argc, return bgp_show(vty, bgp, afi, safi, (exact ? bgp_show_type_lcommunity_exact : bgp_show_type_lcommunity), - lcom, uj, false); + lcom, show_flags); } static int bgp_show_lcommunity_list(struct vty *vty, struct bgp *bgp, @@ -10702,6 +10721,11 @@ static int bgp_show_lcommunity_list(struct vty *vty, struct bgp *bgp, safi_t safi, bool uj) { struct community_list *list; + uint8_t show_flags = 0; + + if (uj) + SET_FLAG(show_flags, BGP_SHOW_OPT_JSON); + list = community_list_lookup(bgp_clist, lcom, 0, LARGE_COMMUNITY_LIST_MASTER); @@ -10714,7 +10738,7 @@ static int bgp_show_lcommunity_list(struct vty *vty, struct bgp *bgp, return bgp_show(vty, bgp, afi, safi, (exact ? bgp_show_type_lcommunity_list_exact : bgp_show_type_lcommunity_list), - list, uj, false); + list, show_flags); } DEFUN (show_ip_bgp_large_community_list, @@ -10777,14 +10801,17 @@ DEFUN (show_ip_bgp_large_community, bool exact_match = 0; struct bgp *bgp = NULL; bool uj = use_json(argc, argv); + uint8_t show_flags = 0; - if (uj) - argc--; + if (uj) { + argc--; + SET_FLAG(show_flags, BGP_SHOW_OPT_JSON); + } - bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi, - &bgp, uj); - if (!idx) - return CMD_WARNING; + bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi, + &bgp, uj); + if (!idx) + return CMD_WARNING; if (argv_find(argv, argc, "AA:BB:CC", &idx)) { if (argv_find(argv, argc, "exact-match", &idx)) @@ -10793,7 +10820,7 @@ DEFUN (show_ip_bgp_large_community, exact_match, afi, safi, uj); } else return bgp_show(vty, bgp, afi, safi, - bgp_show_type_lcommunity_all, NULL, uj, false); + bgp_show_type_lcommunity_all, NULL, show_flags); } static int bgp_table_stats_single(struct vty *vty, struct bgp *bgp, afi_t afi, @@ -10940,11 +10967,11 @@ DEFUN(show_ip_bgp_afi_safi_statistics, show_ip_bgp_afi_safi_statistics_cmd, } /* BGP route print out function without JSON */ -DEFUN(show_ip_bgp, show_ip_bgp_cmd, +DEFPY(show_ip_bgp, show_ip_bgp_cmd, "show [ip] bgp [<view|vrf> VIEWVRFNAME] [" BGP_AFI_CMD_STR " [" BGP_SAFI_WITH_LABEL_CMD_STR "]]\ - <dampening <parameters>\ + <[all$all] dampening <parameters>\ |route-map WORD\ |prefix-list WORD\ |filter-list WORD\ @@ -10954,6 +10981,7 @@ DEFUN(show_ip_bgp, show_ip_bgp_cmd, >", SHOW_STR IP_STR BGP_STR BGP_INSTANCE_HELP_STR BGP_AFI_HELP_STR BGP_SAFI_WITH_LABEL_HELP_STR + "Display the entries for all address families\n" "Display detailed information about dampening\n" "Display detail of configured dampening parameters\n" "Display routes matching the route-map\n" @@ -10976,6 +11004,17 @@ DEFUN(show_ip_bgp, show_ip_bgp_cmd, int exact_match = 0; struct bgp *bgp = NULL; int idx = 0; + uint8_t show_flags = 0; + + /* [<ipv4|ipv6> [all]] */ + if (all) { + SET_FLAG(show_flags, BGP_SHOW_OPT_AFI_ALL); + if (argv_find(argv, argc, "ipv4", &idx)) + SET_FLAG(show_flags, BGP_SHOW_OPT_AFI_IP); + + if (argv_find(argv, argc, "ipv6", &idx)) + SET_FLAG(show_flags, BGP_SHOW_OPT_AFI_IP6); + } bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi, &bgp, false); @@ -10984,7 +11023,8 @@ DEFUN(show_ip_bgp, show_ip_bgp_cmd, if (argv_find(argv, argc, "dampening", &idx)) { if (argv_find(argv, argc, "parameters", &idx)) - return bgp_show_dampening_parameters(vty, afi, safi); + return bgp_show_dampening_parameters(vty, afi, safi, + show_flags); } if (argv_find(argv, argc, "prefix-list", &idx)) @@ -11020,6 +11060,7 @@ DEFUN(show_ip_bgp, show_ip_bgp_cmd, DEFPY (show_ip_bgp_json, show_ip_bgp_json_cmd, "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]]\ + [all$all]\ [cidr-only\ |dampening <flap-statistics|dampened-paths>\ |community [AA:NN|local-AS|no-advertise|no-export\ @@ -11034,6 +11075,7 @@ DEFPY (show_ip_bgp_json, BGP_INSTANCE_HELP_STR BGP_AFI_HELP_STR BGP_SAFI_WITH_LABEL_HELP_STR + "Display the entries for all address families\n" "Display only routes with non-natural netmasks\n" "Display detailed information about dampening\n" "Display flap statistics of routes\n" @@ -11064,9 +11106,29 @@ DEFPY (show_ip_bgp_json, struct bgp *bgp = NULL; int idx = 0; int exact_match = 0; + char *community = NULL; + bool first = true; + uint8_t show_flags = 0; - if (uj) + + if (uj) { argc--; + SET_FLAG(show_flags, BGP_SHOW_OPT_JSON); + } + + /* [<ipv4|ipv6> [all]] */ + if (all) { + SET_FLAG(show_flags, BGP_SHOW_OPT_AFI_ALL); + + if (argv_find(argv, argc, "ipv4", &idx)) + SET_FLAG(show_flags, BGP_SHOW_OPT_AFI_IP); + + if (argv_find(argv, argc, "ipv6", &idx)) + SET_FLAG(show_flags, BGP_SHOW_OPT_AFI_IP6); + } + + if (wide) + SET_FLAG(show_flags, BGP_SHOW_OPT_WIDE); bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi, &bgp, uj); @@ -11074,23 +11136,17 @@ DEFPY (show_ip_bgp_json, return CMD_WARNING; if (argv_find(argv, argc, "cidr-only", &idx)) - return bgp_show(vty, bgp, afi, safi, bgp_show_type_cidr_only, - NULL, uj, wide); + sh_type = bgp_show_type_cidr_only; if (argv_find(argv, argc, "dampening", &idx)) { if (argv_find(argv, argc, "dampened-paths", &idx)) - return bgp_show(vty, bgp, afi, safi, - bgp_show_type_dampend_paths, NULL, uj, - wide); + sh_type = bgp_show_type_dampend_paths; else if (argv_find(argv, argc, "flap-statistics", &idx)) - return bgp_show(vty, bgp, afi, safi, - bgp_show_type_flap_statistics, NULL, uj, - wide); + sh_type = bgp_show_type_flap_statistics; } if (argv_find(argv, argc, "community", &idx)) { char *maybecomm = NULL; - char *community = NULL; if (idx + 1 < argc) { if (argv[idx + 1]->type == VARIABLE_TKN) @@ -11106,16 +11162,103 @@ DEFPY (show_ip_bgp_json, if (argv_find(argv, argc, "exact-match", &idx)) exact_match = 1; + if (!community) + sh_type = bgp_show_type_community_all; + } + + if (!all) { + /* show bgp: AFI_IP6, show ip bgp: AFI_IP */ if (community) return bgp_show_community(vty, bgp, community, - exact_match, afi, safi, uj); + exact_match, afi, safi, + show_flags); else - return (bgp_show(vty, bgp, afi, safi, - bgp_show_type_community_all, NULL, uj, - wide)); - } + return bgp_show(vty, bgp, afi, safi, sh_type, NULL, + show_flags); + } else { + /* show <ip> bgp ipv4 all: AFI_IP, show <ip> bgp ipv6 all: + * AFI_IP6 */ + + if (uj) + vty_out(vty, "{\n"); + + if (CHECK_FLAG(show_flags, BGP_SHOW_OPT_AFI_IP) + || CHECK_FLAG(show_flags, BGP_SHOW_OPT_AFI_IP6)) { + afi = CHECK_FLAG(show_flags, BGP_SHOW_OPT_AFI_IP) + ? AFI_IP + : AFI_IP6; + FOREACH_SAFI (safi) { + if (strmatch(get_afi_safi_str(afi, safi, true), + "Unknown")) + continue; + + if (!bgp_afi_safi_peer_exists(bgp, afi, safi)) + continue; + + if (uj) { + if (first) + first = false; + else + vty_out(vty, ",\n"); + vty_out(vty, "\"%s\":{\n", + get_afi_safi_str(afi, safi, + true)); + } else + vty_out(vty, + "\nFor address family: %s\n", + get_afi_safi_str(afi, safi, + false)); + + if (community) + bgp_show_community(vty, bgp, community, + exact_match, afi, + safi, show_flags); + else + bgp_show(vty, bgp, afi, safi, sh_type, + NULL, show_flags); + if (uj) + vty_out(vty, "}\n"); + } + } else { + /* show <ip> bgp all: for each AFI and SAFI*/ + FOREACH_AFI_SAFI (afi, safi) { + if (strmatch(get_afi_safi_str(afi, safi, true), + "Unknown")) + continue; + + if (!bgp_afi_safi_peer_exists(bgp, afi, safi)) + continue; + + if (uj) { + if (first) + first = false; + else + vty_out(vty, ",\n"); - return bgp_show(vty, bgp, afi, safi, sh_type, NULL, uj, wide); + vty_out(vty, "\"%s\":{\n", + get_afi_safi_str(afi, safi, + true)); + } else + vty_out(vty, + "\nFor address family: %s\n", + get_afi_safi_str(afi, safi, + false)); + + if (community) + bgp_show_community(vty, bgp, community, + exact_match, afi, + safi, show_flags); + else + bgp_show(vty, bgp, afi, safi, sh_type, + NULL, show_flags); + if (uj) + vty_out(vty, "}\n"); + } + } + if (uj) + vty_out(vty, "}\n"); + } + return CMD_SUCCESS; } DEFUN (show_ip_bgp_route, @@ -11242,16 +11385,22 @@ DEFPY (show_ip_bgp_instance_all, safi_t safi = SAFI_UNICAST; struct bgp *bgp = NULL; int idx = 0; + uint8_t show_flags = 0; - if (uj) + if (uj) { argc--; + SET_FLAG(show_flags, BGP_SHOW_OPT_JSON); + } + + if (wide) + SET_FLAG(show_flags, BGP_SHOW_OPT_WIDE); bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi, &bgp, uj); if (!idx) return CMD_WARNING; - bgp_show_all_instances_routes_vty(vty, afi, safi, uj, wide); + bgp_show_all_instances_routes_vty(vty, afi, safi, show_flags); return CMD_SUCCESS; } @@ -11261,6 +11410,10 @@ static int bgp_show_regexp(struct vty *vty, struct bgp *bgp, const char *regstr, { regex_t *regex; int rc; + uint8_t show_flags = 0; + + if (use_json) + SET_FLAG(show_flags, BGP_SHOW_OPT_JSON); if (!config_bgp_aspath_validate(regstr)) { vty_out(vty, "Invalid character in REGEX %s\n", @@ -11274,7 +11427,7 @@ static int bgp_show_regexp(struct vty *vty, struct bgp *bgp, const char *regstr, return CMD_WARNING; } - rc = bgp_show(vty, bgp, afi, safi, type, regex, use_json, false); + rc = bgp_show(vty, bgp, afi, safi, type, regex, show_flags); bgp_regex_free(regex); return rc; } @@ -11284,6 +11437,7 @@ static int bgp_show_prefix_list(struct vty *vty, struct bgp *bgp, safi_t safi, enum bgp_show_type type) { struct prefix_list *plist; + uint8_t show_flags = 0; plist = prefix_list_lookup(afi, prefix_list_str); if (plist == NULL) { @@ -11292,7 +11446,7 @@ static int bgp_show_prefix_list(struct vty *vty, struct bgp *bgp, return CMD_WARNING; } - return bgp_show(vty, bgp, afi, safi, type, plist, 0, false); + return bgp_show(vty, bgp, afi, safi, type, plist, show_flags); } static int bgp_show_filter_list(struct vty *vty, struct bgp *bgp, @@ -11300,6 +11454,7 @@ static int bgp_show_filter_list(struct vty *vty, struct bgp *bgp, enum bgp_show_type type) { struct as_list *as_list; + uint8_t show_flags = 0; as_list = as_list_lookup(filter); if (as_list == NULL) { @@ -11308,7 +11463,7 @@ static int bgp_show_filter_list(struct vty *vty, struct bgp *bgp, return CMD_WARNING; } - return bgp_show(vty, bgp, afi, safi, type, as_list, 0, false); + return bgp_show(vty, bgp, afi, safi, type, as_list, show_flags); } static int bgp_show_route_map(struct vty *vty, struct bgp *bgp, @@ -11316,6 +11471,7 @@ static int bgp_show_route_map(struct vty *vty, struct bgp *bgp, enum bgp_show_type type) { struct route_map *rmap; + uint8_t show_flags = 0; rmap = route_map_lookup_by_name(rmap_str); if (!rmap) { @@ -11323,12 +11479,12 @@ static int bgp_show_route_map(struct vty *vty, struct bgp *bgp, return CMD_WARNING; } - return bgp_show(vty, bgp, afi, safi, type, rmap, 0, false); + return bgp_show(vty, bgp, afi, safi, type, rmap, show_flags); } static int bgp_show_community(struct vty *vty, struct bgp *bgp, const char *comstr, int exact, afi_t afi, - safi_t safi, bool use_json) + safi_t safi, uint8_t show_flags) { struct community *com; int ret = 0; @@ -11342,7 +11498,7 @@ static int bgp_show_community(struct vty *vty, struct bgp *bgp, ret = bgp_show(vty, bgp, afi, safi, (exact ? bgp_show_type_community_exact : bgp_show_type_community), - com, use_json, false); + com, show_flags); community_free(&com); return ret; @@ -11353,6 +11509,7 @@ static int bgp_show_community_list(struct vty *vty, struct bgp *bgp, safi_t safi) { struct community_list *list; + uint8_t show_flags = 0; list = community_list_lookup(bgp_clist, com, 0, COMMUNITY_LIST_MASTER); if (list == NULL) { @@ -11363,7 +11520,7 @@ static int bgp_show_community_list(struct vty *vty, struct bgp *bgp, return bgp_show(vty, bgp, afi, safi, (exact ? bgp_show_type_community_list_exact : bgp_show_type_community_list), - list, 0, false); + list, show_flags); } static int bgp_show_prefix_longer(struct vty *vty, struct bgp *bgp, @@ -11372,6 +11529,7 @@ static int bgp_show_prefix_longer(struct vty *vty, struct bgp *bgp, { int ret; struct prefix *p; + uint8_t show_flags = 0; p = prefix_new(); @@ -11381,7 +11539,7 @@ static int bgp_show_prefix_longer(struct vty *vty, struct bgp *bgp, return CMD_WARNING; } - ret = bgp_show(vty, bgp, afi, safi, type, p, 0, false); + ret = bgp_show(vty, bgp, afi, safi, type, p, show_flags); prefix_free(&p); return ret; } @@ -12195,8 +12353,8 @@ static void show_adj_route_header(struct vty *vty, struct bgp *bgp, static void show_adj_route(struct vty *vty, struct peer *peer, afi_t afi, safi_t safi, enum bgp_show_adj_route_type type, - const char *rmap_name, bool use_json, - json_object *json, bool wide) + const char *rmap_name, json_object *json, + uint8_t show_flags) { struct bgp_table *table; struct bgp_adj_in *ain; @@ -12215,6 +12373,8 @@ static void show_adj_route(struct vty *vty, struct peer *peer, afi_t afi, json_object *json_ar = NULL; struct peer_af *paf; bool route_filtered; + bool use_json = CHECK_FLAG(show_flags, BGP_SHOW_OPT_JSON); + bool wide = CHECK_FLAG(show_flags, BGP_SHOW_OPT_WIDE); if (use_json) { json_scode = json_object_new_object(); @@ -12423,9 +12583,10 @@ static void show_adj_route(struct vty *vty, struct peer *peer, afi_t afi, static int peer_adj_routes(struct vty *vty, struct peer *peer, afi_t afi, safi_t safi, enum bgp_show_adj_route_type type, - const char *rmap_name, bool use_json, bool wide) + const char *rmap_name, uint8_t show_flags) { json_object *json = NULL; + bool use_json = CHECK_FLAG(show_flags, BGP_SHOW_OPT_JSON); if (use_json) json = json_object_new_object(); @@ -12460,8 +12621,7 @@ static int peer_adj_routes(struct vty *vty, struct peer *peer, afi_t afi, return CMD_WARNING; } - show_adj_route(vty, peer, afi, safi, type, rmap_name, use_json, json, - wide); + show_adj_route(vty, peer, afi, safi, type, rmap_name, json, show_flags); return CMD_SUCCESS; } @@ -12491,6 +12651,13 @@ DEFPY (show_ip_bgp_instance_neighbor_bestpath_route, struct peer *peer; enum bgp_show_adj_route_type type = bgp_show_adj_route_bestpath; int idx = 0; + uint8_t show_flags = 0; + + if (uj) + SET_FLAG(show_flags, BGP_SHOW_OPT_JSON); + + if (wide) + SET_FLAG(show_flags, BGP_SHOW_OPT_WIDE); bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi, &bgp, uj); @@ -12505,18 +12672,20 @@ DEFPY (show_ip_bgp_instance_neighbor_bestpath_route, if (!peer) return CMD_WARNING; - return peer_adj_routes(vty, peer, afi, safi, type, rmap_name, uj, wide); + return peer_adj_routes(vty, peer, afi, safi, type, rmap_name, + show_flags); } DEFPY (show_ip_bgp_instance_neighbor_advertised_route, show_ip_bgp_instance_neighbor_advertised_route_cmd, - "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] neighbors <A.B.C.D|X:X::X:X|WORD> <advertised-routes|received-routes|filtered-routes> [route-map WORD] [json$uj | wide$wide]", + "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] [all$all] neighbors <A.B.C.D|X:X::X:X|WORD> <advertised-routes|received-routes|filtered-routes> [route-map WORD] [json$uj | wide$wide]", SHOW_STR IP_STR BGP_STR BGP_INSTANCE_HELP_STR BGP_AFI_HELP_STR BGP_SAFI_WITH_LABEL_HELP_STR + "Display the entries for all address families\n" "Detailed information on TCP and BGP neighbor connections\n" "Neighbor to display information about\n" "Neighbor to display information about\n" @@ -12537,9 +12706,25 @@ DEFPY (show_ip_bgp_instance_neighbor_advertised_route, struct peer *peer; enum bgp_show_adj_route_type type = bgp_show_adj_route_advertised; int idx = 0; + bool first = true; + uint8_t show_flags = 0; - if (uj) + if (uj) { argc--; + SET_FLAG(show_flags, BGP_SHOW_OPT_JSON); + } + + if (all) { + SET_FLAG(show_flags, BGP_SHOW_OPT_AFI_ALL); + if (argv_find(argv, argc, "ipv4", &idx)) + SET_FLAG(show_flags, BGP_SHOW_OPT_AFI_IP); + + if (argv_find(argv, argc, "ipv6", &idx)) + SET_FLAG(show_flags, BGP_SHOW_OPT_AFI_IP6); + } + + if (wide) + SET_FLAG(show_flags, BGP_SHOW_OPT_WIDE); bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi, &bgp, uj); @@ -12564,7 +12749,66 @@ DEFPY (show_ip_bgp_instance_neighbor_advertised_route, if (argv_find(argv, argc, "route-map", &idx)) rmap_name = argv[++idx]->arg; - return peer_adj_routes(vty, peer, afi, safi, type, rmap_name, uj, wide); + if (!all) + return peer_adj_routes(vty, peer, afi, safi, type, rmap_name, + show_flags); + if (uj) + vty_out(vty, "{\n"); + + if (CHECK_FLAG(show_flags, BGP_SHOW_OPT_AFI_IP) + || CHECK_FLAG(show_flags, BGP_SHOW_OPT_AFI_IP6)) { + afi = CHECK_FLAG(show_flags, BGP_SHOW_OPT_AFI_IP) ? AFI_IP + : AFI_IP6; + FOREACH_SAFI (safi) { + if (strmatch(get_afi_safi_str(afi, safi, true), + "Unknown")) + continue; + + if (!bgp_afi_safi_peer_exists(bgp, afi, safi)) + continue; + + if (uj) { + if (first) + first = false; + else + vty_out(vty, ",\n"); + vty_out(vty, "\"%s\":", + get_afi_safi_str(afi, safi, true)); + } else + vty_out(vty, "\nFor address family: %s\n", + get_afi_safi_str(afi, safi, false)); + + peer_adj_routes(vty, peer, afi, safi, type, rmap_name, + show_flags); + } + } else { + FOREACH_AFI_SAFI (afi, safi) { + if (strmatch(get_afi_safi_str(afi, safi, true), + "Unknown")) + continue; + + if (!bgp_afi_safi_peer_exists(bgp, afi, safi)) + continue; + + if (uj) { + if (first) + first = false; + else + vty_out(vty, ",\n"); + vty_out(vty, "\"%s\":", + get_afi_safi_str(afi, safi, true)); + } else + vty_out(vty, "\nFor address family: %s\n", + get_afi_safi_str(afi, safi, false)); + + peer_adj_routes(vty, peer, afi, safi, type, rmap_name, + show_flags); + } + } + if (uj) + vty_out(vty, "}\n"); + + return CMD_SUCCESS; } DEFUN (show_ip_bgp_neighbor_received_prefix_filter, @@ -12653,6 +12897,11 @@ static int bgp_show_neighbor_route(struct vty *vty, struct peer *peer, afi_t afi, safi_t safi, enum bgp_show_type type, bool use_json) { + uint8_t show_flags = 0; + + if (use_json) + SET_FLAG(show_flags, BGP_SHOW_OPT_JSON); + /* labeled-unicast routes live in the unicast table */ if (safi == SAFI_LABELED_UNICAST) safi = SAFI_UNICAST; @@ -12672,8 +12921,7 @@ static int bgp_show_neighbor_route(struct vty *vty, struct peer *peer, return CMD_WARNING; } - return bgp_show(vty, peer->bgp, afi, safi, type, &peer->su, use_json, - false); + return bgp_show(vty, peer->bgp, afi, safi, type, &peer->su, show_flags); } DEFUN (show_ip_bgp_flowspec_routes_detailed, @@ -12693,17 +12941,20 @@ DEFUN (show_ip_bgp_flowspec_routes_detailed, struct bgp *bgp = NULL; int idx = 0; bool uj = use_json(argc, argv); + uint8_t show_flags = 0; - if (uj) + if (uj) { argc--; + SET_FLAG(show_flags, BGP_SHOW_OPT_JSON); + } bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi, &bgp, uj); if (!idx) return CMD_WARNING; - return bgp_show(vty, bgp, afi, safi, bgp_show_type_detail, NULL, uj, - false); + return bgp_show(vty, bgp, afi, safi, bgp_show_type_detail, NULL, + show_flags); } DEFUN (show_ip_bgp_neighbor_routes, diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index 3f734d2672..3407884897 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -498,6 +498,13 @@ DECLARE_HOOK(bgp_process, struct peer *peer, bool withdraw), (bgp, afi, safi, bn, peer, withdraw)) +/* BGP show options */ +#define BGP_SHOW_OPT_JSON (1 << 0) +#define BGP_SHOW_OPT_WIDE (1 << 1) +#define BGP_SHOW_OPT_AFI_ALL (1 << 2) +#define BGP_SHOW_OPT_AFI_IP (1 << 3) +#define BGP_SHOW_OPT_AFI_IP6 (1 << 4) + /* Prototypes. */ extern void bgp_rib_remove(struct bgp_dest *dest, struct bgp_path_info *pi, struct peer *peer, afi_t afi, safi_t safi); diff --git a/bgpd/bgp_rpki.c b/bgpd/bgp_rpki.c index 63cfacf678..ca47fb316a 100644 --- a/bgpd/bgp_rpki.c +++ b/bgpd/bgp_rpki.c @@ -1060,67 +1060,6 @@ DEFUN (no_rpki_retry_interval, return CMD_SUCCESS; } -#if (CONFDATE > 20200901) -CPP_NOTICE("bgpd: time to remove rpki timeout") -CPP_NOTICE("bgpd: this includes rpki_timeout and rpki_synchronisation_timeout") -#endif - -DEFPY_HIDDEN (rpki_timeout, - rpki_timeout_cmd, - "rpki timeout (1-4294967295)$to_arg", - RPKI_OUTPUT_STRING - "Set timeout\n" - "Timeout value\n") -{ - vty_out(vty, - "This config option is deprecated, and is scheduled for removal.\n"); - vty_out(vty, - "This functionality has also already been removed because it caused bugs and was pointless\n"); - return CMD_SUCCESS; -} - -DEFUN_HIDDEN (no_rpki_timeout, - no_rpki_timeout_cmd, - "no rpki timeout", - NO_STR - RPKI_OUTPUT_STRING - "Set timeout back to default\n") -{ - vty_out(vty, - "This config option is deprecated, and is scheduled for removal.\n"); - vty_out(vty, - "This functionality has also already been removed because it caused bugs and was pointless\n"); - return CMD_SUCCESS; -} - -DEFPY_HIDDEN (rpki_synchronisation_timeout, - rpki_synchronisation_timeout_cmd, - "rpki initial-synchronisation-timeout (1-4294967295)$ito_arg", - RPKI_OUTPUT_STRING - "Set a timeout for the initial synchronisation of prefix validation data\n" - "Timeout value\n") -{ - vty_out(vty, - "This config option is deprecated, and is scheduled for removal.\n"); - vty_out(vty, - "This functionality has also already been removed because it caused bugs and was pointless\n"); - return CMD_SUCCESS; -} - -DEFUN_HIDDEN (no_rpki_synchronisation_timeout, - no_rpki_synchronisation_timeout_cmd, - "no rpki initial-synchronisation-timeout", - NO_STR - RPKI_OUTPUT_STRING - "Set the initial synchronisation timeout back to default (30 sec.)\n") -{ - vty_out(vty, - "This config option is deprecated, and is scheduled for removal.\n"); - vty_out(vty, - "This functionality has also already been removed because it caused bugs and was pointless\n"); - return CMD_SUCCESS; -} - DEFPY (rpki_cache, rpki_cache_cmd, "rpki cache <A.B.C.D|WORD><TCPPORT|(1-65535)$sshport SSH_UNAME SSH_PRIVKEY SSH_PUBKEY [SERVER_PUBKEY]> preference (1-255)", @@ -1516,14 +1455,6 @@ static void install_cli_commands(void) install_element(RPKI_NODE, &rpki_retry_interval_cmd); install_element(RPKI_NODE, &no_rpki_retry_interval_cmd); - /* Install rpki timeout commands */ - install_element(RPKI_NODE, &rpki_timeout_cmd); - install_element(RPKI_NODE, &no_rpki_timeout_cmd); - - /* Install rpki synchronisation timeout commands */ - install_element(RPKI_NODE, &rpki_synchronisation_timeout_cmd); - install_element(RPKI_NODE, &no_rpki_synchronisation_timeout_cmd); - /* Install rpki cache commands */ install_element(RPKI_NODE, &rpki_cache_cmd); install_element(RPKI_NODE, &no_rpki_cache_cmd); diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index d80667699a..505f743e29 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -1642,16 +1642,91 @@ DEFUN (no_bgp_maxmed_onstartup, return CMD_SUCCESS; } -static int bgp_update_delay_config_vty(struct vty *vty, const char *delay, - const char *wait) +static int bgp_global_update_delay_config_vty(struct vty *vty, + uint16_t update_delay, + uint16_t establish_wait) +{ + struct listnode *node, *nnode; + struct bgp *bgp; + bool vrf_cfg = false; + + /* + * See if update-delay is set per-vrf and warn user to delete it + * Note that we only need to check this if this is the first time + * setting the global config. + */ + if (bm->v_update_delay == BGP_UPDATE_DELAY_DEF) { + for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) { + if (bgp->v_update_delay != BGP_UPDATE_DELAY_DEF) { + vty_out(vty, + "%% update-delay configuration found in vrf %s\n", + bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT + ? VRF_DEFAULT_NAME + : bgp->name); + vrf_cfg = true; + } + } + } + + if (vrf_cfg) { + vty_out(vty, + "%%Failed: global update-delay config not permitted\n"); + return CMD_WARNING; + } + + if (!establish_wait) { /* update-delay <delay> */ + bm->v_update_delay = update_delay; + bm->v_establish_wait = bm->v_update_delay; + } else { + /* update-delay <delay> <establish-wait> */ + if (update_delay < establish_wait) { + vty_out(vty, + "%%Failed: update-delay less than the establish-wait!\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + bm->v_update_delay = update_delay; + bm->v_establish_wait = establish_wait; + } + + for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) { + bgp->v_update_delay = bm->v_update_delay; + bgp->v_establish_wait = bm->v_establish_wait; + } + + return CMD_SUCCESS; +} + +static int bgp_global_update_delay_deconfig_vty(struct vty *vty) +{ + struct listnode *node, *nnode; + struct bgp *bgp; + + bm->v_update_delay = BGP_UPDATE_DELAY_DEF; + bm->v_establish_wait = bm->v_update_delay; + + for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) { + bgp->v_update_delay = bm->v_update_delay; + bgp->v_establish_wait = bm->v_establish_wait; + } + + return CMD_SUCCESS; +} + +static int bgp_update_delay_config_vty(struct vty *vty, uint16_t update_delay, + uint16_t establish_wait) { VTY_DECLVAR_CONTEXT(bgp, bgp); - uint16_t update_delay; - uint16_t establish_wait; - update_delay = strtoul(delay, NULL, 10); + /* if configured globally, per-instance config is not allowed */ + if (bm->v_update_delay) { + vty_out(vty, + "%%Failed: per-vrf update-delay config not permitted with global update-delay\n"); + return CMD_WARNING_CONFIG_FAILED; + } + - if (!wait) /* update-delay <delay> */ + if (!establish_wait) /* update-delay <delay> */ { bgp->v_update_delay = update_delay; bgp->v_establish_wait = bgp->v_update_delay; @@ -1659,7 +1734,6 @@ static int bgp_update_delay_config_vty(struct vty *vty, const char *delay, } /* update-delay <delay> <establish-wait> */ - establish_wait = atoi(wait); if (update_delay < establish_wait) { vty_out(vty, "%%Failed: update-delay less than the establish-wait!\n"); @@ -1676,6 +1750,12 @@ static int bgp_update_delay_deconfig_vty(struct vty *vty) { VTY_DECLVAR_CONTEXT(bgp, bgp); + /* If configured globally, cannot remove from one bgp instance */ + if (bm->v_update_delay) { + vty_out(vty, + "%%Failed: bgp update-delay configured globally. Delete per-vrf not permitted\n"); + return CMD_WARNING_CONFIG_FAILED; + } bgp->v_update_delay = BGP_UPDATE_DELAY_DEF; bgp->v_establish_wait = bgp->v_update_delay; @@ -1684,7 +1764,8 @@ static int bgp_update_delay_deconfig_vty(struct vty *vty) void bgp_config_write_update_delay(struct vty *vty, struct bgp *bgp) { - if (bgp->v_update_delay != BGP_UPDATE_DELAY_DEF) { + /* If configured globally, no need to display per-instance value */ + if (bgp->v_update_delay != bm->v_update_delay) { vty_out(vty, " update-delay %d", bgp->v_update_delay); if (bgp->v_update_delay != bgp->v_establish_wait) vty_out(vty, " %d", bgp->v_establish_wait); @@ -1692,39 +1773,51 @@ void bgp_config_write_update_delay(struct vty *vty, struct bgp *bgp) } } +/* Global update-delay configuration */ +DEFPY (bgp_global_update_delay, + bgp_global_update_delay_cmd, + "bgp update-delay (0-3600)$delay [(1-3600)$wait]", + BGP_STR + "Force initial delay for best-path and updates for all bgp instances\n" + "Max delay in seconds\n" + "Establish wait in seconds\n") +{ + return bgp_global_update_delay_config_vty(vty, delay, wait); +} -/* Update-delay configuration */ -DEFUN (bgp_update_delay, - bgp_update_delay_cmd, - "update-delay (0-3600)", +/* Global update-delay deconfiguration */ +DEFPY (no_bgp_global_update_delay, + no_bgp_global_update_delay_cmd, + "no bgp update-delay [(0-3600) [(1-3600)]]", + NO_STR + BGP_STR "Force initial delay for best-path and updates\n" - "Seconds\n") + "Max delay in seconds\n" + "Establish wait in seconds\n") { - int idx_number = 1; - return bgp_update_delay_config_vty(vty, argv[idx_number]->arg, NULL); + return bgp_global_update_delay_deconfig_vty(vty); } -DEFUN (bgp_update_delay_establish_wait, - bgp_update_delay_establish_wait_cmd, - "update-delay (0-3600) (1-3600)", +/* Update-delay configuration */ + +DEFPY (bgp_update_delay, + bgp_update_delay_cmd, + "update-delay (0-3600)$delay [(1-3600)$wait]", "Force initial delay for best-path and updates\n" - "Seconds\n" - "Seconds\n") + "Max delay in seconds\n" + "Establish wait in seconds\n") { - int idx_number = 1; - int idx_number_2 = 2; - return bgp_update_delay_config_vty(vty, argv[idx_number]->arg, - argv[idx_number_2]->arg); + return bgp_update_delay_config_vty(vty, delay, wait); } /* Update-delay deconfiguration */ -DEFUN (no_bgp_update_delay, +DEFPY (no_bgp_update_delay, no_bgp_update_delay_cmd, "no update-delay [(0-3600) [(1-3600)]]", NO_STR "Force initial delay for best-path and updates\n" - "Seconds\n" - "Seconds\n") + "Max delay in seconds\n" + "Establish wait in seconds\n") { return bgp_update_delay_deconfig_vty(vty); } @@ -9774,15 +9867,16 @@ int bgp_show_summary_vty(struct vty *vty, const char *name, afi_t afi, } /* `show [ip] bgp summary' commands. */ -DEFUN (show_ip_bgp_summary, +DEFPY (show_ip_bgp_summary, show_ip_bgp_summary_cmd, - "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] summary [established|failed] [json]", + "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] [all$all] summary [established|failed] [json$uj]", SHOW_STR IP_STR BGP_STR BGP_INSTANCE_HELP_STR BGP_AFI_HELP_STR BGP_SAFI_WITH_LABEL_HELP_STR + "Display the entries for all address families\n" "Summary of BGP neighbor status\n" "Show only sessions in Established state\n" "Show only sessions not in Established state\n" @@ -9797,7 +9891,7 @@ DEFUN (show_ip_bgp_summary, int idx = 0; /* show [ip] bgp */ - if (argv_find(argv, argc, "ip", &idx)) + if (!all && argv_find(argv, argc, "ip", &idx)) afi = AFI_IP; /* [<vrf> VIEWVRFNAME] */ if (argv_find(argv, argc, "vrf", &idx)) { @@ -9817,8 +9911,6 @@ DEFUN (show_ip_bgp_summary, if (argv_find(argv, argc, "established", &idx)) show_established = true; - bool uj = use_json(argc, argv); - return bgp_show_summary_vty(vty, vrf, afi, safi, show_failed, show_established, uj); } @@ -15429,6 +15521,13 @@ int bgp_config_write(struct vty *vty) vty_out(vty, "bgp route-map delay-timer %u\n", bm->rmap_update_timer); + if (bm->v_update_delay != BGP_UPDATE_DELAY_DEF) { + vty_out(vty, "bgp update-delay %d", bm->v_update_delay); + if (bm->v_update_delay != bm->v_establish_wait) + vty_out(vty, " %d", bm->v_establish_wait); + vty_out(vty, "\n"); + } + /* BGP configuration. */ for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) { @@ -15943,6 +16042,10 @@ void bgp_vty_init(void) install_element(CONFIG_NODE, &bgp_set_route_map_delay_timer_cmd); install_element(CONFIG_NODE, &no_bgp_set_route_map_delay_timer_cmd); + /* global bgp update-delay command */ + install_element(CONFIG_NODE, &bgp_global_update_delay_cmd); + install_element(CONFIG_NODE, &no_bgp_global_update_delay_cmd); + /* Dummy commands (Currently not supported) */ install_element(BGP_NODE, &no_synchronization_cmd); install_element(BGP_NODE, &no_auto_summary_cmd); @@ -15983,7 +16086,6 @@ void bgp_vty_init(void) /* bgp update-delay command */ install_element(BGP_NODE, &bgp_update_delay_cmd); install_element(BGP_NODE, &no_bgp_update_delay_cmd); - install_element(BGP_NODE, &bgp_update_delay_establish_wait_cmd); install_element(BGP_NODE, &bgp_wpkt_quanta_cmd); install_element(BGP_NODE, &bgp_rpkt_quanta_cmd); diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 15bd6d33b8..11e872f6b4 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -1144,7 +1144,8 @@ static bool update_ipv6nh_for_route_install(int nh_othervrf, struct bgp *nh_bgp, api_nh->ifindex = 0; } } - api_nh->gate.ipv6 = *nexthop; + if (nexthop) + api_nh->gate.ipv6 = *nexthop; return true; } @@ -2153,10 +2154,10 @@ static int rule_notify_owner(ZAPI_CALLBACK_ARGS) enum zapi_rule_notify_owner note; struct bgp_pbr_action *bgp_pbra; struct bgp_pbr_rule *bgp_pbr = NULL; - ifindex_t ifi; + char ifname[INTERFACE_NAMSIZ + 1]; if (!zapi_rule_notify_decode(zclient->ibuf, &seqno, &priority, &unique, - &ifi, ¬e)) + ifname, ¬e)) return -1; bgp_pbra = bgp_pbr_action_rule_lookup(vrf_id, unique); diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index d638c6686e..b654e85206 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -2969,7 +2969,8 @@ static struct bgp *bgp_create(as_t *as, const char *name, bgp->gr_info[afi][safi].route_list = list_new(); } - bgp->v_update_delay = BGP_UPDATE_DELAY_DEF; + bgp->v_update_delay = bm->v_update_delay; + bgp->v_establish_wait = bm->v_establish_wait; bgp->default_local_pref = BGP_DEFAULT_LOCAL_PREF; bgp->default_subgroup_pkt_queue_max = BGP_DEFAULT_SUBGROUP_PKT_QUEUE_MAX; @@ -7005,6 +7006,8 @@ void bgp_master_init(struct thread_master *master, const int buffer_size) bm->start_time = bgp_clock(); bm->t_rmap_update = NULL; bm->rmap_update_timer = RMAP_DEFAULT_UPDATE_TIMER; + bm->v_update_delay = BGP_UPDATE_DELAY_DEF; + bm->v_establish_wait = BGP_UPDATE_DELAY_DEF; bm->terminating = false; bm->socket_buffer = buffer_size; diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 8707ebacb6..2aa0690025 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -169,6 +169,10 @@ struct bgp_master { /* EVPN multihoming */ struct bgp_evpn_mh_info *mh_info; + /* global update-delay timer values */ + uint16_t v_update_delay; + uint16_t v_establish_wait; + bool terminating; /* global flag that sigint terminate seen */ QOBJ_FIELDS }; diff --git a/configure.ac b/configure.ac index ae116ef754..a952cf7063 100755 --- a/configure.ac +++ b/configure.ac @@ -7,7 +7,7 @@ ## AC_PREREQ([2.60]) -AC_INIT([frr], [7.5-dev], [https://github.com/frrouting/frr/issues]) +AC_INIT([frr], [7.6-dev], [https://github.com/frrouting/frr/issues]) PACKAGE_URL="https://frrouting.org/" AC_SUBST([PACKAGE_URL]) PACKAGE_FULLNAME="FRRouting" @@ -1728,8 +1728,8 @@ AC_SUBST([SNMP_CFLAGS]) dnl --------------- dnl libyang dnl --------------- -PKG_CHECK_MODULES([LIBYANG], [libyang >= 0.16.105], , [ - AC_MSG_ERROR([libyang (>= 0.16.105) was not found on your system.]) +PKG_CHECK_MODULES([LIBYANG], [libyang >= 1.0.184], , [ + AC_MSG_ERROR([libyang (>= 1.0.184) was not found on your system.]) ]) ac_cflags_save="$CFLAGS" CFLAGS="$CFLAGS $LIBYANG_CFLAGS" diff --git a/debian/control b/debian/control index f4275471d5..fca6956760 100644 --- a/debian/control +++ b/debian/control @@ -24,7 +24,7 @@ Build-Depends: libsnmp-dev, libssh-dev <!pkg.frr.nortrlib>, libsystemd-dev <!pkg.frr.nosystemd>, - libyang-dev (>= 0.16.74), + libyang-dev (>= 1.0.184), pkg-config, python3, python3-dev, diff --git a/doc/developer/building-libyang.rst b/doc/developer/building-libyang.rst index f50b8cf72d..5f82447d74 100644 --- a/doc/developer/building-libyang.rst +++ b/doc/developer/building-libyang.rst @@ -5,12 +5,16 @@ library. **Option 1: Binary Install** -The FRR project builds binary ``libyang`` packages, which we offer for download -`here <https://ci1.netdef.org/browse/LIBYANG-YANGRELEASE/latestSuccessful/artifact>`_. +The FRR project builds some binary ``libyang`` packages. + +RPM packages are at our `RPM repository <https://rpm.frrouting.org>`_. + +DEB packages are available as CI artifacts `here +<https://ci1.netdef.org/browse/LIBYANG-LY1REL-DEB10AMD64-4/artifact>`_. .. warning:: - ``libyang`` version 0.16.105 or newer is required to build FRR. + ``libyang`` version 1.0.184 or newer is required to build FRR. .. note:: @@ -50,8 +54,3 @@ The FRR project builds binary ``libyang`` packages, which we offer for download -D CMAKE_BUILD_TYPE:String="Release" .. make sudo make install - -When building ``libyang`` version ``0.16.x`` it's also necessary to pass the -``-DENABLE_CACHE=OFF`` parameter to ``cmake`` to work around a -`known bug <https://github.com/CESNET/libyang/issues/752>`_ in libyang. - diff --git a/doc/developer/lists.rst b/doc/developer/lists.rst index 9355141aa4..28b21533c0 100644 --- a/doc/developer/lists.rst +++ b/doc/developer/lists.rst @@ -497,6 +497,7 @@ API for hash tables Items that compare as equal cannot be inserted. Refer to the notes about sorted structures in the previous section. + .. c:function:: void Z_init_size(struct Z_head *, size_t size) Same as :c:func:`Z_init()` but preset the minimum hash table to @@ -506,6 +507,66 @@ Hash tables also support :c:func:`Z_add()` and :c:func:`Z_find()` with the same semantics as noted above. :c:func:`Z_find_gteq()` and :c:func:`Z_find_lt()` are **not** provided for hash tables. +Hash table invariants +^^^^^^^^^^^^^^^^^^^^^ + +There are several ways to injure yourself using the hash table API. + +First, note that there are two functions related to computing uniqueness of +objects inserted into the hash table. There is a hash function and a comparison +function. The hash function computes the hash of the object. Our hash table +implementation uses `chaining +<https://en.wikipedia.org/wiki/Hash_table#Separate_chaining_with_linked_lists>`_. +This means that your hash function does not have to be perfect; multiple +objects having the same computed hash will be placed into a linked list +corresponding to that key. The closer to perfect the hash function, the better +performance, as items will be more evenly distributed and the chain length will +not be long on any given lookup, minimizing the number of list operations +required to find the correct item. However, the comparison function *must* be +perfect, in the sense that any two unique items inserted into the hash table +must compare not equal. At insertion time, if you try to insert an item that +compares equal to an existing item the insertion will not happen and +``hash_get()`` will return the existing item. However, this invariant *must* be +maintained while the object is in the hash table. Suppose you insert items +``A`` and ``B`` into the hash table which both hash to the same value ``1234`` +but do not compare equal. They will be placed in a chain like so:: + + 1234 : A -> B + +Now suppose you do something like this elsewhere in the code:: + + *A = *B + +I.e. you copy all fields of ``B`` into ``A``, such that the comparison function +now says that they are equal based on their contents. At this point when you +look up ``B`` in the hash table, ``hash_get()`` will search the chain for the +first item that compares equal to ``B``, which will be ``A``. This leads to +insidious bugs. + +.. warning:: + + Never modify the values looked at by the comparison or hash functions after + inserting an item into a hash table. + +A similar situation can occur with the hash allocation function. ``hash_get()`` +accepts a function pointer that it will call to get the item that should be +inserted into the list if the provided item is not already present. There is a +builtin function, ``hash_alloc_intern``, that will simply return the item you +provided; if you always want to store the value you pass to ``hash_get`` you +should use this one. If you choose to provide a different one, that function +*must* return a new item that hashes and compares equal to the one you provided +to ``hash_get()``. If it does not the behavior of the hash table is undefined. + +.. warning:: + + Always make sure your hash allocation function returns a value that hashes + and compares equal to the item you provided to ``hash_get()``. + +Finally, if you maintain pointers to items you have inserted into a hash table, +then before deallocating them you must release them from the hash table. This +is basic memory management but worth repeating as bugs have arisen from failure +to do this. + API for heaps ------------- diff --git a/doc/developer/process-architecture.rst b/doc/developer/process-architecture.rst index 6e0eb68188..6a028d0000 100644 --- a/doc/developer/process-architecture.rst +++ b/doc/developer/process-architecture.rst @@ -94,7 +94,9 @@ irrelevant for the time being) for the specific type. For example, to add a thread_add_read(struct thread_master *master, int (*handler)(struct thread *), void *arg, int fd, struct thread **ref); The ``struct thread`` is then created and added to the appropriate internal -datastructure within the ``threadmaster``. +datastructure within the ``threadmaster``. Note that the ``READ`` and +``WRITE`` tasks are independent - a ``READ`` task only tests for +readability, for example. The Event Loop ^^^^^^^^^^^^^^ diff --git a/doc/developer/topotests.rst b/doc/developer/topotests.rst index b32f2bbf49..5486fd826d 100644 --- a/doc/developer/topotests.rst +++ b/doc/developer/topotests.rst @@ -722,7 +722,7 @@ Example: .. code:: py # For all registered routers, load the zebra configuration file - for rname, router in router_list.iteritems(): + for rname, router in router_list.items(): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, '{}/zebra.conf'.format(rname)) diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index 25eaa577c1..9983911581 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -1103,6 +1103,41 @@ Redistribution Redistribute VNC direct (not via zebra) routes to BGP process. +.. index:: bgp update-delay MAX-DELAY +.. clicmd:: bgp update-delay MAX-DELAY + +.. index:: bgp update-delay MAX-DELAY ESTABLISH-WAIT +.. clicmd:: bgp update-delay MAX-DELAY ESTABLISH-WAIT + + 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 at the global level and applies to all bgp instances/vrfs. It + cannot be used at the same time as the "update-delay" command described below, + which is entered in each bgp instance/vrf desired to delay update installation + and advertisements. The global and per-vrf approaches to defining update-delay + are mutually exclusive. + + When applicable, read-only mode would begin as soon as the first peer reaches + Established status and a timer for max-delay seconds is started. During this + mode BGP doesn't run any best-path or generate any updates to its peers. This + mode continues until: + + 1. All the configured peers, except the shutdown peers, have sent explicit EOR + (End-Of-RIB) or an implicit-EOR. The first keep-alive after BGP has reached + Established is considered an implicit-EOR. + If the establish-wait optional value is given, then BGP will wait for + peers to reach established from the beginning of the update-delay till the + establish-wait period is over, i.e. the minimum set of established peers for + which EOR is expected would be peers established during the establish-wait + window, not necessarily all the configured neighbors. + 2. max-delay period is over. + + On hitting any of the above two conditions, BGP resumes the decision process + and generates updates to its peers. + + Default max-delay is 0, i.e. the feature is off by default. + + .. index:: update-delay MAX-DELAY .. clicmd:: update-delay MAX-DELAY @@ -1110,12 +1145,17 @@ Redistribution .. clicmd:: update-delay MAX-DELAY ESTABLISH-WAIT This feature is used to enable read-only mode on BGP process restart or when - BGP process is cleared using 'clear ip bgp \*'. When applicable, read-only - mode would begin as soon as the first peer reaches Established status and a - timer for max-delay seconds is started. - - During this mode BGP doesn't run any best-path or generate any updates to its - peers. This mode continues until: + 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. + 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 + exclusive. + + When applicable, read-only mode would begin as soon as the first peer reaches + Established status and a timer for max-delay seconds is started. During this + mode BGP doesn't run any best-path or generate any updates to its peers. This + mode continues until: 1. All the configured peers, except the shutdown peers, have sent explicit EOR (End-Of-RIB) or an implicit-EOR. The first keep-alive after BGP has reached @@ -2500,6 +2540,26 @@ the same behavior of using same next-hop and RMAC values. Enables or disables advertise-pip feature, specifiy system-IP and/or system-MAC parameters. ++Support with VRF network namespace backend ++^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +It is possible to separate overlay networks contained in VXLAN interfaces from +underlay networks by using VRFs. VRF-lite and VRF-netns backends can be used for +that. In the latter case, it is necessary to set both bridge and vxlan interface +in the same network namespace, as below example illustrates: + +.. code-block:: shell + + # linux shell + ip netns add vrf1 + ip link add name vxlan101 type vxlan id 101 dstport 4789 dev eth0 local 10.1.1.1 + ip link set dev vxlan101 netns vrf1 + ip netns exec vrf1 ip link set dev lo up + ip netns exec vrf1 brctl addbr bridge101 + ip netns exec vrf1 brctl addif bridge101 vxlan101 + +This makes it possible to separate not only layer 3 networks like VRF-lite networks. +Also, VRF netns based make possible to separate layer 2 networks on separate VRF +instances. .. _bgp-debugging: @@ -2681,17 +2741,17 @@ daemon project, while :clicmd:`show bgp` command is the new format. The choice has been done to keep old format with IPv4 routing table, while new format displays IPv6 routing table. -.. index:: show ip bgp [wide] -.. clicmd:: show ip bgp [wide] +.. index:: show ip bgp [all] [wide|json] +.. clicmd:: show ip bgp [all] [wide|json] -.. index:: show ip bgp A.B.C.D [wide] -.. clicmd:: show ip bgp A.B.C.D [wide] +.. index:: show ip bgp A.B.C.D [json] +.. clicmd:: show ip bgp A.B.C.D [json] -.. index:: show bgp [wide] -.. clicmd:: show bgp [wide] +.. index:: show bgp [all] [wide|json] +.. clicmd:: show bgp [all] [wide|json] -.. index:: show bgp X:X::X:X [wide] -.. clicmd:: show bgp X:X::X:X [wide] +.. index:: show bgp X:X::X:X [json] +.. clicmd:: show bgp X:X::X:X [json] These commands display BGP routes. When no route is specified, the default is to display all BGP routes. @@ -2713,6 +2773,11 @@ displays IPv6 routing table. This is especially handy dealing with IPv6 prefixes and if :clicmd:`[no] bgp default show-nexthop-hostname` is enabled. + If _all_ option is specified, _ip_ keyword is ignored, show bgp all and + show ip bgp all commands display routes for all AFIs and SAFIs. + + If _json_ option is specified, output is displayed in JSON format. + Some other commands provide additional options for filtering the output. .. index:: show [ip] bgp regexp LINE @@ -2721,8 +2786,8 @@ Some other commands provide additional options for filtering the output. This command displays BGP routes using AS path regular expression (:ref:`bgp-regular-expressions`). -.. index:: show [ip] bgp summary -.. clicmd:: show [ip] bgp summary +.. index:: show [ip] bgp [all] summary [json] +.. clicmd:: show [ip] bgp [all] summary [json] Show a bgp peer summary for the specified address family. @@ -2731,8 +2796,8 @@ and should no longer be used. In order to reach the other BGP routing tables other than the IPv6 routing table given by :clicmd:`show bgp`, the new command structure is extended with :clicmd:`show bgp [afi] [safi]`. -.. index:: show bgp [afi] [safi] -.. clicmd:: show bgp [afi] [safi] +.. index:: show bgp [afi] [safi] [all] [wide|json] +.. clicmd:: show bgp [afi] [safi] [all] [wide|json] .. index:: show bgp <ipv4|ipv6> <unicast|multicast|vpn|labeled-unicast> .. clicmd:: show bgp <ipv4|ipv6> <unicast|multicast|vpn|labeled-unicast> @@ -2748,20 +2813,20 @@ structure is extended with :clicmd:`show bgp [afi] [safi]`. Additionally, you can also filter this output by route type. -.. index:: show bgp [afi] [safi] summary -.. clicmd:: show bgp [afi] [safi] summary +.. index:: show bgp [afi] [safi] [all] summary [json] +.. clicmd:: show bgp [afi] [safi] [all] summary [json] Show a bgp peer summary for the specified address family, and subsequent address-family. -.. index:: show bgp [afi] [safi] summary failed [json] -.. clicmd:: show bgp [afi] [safi] summary failed [json] +.. index:: show bgp [afi] [safi] [all] summary failed [json] +.. clicmd:: show bgp [afi] [safi] [all] summary failed [json] Show a bgp peer summary for peers that are not succesfully exchanging routes for the specified address family, and subsequent address-family. -.. index:: show bgp [afi] [safi] summary established [json] -.. clicmd:: show bgp [afi] [safi] summary established [json] +.. index:: show bgp [afi] [safi] [all] summary established [json] +.. clicmd:: show bgp [afi] [safi] [all] summary established [json] Show a bgp peer summary for peers that are succesfully exchanging routes for the specified address family, and subsequent address-family. @@ -2772,14 +2837,14 @@ structure is extended with :clicmd:`show bgp [afi] [safi]`. This command shows information on a specific BGP peer of the relevant afi and safi selected. -.. index:: show bgp [afi] [safi] dampening dampened-paths -.. clicmd:: show bgp [afi] [safi] dampening dampened-paths +.. index:: show bgp [afi] [safi] [all] dampening dampened-paths [wide|json] +.. clicmd:: show bgp [afi] [safi] [all] dampening dampened-paths [wide|json] Display paths suppressed due to dampening of the selected afi and safi selected. -.. index:: show bgp [afi] [safi] dampening flap-statistics -.. clicmd:: show bgp [afi] [safi] dampening flap-statistics +.. index:: show bgp [afi] [safi] [all] dampening flap-statistics [wide|json] +.. clicmd:: show bgp [afi] [safi] [all] dampening flap-statistics [wide|json] Display flap statistics of routes of the selected afi and safi selected. @@ -2793,6 +2858,31 @@ structure is extended with :clicmd:`show bgp [afi] [safi]`. Display statistics of routes of all the afi and safi. +.. index:: show [ip] bgp [afi] [safi] [all] cidr-only [wide|json] +.. clicmd:: show [ip] bgp [afi] [safi] [all] cidr-only [wide|json] + + Display routes with non-natural netmasks. + +.. index:: show [ip] bgp [afi] [safi] [all] neighbors A.B.C.D [advertised-routes|received-routes|filtered-routes] [json|wide] +.. clicmd:: show [ip] bgp [afi] [safi] [all] neighbors A.B.C.D [advertised-routes|received-routes|filtered-routes] [json|wide] + + Display the routes advertised to a BGP neighbor or received routes + from neighbor or filtered routes received from neighbor based on the + option specified. + + If _wide_ option is specified, then the prefix table's width is increased + to fully display the prefix and the nexthop. + + This is especially handy dealing with IPv6 prefixes and + if :clicmd:`[no] bgp default show-nexthop-hostname` is enabled. + + If _all_ option is specified, _ip_ keyword is ignored and, + routes displayed for all AFIs and SAFIs. + if afi is specified, with _all_ option, routes will be displayed for + each SAFI in the selcted AFI + + If _json_ option is specified, output is displayed in JSON format. + .. _bgp-display-routes-by-community: Displaying Routes by Community Attribute @@ -2801,14 +2891,14 @@ Displaying Routes by Community Attribute The following commands allow displaying routes based on their community attribute. -.. index:: show [ip] bgp <ipv4|ipv6> community -.. clicmd:: show [ip] bgp <ipv4|ipv6> community +.. index:: show [ip] bgp <ipv4|ipv6> [all] community [wide|json] +.. clicmd:: show [ip] bgp <ipv4|ipv6> [all] community [wide|json] -.. index:: show [ip] bgp <ipv4|ipv6> community COMMUNITY -.. clicmd:: show [ip] bgp <ipv4|ipv6> community COMMUNITY +.. index:: show [ip] bgp <ipv4|ipv6> [all] community COMMUNITY [wide|json] +.. clicmd:: show [ip] bgp <ipv4|ipv6> [all] community COMMUNITY [wide|json] -.. index:: show [ip] bgp <ipv4|ipv6> community COMMUNITY exact-match -.. clicmd:: show [ip] bgp <ipv4|ipv6> community COMMUNITY exact-match +.. index:: show [ip] bgp <ipv4|ipv6> [all] community COMMUNITY exact-match [wide|json] +.. clicmd:: show [ip] bgp <ipv4|ipv6> [all] community COMMUNITY exact-match [wide|json] These commands display BGP routes which have the community attribute. attribute. When ``COMMUNITY`` is specified, BGP routes that match that @@ -2825,6 +2915,19 @@ attribute. match the specified community list. When `exact-match` is specified, it displays only routes that have an exact match. + If _wide_ option is specified, then the prefix table's width is increased + to fully display the prefix and the nexthop. + + This is especially handy dealing with IPv6 prefixes and + if :clicmd:`[no] bgp default show-nexthop-hostname` is enabled. + + If _all_ option is specified, _ip_ keyword is ignored and, + routes displayed for all AFIs and SAFIs. + if afi is specified, with _all_ option, routes will be displayed for + each SAFI in the selcted AFI + + If _json_ option is specified, output is displayed in JSON format. + .. _bgp-display-routes-by-lcommunity: Displaying Routes by Large Community Attribute diff --git a/doc/user/overview.rst b/doc/user/overview.rst index ac6a1e5a8c..724d1d0b9c 100644 --- a/doc/user/overview.rst +++ b/doc/user/overview.rst @@ -31,7 +31,8 @@ information, as well as links to additional resources. Several distributions provide packages for FRR. Check your distribution's repositories to find out if a suitable version is available. -Up-to-date Debian packages are available at https://deb.frrouting.org/. +Up-to-date Debian & Redhat packages are available at https://deb.frrouting.org/ +& https://rpm.frrouting.org/ respectively. For instructions on installing from source, refer to the `developer documentation <http://docs.frrouting.org/projects/dev-guide/en/latest/>`_. diff --git a/include/linux/net_namespace.h b/include/linux/net_namespace.h index 0187c74d88..0ed9dd61d3 100644 --- a/include/linux/net_namespace.h +++ b/include/linux/net_namespace.h @@ -16,6 +16,7 @@ enum { NETNSA_NSID, NETNSA_PID, NETNSA_FD, + NETNSA_TARGET_NSID, __NETNSA_MAX, }; diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c index 1214c01a12..d6988095e5 100644 --- a/isisd/isis_circuit.c +++ b/isisd/isis_circuit.c @@ -59,6 +59,7 @@ #include "isisd/isis_errors.h" #include "isisd/isis_tx_queue.h" #include "isisd/isis_nb.h" +#include "isisd/isis_ldp_sync.h" DEFINE_QOBJ_TYPE(isis_circuit) @@ -1280,6 +1281,7 @@ struct isis_circuit *isis_circuit_create(struct isis_area *area, isis_circuit_if_bind(circuit, ifp); if (circuit->area->mta && circuit->area->mta->status) isis_link_params_update(circuit, ifp); + return circuit; } @@ -1350,11 +1352,16 @@ ferr_r isis_circuit_metric_set(struct isis_circuit *circuit, int level, return ferr_cfg_invalid("metric %d too large for narrow metric", metric); - circuit->te_metric[level - 1] = metric; - circuit->metric[level - 1] = metric; - - if (circuit->area) - lsp_regenerate_schedule(circuit->area, level, 0); + /* inform ldp-sync of metric change + * if ldp-sync is running need to save metric + * and restore new values after ldp-sync completion. + */ + if (isis_ldp_sync_if_metric_config(circuit, level, metric)) { + circuit->te_metric[level - 1] = metric; + circuit->metric[level - 1] = metric; + if (circuit->area) + lsp_regenerate_schedule(circuit->area, level, 0); + } return ferr_ok(); } diff --git a/isisd/isis_circuit.h b/isisd/isis_circuit.h index da358f411b..5766d1962f 100644 --- a/isisd/isis_circuit.h +++ b/isisd/isis_circuit.h @@ -139,6 +139,7 @@ struct isis_circuit { uint8_t flags; bool disable_threeway_adj; struct bfd_info *bfd_info; + struct ldp_sync_info *ldp_sync_info; /* * Counters as in 10589--11.2.5.9 */ diff --git a/isisd/isis_cli.c b/isisd/isis_cli.c index 31fe41db82..406717e04f 100644 --- a/isisd/isis_cli.c +++ b/isisd/isis_cli.c @@ -235,7 +235,7 @@ DEFPY_YANG(ip_router_isis, ip_router_isis_cmd, } /* check if the interface is a loopback and if so set it as passive */ - if (ifp && if_is_loopback(ifp)) + if (if_is_loopback(ifp)) nb_cli_enqueue_change(vty, "./frr-isisd:isis/passive", NB_OP_MODIFY, "true"); @@ -326,7 +326,7 @@ DEFPY_YANG(ip6_router_isis, ip6_router_isis_cmd, } /* check if the interface is a loopback and if so set it as passive */ - if (ifp && if_is_loopback(ifp)) + if (if_is_loopback(ifp)) nb_cli_enqueue_change(vty, "./frr-isisd:isis/passive", NB_OP_MODIFY, "true"); @@ -2386,6 +2386,178 @@ void cli_show_isis_log_adjacency(struct vty *vty, struct lyd_node *dnode, vty_out(vty, " log-adjacency-changes\n"); } +/* + * XPath: /frr-isisd:isis/instance/mpls/ldp-sync + */ +DEFPY(isis_mpls_ldp_sync, isis_mpls_ldp_sync_cmd, "mpls ldp-sync", + MPLS_STR MPLS_LDP_SYNC_STR) +{ + nb_cli_enqueue_change(vty, "./mpls/ldp-sync", NB_OP_CREATE, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY(no_isis_mpls_ldp_sync, no_isis_mpls_ldp_sync_cmd, "no mpls ldp-sync", + NO_STR MPLS_STR NO_MPLS_LDP_SYNC_STR) +{ + nb_cli_enqueue_change(vty, "./mpls/ldp-sync", NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +void cli_show_isis_mpls_ldp_sync(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + vty_out(vty, " mpls ldp-sync\n"); +} + +DEFPY(isis_mpls_ldp_sync_holddown, isis_mpls_ldp_sync_holddown_cmd, + "mpls ldp-sync holddown (0-10000)", + MPLS_STR MPLS_LDP_SYNC_STR + "Time to wait for LDP-SYNC to occur before restoring interface metric\n" + "Time in seconds\n") +{ + nb_cli_enqueue_change(vty, "./mpls/ldp-sync/holddown", NB_OP_MODIFY, + holddown_str); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY(no_isis_mpls_ldp_sync_holddown, no_isis_mpls_ldp_sync_holddown_cmd, + "no mpls ldp-sync holddown [<(1-10000)>]", + NO_STR MPLS_STR MPLS_LDP_SYNC_STR NO_MPLS_LDP_SYNC_HOLDDOWN_STR) +{ + nb_cli_enqueue_change(vty, "./mpls/ldp-sync/holddown", NB_OP_DESTROY, + NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +void cli_show_isis_mpls_ldp_sync_holddown(struct vty *vty, + struct lyd_node *dnode, + bool show_defaults) +{ + vty_out(vty, " mpls ldp-sync holddown %s\n", + yang_dnode_get_string(dnode, NULL)); +} + +/* + * XPath: /frr-interface:lib/interface/frr-isisd:isis/mpls/ldp-sync + */ +DEFPY(isis_mpls_if_ldp_sync, isis_mpls_if_ldp_sync_cmd, + "[no] isis mpls ldp-sync", + NO_STR "IS-IS routing protocol\n" MPLS_STR MPLS_LDP_SYNC_STR) +{ + const struct lyd_node *dnode; + struct interface *ifp; + + dnode = yang_dnode_get(vty->candidate_config->dnode, + "%s/frr-isisd:isis", VTY_CURR_XPATH); + if (dnode == NULL) { + vty_out(vty, "ISIS is not enabled on this circuit\n"); + return CMD_SUCCESS; + } + + ifp = nb_running_get_entry(NULL, VTY_CURR_XPATH, false); + if (if_is_loopback(ifp)) { + vty_out(vty, "ldp-sync does not run on loopback interface\n"); + return CMD_SUCCESS; + } + + if (ifp->vrf_id != VRF_DEFAULT) { + vty_out(vty, "ldp-sync only runs on DEFAULT VRF\n"); + return CMD_SUCCESS; + } + + nb_cli_enqueue_change(vty, "./frr-isisd:isis/mpls/ldp-sync", + NB_OP_MODIFY, no ? "false" : "true"); + + return nb_cli_apply_changes(vty, NULL); +} + + +void cli_show_isis_mpls_if_ldp_sync(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + if (!yang_dnode_get_bool(dnode, NULL)) + vty_out(vty, " no"); + + vty_out(vty, " isis mpls ldp-sync\n"); +} + +DEFPY(isis_mpls_if_ldp_sync_holddown, isis_mpls_if_ldp_sync_holddown_cmd, + "isis mpls ldp-sync holddown (0-10000)", + "IS-IS routing protocol\n" MPLS_STR MPLS_LDP_SYNC_STR + "Time to wait for LDP-SYNC to occur before restoring interface metric\n" + "Time in seconds\n") +{ + const struct lyd_node *dnode; + struct interface *ifp; + + dnode = yang_dnode_get(vty->candidate_config->dnode, + "%s/frr-isisd:isis", VTY_CURR_XPATH); + if (dnode == NULL) { + vty_out(vty, "ISIS is not enabled on this circuit\n"); + return CMD_SUCCESS; + } + + ifp = nb_running_get_entry(NULL, VTY_CURR_XPATH, false); + if (if_is_loopback(ifp)) { + vty_out(vty, "ldp-sync does not run on loopback interface\n"); + return CMD_SUCCESS; + } + + if (ifp->vrf_id != VRF_DEFAULT) { + vty_out(vty, "ldp-sync only runs on DEFAULT VRF\n"); + return CMD_SUCCESS; + } + + nb_cli_enqueue_change(vty, "./frr-isisd:isis/mpls/holddown", + NB_OP_MODIFY, holddown_str); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY(no_isis_mpls_if_ldp_sync_holddown, no_isis_mpls_if_ldp_sync_holddown_cmd, + "no isis mpls ldp-sync holddown [<(1-10000)>]", + NO_STR "IS-IS routing protocol\n" MPLS_STR NO_MPLS_LDP_SYNC_STR + NO_MPLS_LDP_SYNC_HOLDDOWN_STR) +{ + const struct lyd_node *dnode; + struct interface *ifp; + + dnode = yang_dnode_get(vty->candidate_config->dnode, + "%s/frr-isisd:isis", VTY_CURR_XPATH); + if (dnode == NULL) { + vty_out(vty, "ISIS is not enabled on this circuit\n"); + return CMD_SUCCESS; + } + + ifp = nb_running_get_entry(NULL, VTY_CURR_XPATH, false); + if (if_is_loopback(ifp)) { + vty_out(vty, "ldp-sync does not run on loopback interface\n"); + return CMD_SUCCESS; + } + + if (ifp->vrf_id != VRF_DEFAULT) { + vty_out(vty, "ldp-sync only runs on DEFAULT VRF\n"); + return CMD_SUCCESS; + } + + nb_cli_enqueue_change(vty, "./frr-isisd:isis/mpls/holddown", + NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +void cli_show_isis_mpls_if_ldp_sync_holddown(struct vty *vty, + struct lyd_node *dnode, + bool show_defaults) +{ + vty_out(vty, " isis mpls ldp-sync holddown %s\n", + yang_dnode_get_string(dnode, NULL)); +} + void isis_cli_init(void) { install_element(CONFIG_NODE, &router_isis_cmd); @@ -2489,6 +2661,14 @@ void isis_cli_init(void) install_element(INTERFACE_NODE, &no_isis_priority_cmd); install_element(ISIS_NODE, &log_adj_changes_cmd); + + install_element(ISIS_NODE, &isis_mpls_ldp_sync_cmd); + install_element(ISIS_NODE, &no_isis_mpls_ldp_sync_cmd); + install_element(ISIS_NODE, &isis_mpls_ldp_sync_holddown_cmd); + install_element(ISIS_NODE, &no_isis_mpls_ldp_sync_holddown_cmd); + install_element(INTERFACE_NODE, &isis_mpls_if_ldp_sync_cmd); + install_element(INTERFACE_NODE, &isis_mpls_if_ldp_sync_holddown_cmd); + install_element(INTERFACE_NODE, &no_isis_mpls_if_ldp_sync_holddown_cmd); } #endif /* ifndef FABRICD */ diff --git a/isisd/isis_events.c b/isisd/isis_events.c index f330407bf5..717a5fd046 100644 --- a/isisd/isis_events.c +++ b/isisd/isis_events.c @@ -76,6 +76,12 @@ void isis_event_circuit_state_change(struct isis_circuit *circuit, static void circuit_commence_level(struct isis_circuit *circuit, int level) { + if (IS_DEBUG_EVENTS) + zlog_debug( + "ISIS-Evt (%s) circuit %u on iface %s commencing on L%d", + circuit->area->area_tag, circuit->circuit_id, + circuit->interface->name, level); + if (!circuit->is_passive) { if (level == 1) { thread_add_timer(master, send_l1_psnp, circuit, @@ -105,6 +111,12 @@ static void circuit_resign_level(struct isis_circuit *circuit, int level) { int idx = level - 1; + if (IS_DEBUG_EVENTS) + zlog_debug( + "ISIS-Evt (%s) circuit %u on iface %s resigning on L%d", + circuit->area->area_tag, circuit->circuit_id, + circuit->interface->name, level); + THREAD_TIMER_OFF(circuit->t_send_csnp[idx]); THREAD_TIMER_OFF(circuit->t_send_psnp[idx]); @@ -114,6 +126,7 @@ static void circuit_resign_level(struct isis_circuit *circuit, int level) THREAD_TIMER_OFF(circuit->u.bc.t_refresh_pseudo_lsp[idx]); circuit->lsp_regenerate_pending[idx] = 0; circuit->u.bc.run_dr_elect[idx] = 0; + circuit->u.bc.is_dr[idx] = 0; if (circuit->u.bc.lan_neighs[idx] != NULL) list_delete(&circuit->u.bc.lan_neighs[idx]); } diff --git a/isisd/isis_ldp_sync.c b/isisd/isis_ldp_sync.c new file mode 100644 index 0000000000..42928e069a --- /dev/null +++ b/isisd/isis_ldp_sync.c @@ -0,0 +1,788 @@ +/** + * isis_ldp_sync.c: ISIS LDP-IGP Sync handling routines + * Copyright (C) 2020 Volta Networks, Inc. + * + * This program 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 of the License, or (at your option) + * any later version. + * + * This program 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 <string.h> + +#include "monotime.h" +#include "memory.h" +#include "thread.h" +#include "prefix.h" +#include "table.h" +#include "vty.h" +#include "command.h" +#include "plist.h" +#include "log.h" +#include "zclient.h" +#include <lib/json.h> +#include "defaults.h" +#include "ldp_sync.h" + +#include "isisd/isis_constants.h" +#include "isisd/isis_common.h" +#include "isisd/isis_flags.h" +#include "isisd/isis_circuit.h" +#include "isisd/isis_lsp.h" +#include "isisd/isis_pdu.h" +#include "isisd/isis_network.h" +#include "isisd/isis_misc.h" +#include "isisd/isis_constants.h" +#include "isisd/isis_adjacency.h" +#include "isisd/isis_dr.h" +#include "isisd/isisd.h" +#include "isisd/isis_csm.h" +#include "isisd/isis_events.h" +#include "isisd/isis_te.h" +#include "isisd/isis_mt.h" +#include "isisd/isis_errors.h" +#include "isisd/isis_tx_queue.h" +#include "isisd/isis_nb.h" +#include "isisd/isis_ldp_sync.h" + +extern struct zclient *zclient; + +/* + * LDP-SYNC msg between IGP and LDP + */ +int isis_ldp_sync_state_update(struct ldp_igp_sync_if_state state) +{ + struct interface *ifp; + struct isis_circuit *circuit = NULL; + struct isis_area *area; + struct listnode *node; + struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); + + /* if isis is not enabled or LDP-SYNC is not configured ignore */ + if (!isis || + !CHECK_FLAG(isis->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE)) + return 0; + + /* lookup circuit */ + ifp = if_lookup_by_index(state.ifindex, VRF_DEFAULT); + if (ifp == NULL) + return 0; + + for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) { + circuit = circuit_lookup_by_ifp(ifp, area->circuit_list); + if (circuit != NULL) + break; + } + + /* if isis is not enabled or LDP-SYNC is not configured ignore */ + if (circuit == NULL || + !CHECK_FLAG(isis->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE)) + return 0; + + /* received ldp-sync interface state from LDP */ + ils_debug("ldp_sync: rcvd %s from LDP if %s", + state.sync_start ? "sync-start" : "sync-complete", ifp->name); + if (state.sync_start) + isis_ldp_sync_if_start(circuit, false); + else + isis_ldp_sync_if_complete(circuit); + + return 0; +} + +int isis_ldp_sync_announce_update(struct ldp_igp_sync_announce announce) +{ + struct isis_area *area; + struct listnode *node; + struct vrf *vrf; + struct interface *ifp; + struct isis_circuit *circuit; + struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); + + /* if isis is not enabled or LDP-SYNC is not configured ignore */ + if (!isis || + !CHECK_FLAG(isis->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE)) + return 0; + + if (announce.proto != ZEBRA_ROUTE_LDP) + return 0; + + ils_debug("ldp_sync: rcvd announce from LDP"); + + /* LDP just started up: + * set cost to LSInfinity + * send request to LDP for LDP-SYNC state for each interface + * start hello timer + */ + vrf = vrf_lookup_by_id(VRF_DEFAULT); + FOR_ALL_INTERFACES (vrf, ifp) { + for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) { + circuit = circuit_lookup_by_ifp(ifp, + area->circuit_list); + if (circuit == NULL) + continue; + isis_ldp_sync_if_start(circuit, true); + } + } + + THREAD_TIMER_OFF(isis->ldp_sync_cmd.t_hello); + isis->ldp_sync_cmd.t_hello = NULL; + isis->ldp_sync_cmd.sequence = 0; + isis_ldp_sync_hello_timer_add(); + + return 0; +} + +int isis_ldp_sync_hello_update(struct ldp_igp_sync_hello hello) +{ + struct isis_area *area; + struct listnode *node; + struct vrf *vrf; + struct interface *ifp; + struct isis_circuit *circuit; + struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); + + /* if isis is not enabled or LDP-SYNC is not configured ignore */ + if (!isis || + !CHECK_FLAG(isis->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE)) + return 0; + + if (hello.proto != ZEBRA_ROUTE_LDP) + return 0; + + /* Received Hello from LDP: + * if current sequence number is greater than received hello + * sequence number then assume LDP restarted + * set cost to LSInfinity + * send request to LDP for LDP-SYNC state for each interface + * else all is fine just restart hello timer + */ + if (hello.sequence == 0) + /* rolled over */ + isis->ldp_sync_cmd.sequence = 0; + + if (isis->ldp_sync_cmd.sequence > hello.sequence) { + zlog_err("ldp_sync: LDP restarted"); + + vrf = vrf_lookup_by_id(VRF_DEFAULT); + FOR_ALL_INTERFACES (vrf, ifp) { + for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, + area)) { + circuit = circuit_lookup_by_ifp(ifp, + area->circuit_list); + if (circuit == NULL) + continue; + isis_ldp_sync_if_start(circuit, true); + } + } + } else { + THREAD_TIMER_OFF(isis->ldp_sync_cmd.t_hello); + isis_ldp_sync_hello_timer_add(); + } + isis->ldp_sync_cmd.sequence = hello.sequence; + + return 0; +} + +void isis_ldp_sync_state_req_msg(struct isis_circuit *circuit) +{ + struct ldp_igp_sync_if_state_req request; + struct interface *ifp = circuit->interface; + + ils_debug("ldp_sync: send state request to LDP for %s", + ifp->name); + + strlcpy(request.name, ifp->name, sizeof(ifp->name)); + request.proto = LDP_IGP_SYNC_IF_STATE_REQUEST; + request.ifindex = ifp->ifindex; + + zclient_send_opaque(zclient, LDP_IGP_SYNC_IF_STATE_REQUEST, + (uint8_t *)&request, sizeof(request)); +} + +/* + * LDP-SYNC general interface routines + */ +void isis_ldp_sync_if_init(struct isis_circuit *circuit, struct isis *isis) +{ + struct ldp_sync_info *ldp_sync_info; + struct interface *ifp = circuit->interface; + + /* called when ISIS is configured on an interface + * if LDP-IGP Sync is configured globally set state + * and if ptop interface LDP LDP-SYNC is enabled + */ + ils_debug("ldp_sync: init if %s ", ifp->name); + if (circuit->ldp_sync_info == NULL) + circuit->ldp_sync_info = ldp_sync_info_create(); + ldp_sync_info = circuit->ldp_sync_info; + + /* specifed on interface overrides global config. */ + if (!CHECK_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_HOLDDOWN)) + ldp_sync_info->holddown = isis->ldp_sync_cmd.holddown; + + if (!CHECK_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_IF_CONFIG)) + ldp_sync_info->enabled = LDP_IGP_SYNC_ENABLED; + + if ((circuit->circ_type == CIRCUIT_T_P2P || if_is_pointopoint(ifp)) && + ldp_sync_info->enabled == LDP_IGP_SYNC_ENABLED) + ldp_sync_info->state = LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP; +} + +void isis_ldp_sync_if_start(struct isis_circuit *circuit, + bool send_state_req) +{ + struct ldp_sync_info *ldp_sync_info; + + ldp_sync_info = circuit->ldp_sync_info; + + /* Start LDP-SYNC on this interface: + * set cost of interface to LSInfinity so traffic will use different + * interface until LDP has learned all labels from peer + * start holddown timer if configured + * send msg to LDP to get LDP-SYNC state + */ + if (ldp_sync_info && + ldp_sync_info->enabled == LDP_IGP_SYNC_ENABLED && + ldp_sync_info->state != LDP_IGP_SYNC_STATE_NOT_REQUIRED) { + ils_debug("ldp_sync: start on if %s state: %s", + circuit->interface->name, "Holding down until Sync"); + ldp_sync_info->state = LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP; + isis_ldp_sync_set_if_metric(circuit, true); + isis_ldp_sync_holddown_timer_add(circuit); + + if (send_state_req) + isis_ldp_sync_state_req_msg(circuit); + } +} + +void isis_ldp_sync_if_complete(struct isis_circuit *circuit) +{ + struct ldp_sync_info *ldp_sync_info; + + ldp_sync_info = circuit->ldp_sync_info; + + /* received sync-complete from LDP: + * set state to up + * stop timer + * restore interface cost to original value + */ + if (ldp_sync_info && ldp_sync_info->enabled == LDP_IGP_SYNC_ENABLED) { + if (ldp_sync_info->state == LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP) + ldp_sync_info->state = LDP_IGP_SYNC_STATE_REQUIRED_UP; + THREAD_TIMER_OFF(ldp_sync_info->t_holddown); + ldp_sync_info->t_holddown = NULL; + isis_ldp_sync_set_if_metric(circuit, true); + } +} + +void isis_ldp_sync_ldp_fail(struct isis_circuit *circuit) +{ + struct ldp_sync_info *ldp_sync_info; + + ldp_sync_info = circuit->ldp_sync_info; + + /* LDP failed to send hello: + * stop holddown timer + * set cost of interface to LSInfinity so traffic will use different + * interface until LDP restarts and has learned all labels from peer + */ + if (ldp_sync_info && + ldp_sync_info->enabled == LDP_IGP_SYNC_ENABLED && + ldp_sync_info->state != LDP_IGP_SYNC_STATE_NOT_REQUIRED) { + if (ldp_sync_info->t_holddown != NULL) { + THREAD_TIMER_OFF(ldp_sync_info->t_holddown); + ldp_sync_info->t_holddown = NULL; + } + ldp_sync_info->state = LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP; + isis_ldp_sync_set_if_metric(circuit, true); + } +} + +void isis_ldp_sync_if_remove(struct isis_circuit *circuit, bool remove) +{ + struct ldp_sync_info *ldp_sync_info; + + if (circuit->ldp_sync_info == NULL) + return; + + ldp_sync_info = circuit->ldp_sync_info; + + /* Stop LDP-SYNC on this interface: + * if holddown timer is running stop it + * delete ldp instance on interface + * restore metric + */ + ils_debug("ldp_sync: remove if %s", circuit->interface + ? circuit->interface->name : ""); + + if (ldp_sync_info->t_holddown) + THREAD_TIMER_OFF(ldp_sync_info->t_holddown); + ldp_sync_info->state = LDP_IGP_SYNC_STATE_NOT_REQUIRED; + isis_ldp_sync_set_if_metric(circuit, true); + if (remove) { + /* ISIS instance being removed free ldp-sync info */ + ldp_sync_info_free((struct ldp_sync_info **)&(ldp_sync_info)); + circuit->ldp_sync_info = NULL; + } +} + +static int isis_ldp_sync_adj_state_change(struct isis_adjacency *adj) +{ + struct isis_circuit *circuit = adj->circuit; + struct ldp_sync_info *ldp_sync_info; + struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); + + if (!isis || + !CHECK_FLAG(isis->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE) || + circuit->interface->vrf_id != VRF_DEFAULT || + if_is_loopback(circuit->interface)) + return 0; + + if (circuit->ldp_sync_info == NULL) + isis_ldp_sync_if_init(circuit, isis); + ldp_sync_info = circuit->ldp_sync_info; + + if (ldp_sync_info->enabled != LDP_IGP_SYNC_ENABLED) + return 0; + + if (adj->adj_state == ISIS_ADJ_UP) { + if (circuit->circ_type == CIRCUIT_T_P2P || + if_is_pointopoint(circuit->interface)) { + /* If LDP-SYNC is configure on interface then start */ + ldp_sync_info->state = + LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP; + isis_ldp_sync_if_start(circuit, true); + } else { + /* non ptop link so don't run ldp-sync */ + ldp_sync_info->state = LDP_IGP_SYNC_STATE_NOT_REQUIRED; + isis_ldp_sync_set_if_metric(circuit, true); + } + } else { + /* If LDP-SYNC is configure on this interface then stop it */ + if (circuit->circ_type == CIRCUIT_T_P2P || + if_is_pointopoint(circuit->interface)) + ldp_sync_info->state = + LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP; + else + ldp_sync_info->state = LDP_IGP_SYNC_STATE_NOT_REQUIRED; + + ils_debug("ldp_sync: down on if %s", circuit->interface->name); + ldp_sync_if_down(circuit->ldp_sync_info); + } + + return 0; +} + +bool isis_ldp_sync_if_metric_config(struct isis_circuit *circuit, int level, + int metric) +{ + struct ldp_sync_info *ldp_sync_info = circuit->ldp_sync_info; + struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); + + /* configured interface metric has been changed: + * if LDP-IGP Sync is running and metric has been set to LSInfinity + * change saved value so when ldp-sync completes proper metric is + * restored + */ + if (isis && + CHECK_FLAG(isis->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE) && + ldp_sync_info != NULL) { + + if (CHECK_FLAG(ldp_sync_info->flags, + LDP_SYNC_FLAG_SET_METRIC)) { + ldp_sync_info->metric[level-1] = metric; + ldp_sync_info->metric[level-1] = metric; + return false; + } + } + return true; +} + +void isis_ldp_sync_set_if_metric(struct isis_circuit *circuit, bool run_regen) +{ + struct ldp_sync_info *ldp_sync_info; + + /* set interface metric: + * if LDP-IGP Sync is starting set metric so interface + * is used only as last resort + * else restore metric to original value + */ + if (circuit->ldp_sync_info == NULL || circuit->area == NULL) + return; + + ldp_sync_info = circuit->ldp_sync_info; + if (ldp_sync_if_is_enabled(ldp_sync_info)) { + /* if metric already set to LSInfinity just return */ + if (CHECK_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_SET_METRIC)) + return; + + SET_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_SET_METRIC); + if (circuit->is_type & IS_LEVEL_1) { + if (circuit->area->newmetric) { + ldp_sync_info->metric[0] = + circuit->te_metric[0]; + circuit->te_metric[0] = LDP_ISIS_LSINFINITY; + } else { + ldp_sync_info->metric[0] = circuit->metric[0]; + circuit->metric[0] = LDP_ISIS_LSINFINITY_NL; + } + } + if (circuit->is_type & IS_LEVEL_2) { + if (circuit->area->newmetric) { + ldp_sync_info->metric[1] = + circuit->te_metric[1]; + circuit->te_metric[1] = LDP_ISIS_LSINFINITY; + } else { + ldp_sync_info->metric[1] = circuit->metric[1]; + circuit->metric[1] = LDP_ISIS_LSINFINITY_NL; + } + } + } else { + /* if metric already restored just return */ + if (!CHECK_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_SET_METRIC)) + return; + + UNSET_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_SET_METRIC); + if (circuit->is_type & IS_LEVEL_1) { + circuit->te_metric[0] = ldp_sync_info->metric[0]; + circuit->metric[0] = ldp_sync_info->metric[0]; + } + if (circuit->is_type & IS_LEVEL_2) { + circuit->te_metric[1] = ldp_sync_info->metric[1]; + circuit->metric[1] = ldp_sync_info->metric[1]; + } + } + + if (run_regen) + lsp_regenerate_schedule(circuit->area, circuit->is_type, 0); +} + + +/* + * LDP-SYNC holddown timer routines + */ +static int isis_ldp_sync_holddown_timer(struct thread *thread) +{ + struct isis_circuit *circuit; + struct ldp_sync_info *ldp_sync_info; + + /* holddown timer expired: + * didn't receive msg from LDP indicating sync-complete + * restore interface cost to original value + */ + circuit = THREAD_ARG(thread); + if (circuit->ldp_sync_info == NULL) + return 0; + + ldp_sync_info = circuit->ldp_sync_info; + + ldp_sync_info->state = LDP_IGP_SYNC_STATE_REQUIRED_UP; + ldp_sync_info->t_holddown = NULL; + + ils_debug("ldp_sync: holddown timer expired for %s state:sync achieved", + circuit->interface->name); + + isis_ldp_sync_set_if_metric(circuit, true); + return 0; +} + +void isis_ldp_sync_holddown_timer_add(struct isis_circuit *circuit) +{ + struct ldp_sync_info *ldp_sync_info; + + ldp_sync_info = circuit->ldp_sync_info; + + /* Start holddown timer: + * this timer is used to keep interface cost at LSInfinity + * once expires returns cost to original value + * if timer is already running or holddown time is off just return + */ + if (ldp_sync_info->t_holddown || + ldp_sync_info->holddown == LDP_IGP_SYNC_HOLDDOWN_DEFAULT) + return; + + ils_debug("ldp_sync: start holddown timer for %s time %d", + circuit->interface->name, ldp_sync_info->holddown); + + thread_add_timer(master, isis_ldp_sync_holddown_timer, + circuit, ldp_sync_info->holddown, + &ldp_sync_info->t_holddown); +} + +/* + * LDP-SYNC hello timer routines + */ +static int isis_ldp_sync_hello_timer(struct thread *thread) +{ + struct isis_area *area; + struct listnode *node; + struct isis_circuit *circuit; + struct interface *ifp; + struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); + struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); + + if (!isis) + return 0; + + /* hello timer expired: + * didn't receive hello msg from LDP + * set cost of all interfaces to LSInfinity + */ + FOR_ALL_INTERFACES (vrf, ifp) { + for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) { + circuit = circuit_lookup_by_ifp(ifp, + area->circuit_list); + if (circuit == NULL) + continue; + + isis_ldp_sync_ldp_fail(circuit); + } + } + + zlog_debug("ldp_sync: hello timer expired, LDP down"); + + return 0; +} + +void isis_ldp_sync_hello_timer_add(void) +{ + struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); + + /* Start hello timer: + * this timer is used to make sure LDP is up + * if expires set interface cost to LSInfinity + */ + if (!isis || + !CHECK_FLAG(isis->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE)) + return; + + thread_add_timer(master, isis_ldp_sync_hello_timer, + NULL, LDP_IGP_SYNC_HELLO_TIMEOUT, + &isis->ldp_sync_cmd.t_hello); +} + +/* + * LDP-SYNC routes used by set commands. + */ + +void isis_if_set_ldp_sync_enable(struct isis_circuit *circuit) +{ + struct ldp_sync_info *ldp_sync_info; + struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); + + /* called when setting LDP-SYNC at the global level: + * specifed on interface overrides global config + * if ptop link send msg to LDP indicating ldp-sync enabled + */ + if (!isis || if_is_loopback(circuit->interface)) + return; + + if (CHECK_FLAG(isis->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE)) { + if (circuit->ldp_sync_info == NULL) + isis_ldp_sync_if_init(circuit, isis); + ldp_sync_info = circuit->ldp_sync_info; + + /* config on interface, overrides global config. */ + if (CHECK_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_IF_CONFIG)) + if (ldp_sync_info->enabled != LDP_IGP_SYNC_ENABLED) + return; + + ldp_sync_info->enabled = LDP_IGP_SYNC_ENABLED; + ils_debug("ldp_sync: enable if %s", circuit->interface->name); + + /* send message to LDP if ptop link */ + if (circuit->circ_type == CIRCUIT_T_P2P || + if_is_pointopoint(circuit->interface)) { + ldp_sync_info->state = + LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP; + isis_ldp_sync_state_req_msg(circuit); + } else { + ldp_sync_info->state = LDP_IGP_SYNC_STATE_NOT_REQUIRED; + zlog_debug("ldp_sync: Sync only runs on P2P links %s", + circuit->interface->name); + } + } else + /* delete LDP sync even if configured on an interface */ + isis_ldp_sync_if_remove(circuit, false); +} + +void isis_if_set_ldp_sync_holddown(struct isis_circuit *circuit) +{ + struct ldp_sync_info *ldp_sync_info; + struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); + + /* called when setting LDP-SYNC at the global level: + * specifed on interface overrides global config. + */ + if (!isis || if_is_loopback(circuit->interface)) + return; + + if (circuit->ldp_sync_info == NULL) + isis_ldp_sync_if_init(circuit, isis); + ldp_sync_info = circuit->ldp_sync_info; + + /* config on interface, overrides global config. */ + if (CHECK_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_HOLDDOWN)) + return; + if (CHECK_FLAG(isis->ldp_sync_cmd.flags, LDP_SYNC_FLAG_HOLDDOWN)) + ldp_sync_info->holddown = isis->ldp_sync_cmd.holddown; + else + ldp_sync_info->holddown = LDP_IGP_SYNC_HOLDDOWN_DEFAULT; +} + +void isis_ldp_sync_gbl_exit(bool remove) +{ + struct isis_area *area; + struct listnode *node; + struct isis_circuit *circuit; + struct interface *ifp; + struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); + struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); + + /* if you delete LDP-SYNC at a gobal level is clears all LDP-SYNC + * configuration, even interface configuration + */ + if (isis && + CHECK_FLAG(isis->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE)) { + /* register with opaque client to recv LDP-IGP Sync msgs */ + zclient_unregister_opaque(zclient, + LDP_IGP_SYNC_IF_STATE_UPDATE); + zclient_unregister_opaque(zclient, + LDP_IGP_SYNC_ANNOUNCE_UPDATE); + zclient_unregister_opaque(zclient, LDP_IGP_SYNC_HELLO_UPDATE); + + /* disable LDP-SYNC globally */ + UNSET_FLAG(isis->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE); + UNSET_FLAG(isis->ldp_sync_cmd.flags, LDP_SYNC_FLAG_HOLDDOWN); + isis->ldp_sync_cmd.holddown = LDP_IGP_SYNC_HOLDDOWN_DEFAULT; + THREAD_TIMER_OFF(isis->ldp_sync_cmd.t_hello); + isis->ldp_sync_cmd.t_hello = NULL; + + /* remove LDP-SYNC on all ISIS interfaces */ + FOR_ALL_INTERFACES (vrf, ifp) { + for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, + area)) { + circuit = circuit_lookup_by_ifp(ifp, + area->circuit_list); + if (circuit == NULL) + continue; + isis_ldp_sync_if_remove(circuit, remove); + } + } + } +} + +/* + * LDP-SYNC routines used by show commands. + */ + +static void isis_circuit_ldp_sync_print_vty(struct isis_circuit *circuit, + struct vty *vty) +{ + struct ldp_sync_info *ldp_sync_info; + const char *ldp_state; + + if (circuit->ldp_sync_info == NULL || + if_is_loopback(circuit->interface)) + return; + + ldp_sync_info = circuit->ldp_sync_info; + vty_out(vty, "%-10s\n", circuit->interface->name); + vty_out(vty, " LDP-IGP Synchronization enabled: %s\n", + ldp_sync_info->enabled == LDP_IGP_SYNC_ENABLED + ? "yes" + : "no"); + vty_out(vty, " holddown timer in seconds: %u\n", + ldp_sync_info->holddown); + + switch (ldp_sync_info->state) { + case LDP_IGP_SYNC_STATE_REQUIRED_UP: + vty_out(vty, " State: Sync achieved\n"); + break; + case LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP: + if (ldp_sync_info->t_holddown != NULL) { + struct timeval remain = thread_timer_remain( + ldp_sync_info->t_holddown); + vty_out(vty, + " Holddown timer is running %lld.%03lld remaining\n", + (long long)remain.tv_sec, + (long long)remain.tv_usec/1000); + + vty_out(vty, " State: Holding down until Sync\n"); + } else + vty_out(vty, " State: Sync not achieved\n"); + break; + case LDP_IGP_SYNC_STATE_NOT_REQUIRED: + default: + if ((circuit->circ_type != CIRCUIT_T_P2P && + !if_is_pointopoint(circuit->interface)) && + circuit->circ_type != CIRCUIT_T_UNKNOWN) + ldp_state = "Sync not required: non-p2p link"; + else + ldp_state = "Sync not required"; + vty_out(vty, " State: %s\n", ldp_state); + break; + } +} + +DEFUN (show_isis_mpls_ldp_interface, + show_isis_mpls_ldp_interface_cmd, + "show " PROTO_NAME " mpls ldp-sync [interface <INTERFACE|all>]", + SHOW_STR + PROTO_HELP + MPLS_STR + "LDP-IGP Sync information\n" + "Interface name\n") +{ + char *ifname = NULL; + int idx_intf = 0; + struct listnode *anode, *cnode; + struct isis_area *area; + struct isis_circuit *circuit; + struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); + + if (!isis) { + vty_out(vty, "IS-IS Routing Process not enabled\n"); + return CMD_SUCCESS; + } + + if (!CHECK_FLAG(isis->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE)) { + vty_out(vty, "LDP-sync is disabled\n"); + return CMD_SUCCESS; + } + + if (argv_find(argv, argc, "INTERFACE", &idx_intf)) + ifname = argv[idx_intf]->arg; + + for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode, area)) { + for (ALL_LIST_ELEMENTS_RO(area->circuit_list, cnode, circuit)) + if (!ifname) + isis_circuit_ldp_sync_print_vty(circuit, vty); + else if (strcmp(circuit->interface->name, ifname) == 0) + isis_circuit_ldp_sync_print_vty(circuit, vty); + } + + return CMD_SUCCESS; +} + +void isis_ldp_sync_init(void) +{ + + /* "show ip isis mpls ldp interface" commands. */ + install_element(VIEW_NODE, &show_isis_mpls_ldp_interface_cmd); + + /* register for adjacency state changes */ + hook_register(isis_adj_state_change_hook, + isis_ldp_sync_adj_state_change); +} diff --git a/isisd/isis_ldp_sync.h b/isisd/isis_ldp_sync.h new file mode 100644 index 0000000000..6017cdf001 --- /dev/null +++ b/isisd/isis_ldp_sync.h @@ -0,0 +1,54 @@ +/* + * isis_ldp_sync.h: ISIS LDP-IGP Sync handling routines + * Copyright (C) 2020 Volta Networks, Inc. + * + * This program 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 of the License, or (at your option) + * any later version. + * + * This program 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 _ZEBRA_ISIS_LDP_SYNC_H +#define _ZEBRA_ISIS_LDP_SYNC_H + +#define LDP_ISIS_LSINFINITY 0xFFFFFE /* wide link metric */ +#define LDP_ISIS_LSINFINITY_NL 62 /* narrow link metric */ + +/* Macro to log debug message */ +#define ils_debug(...) \ + do { \ + if (IS_DEBUG_LDP_SYNC) \ + zlog_debug(__VA_ARGS__); \ + } while (0) + +extern void isis_if_set_ldp_sync_enable(struct isis_circuit *circuit); +extern void isis_if_set_ldp_sync_holddown(struct isis_circuit *circuit); +extern void isis_ldp_sync_if_init(struct isis_circuit *circuit, + struct isis *isis); +extern void isis_ldp_sync_if_start(struct isis_circuit *circuit, + bool send_state_req); +extern void isis_ldp_sync_if_remove(struct isis_circuit *circuit, bool remove); +extern void isis_ldp_sync_if_complete(struct isis_circuit *circuit); +extern void isis_ldp_sync_holddown_timer_add(struct isis_circuit *circuit); +extern void isis_ldp_sync_hello_timer_add(void); +extern void isis_ldp_sync_ldp_fail(struct isis_circuit *circuit); +extern int isis_ldp_sync_state_update(struct ldp_igp_sync_if_state state); +extern int isis_ldp_sync_announce_update(struct ldp_igp_sync_announce announce); +extern int isis_ldp_sync_hello_update(struct ldp_igp_sync_hello hello); +extern void isis_ldp_sync_state_req_msg(struct isis_circuit *circuit); +extern void isis_ldp_sync_set_if_metric(struct isis_circuit *circuit, + bool run_regen); +extern bool isis_ldp_sync_if_metric_config(struct isis_circuit *circuit, + int level, int metric); +extern void isis_ldp_sync_init(void); +extern void isis_ldp_sync_gbl_exit(bool remove); +#endif /* _ZEBRA_ISIS_LDP_SYNC_H */ diff --git a/isisd/isis_main.c b/isisd/isis_main.c index 26f5227aae..22df3ff37e 100644 --- a/isisd/isis_main.c +++ b/isisd/isis_main.c @@ -58,6 +58,7 @@ #include "isisd/isis_mt.h" #include "isisd/fabricd.h" #include "isisd/isis_nb.h" +#include "isisd/isis_ldp_sync.h" /* Default configuration file name */ #define ISISD_DEFAULT_CONFIG "isisd.conf" @@ -264,6 +265,7 @@ int main(int argc, char **argv, char **envp) isis_zebra_init(master, instance); isis_bfd_init(); + isis_ldp_sync_init(); fabricd_init(); frr_config_fork(); diff --git a/isisd/isis_nb.c b/isisd/isis_nb.c index 33b0b4d02c..14ea1170c4 100644 --- a/isisd/isis_nb.c +++ b/isisd/isis_nb.c @@ -538,6 +538,21 @@ const struct frr_yang_module_info frr_isisd_info = { }, }, { + .xpath = "/frr-isisd:isis/instance/mpls/ldp-sync", + .cbs = { + .cli_show = cli_show_isis_mpls_ldp_sync, + .create = isis_instance_mpls_ldp_sync_create, + .destroy = isis_instance_mpls_ldp_sync_destroy, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/mpls/ldp-sync/holddown", + .cbs = { + .cli_show = cli_show_isis_mpls_ldp_sync_holddown, + .modify = isis_instance_mpls_ldp_sync_holddown_modify, + }, + }, + { .xpath = "/frr-interface:lib/interface/frr-isisd:isis", .cbs = { .create = lib_interface_isis_create, @@ -906,6 +921,21 @@ const struct frr_yang_module_info frr_isisd_info = { } }, { + .xpath = "/frr-interface:lib/interface/frr-isisd:isis/mpls/ldp-sync", + .cbs = { + .cli_show = cli_show_isis_mpls_if_ldp_sync, + .modify = lib_interface_isis_mpls_ldp_sync_modify, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-isisd:isis/mpls/holddown", + .cbs = { + .cli_show = cli_show_isis_mpls_if_ldp_sync_holddown, + .modify = lib_interface_isis_mpls_holddown_modify, + .destroy = lib_interface_isis_mpls_holddown_destroy, + } + }, + { .xpath = NULL, }, } diff --git a/isisd/isis_nb.h b/isisd/isis_nb.h index a79cb8ff57..8a6d24b845 100644 --- a/isisd/isis_nb.h +++ b/isisd/isis_nb.h @@ -206,6 +206,9 @@ int isis_instance_segment_routing_prefix_sid_map_prefix_sid_sid_value_modify( struct nb_cb_modify_args *args); int isis_instance_segment_routing_prefix_sid_map_prefix_sid_last_hop_behavior_modify( struct nb_cb_modify_args *args); +int isis_instance_mpls_ldp_sync_destroy(struct nb_cb_destroy_args *args); +int isis_instance_mpls_ldp_sync_create(struct nb_cb_create_args *args); +int isis_instance_mpls_ldp_sync_holddown_modify(struct nb_cb_modify_args *args); int lib_interface_isis_csnp_interval_level_1_modify( struct nb_cb_modify_args *args); int lib_interface_isis_csnp_interval_level_2_modify( @@ -250,6 +253,9 @@ int lib_interface_isis_multi_topology_ipv6_management_modify( struct nb_cb_modify_args *args); int lib_interface_isis_multi_topology_ipv6_dstsrc_modify( struct nb_cb_modify_args *args); +int lib_interface_isis_mpls_ldp_sync_modify(struct nb_cb_modify_args *args); +int lib_interface_isis_mpls_holddown_modify(struct nb_cb_modify_args *args); +int lib_interface_isis_mpls_holddown_destroy(struct nb_cb_destroy_args *args); struct yang_data * lib_interface_state_isis_get_elem(struct nb_cb_get_elem_args *args); const void *lib_interface_state_isis_adjacencies_adjacency_get_next( @@ -434,6 +440,16 @@ void cli_show_ip_isis_priority(struct vty *vty, struct lyd_node *dnode, bool show_defaults); void cli_show_isis_log_adjacency(struct vty *vty, struct lyd_node *dnode, bool show_defaults); +void cli_show_isis_mpls_ldp_sync(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_isis_mpls_ldp_sync_holddown(struct vty *vty, + struct lyd_node *dnode, + bool show_defaults); +void cli_show_isis_mpls_if_ldp_sync(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_isis_mpls_if_ldp_sync_holddown(struct vty *vty, + struct lyd_node *dnode, + bool show_defaults); /* Notifications. */ void isis_notif_db_overload(const struct isis_area *area, bool overload); diff --git a/isisd/isis_nb_config.c b/isisd/isis_nb_config.c index 170fe92c28..0988fe8578 100644 --- a/isisd/isis_nb_config.c +++ b/isisd/isis_nb_config.c @@ -29,6 +29,8 @@ #include "spf_backoff.h" #include "lib_errors.h" #include "vrf.h" +#include "zclient.h" +#include "ldp_sync.h" #include "isisd/isisd.h" #include "isisd/isis_nb.h" @@ -45,6 +47,9 @@ #include "isisd/isis_memory.h" #include "isisd/isis_mt.h" #include "isisd/isis_redist.h" +#include "isisd/isis_ldp_sync.h" + +extern struct zclient *zclient; /* * XPath: /frr-isisd:isis/instance @@ -81,6 +86,10 @@ int isis_instance_destroy(struct nb_cb_destroy_args *args) area = nb_running_unset_entry(args->dnode); isis_area_destroy(area); + /* remove ldp-sync config */ + if (area->isis->vrf_id == VRF_DEFAULT) + isis_ldp_sync_gbl_exit(true); + return NB_OK; } @@ -1826,6 +1835,113 @@ int isis_instance_segment_routing_prefix_sid_map_prefix_sid_last_hop_behavior_mo } /* + * XPath: /frr-isisd:isis/instance/mpls/ldp-sync + */ +int isis_instance_mpls_ldp_sync_create(struct nb_cb_create_args *args) +{ + struct isis_area *area; + struct listnode *node; + struct isis_circuit *circuit; + struct interface *ifp; + struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); + struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); + + switch (args->event) { + case NB_EV_VALIDATE: + if (isis == NULL) + return NB_ERR_VALIDATION; + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* register with opaque client to recv LDP-IGP Sync msgs */ + zclient_register_opaque(zclient, LDP_IGP_SYNC_IF_STATE_UPDATE); + zclient_register_opaque(zclient, LDP_IGP_SYNC_ANNOUNCE_UPDATE); + zclient_register_opaque(zclient, LDP_IGP_SYNC_HELLO_UPDATE); + + if (!CHECK_FLAG(isis->ldp_sync_cmd.flags, + LDP_SYNC_FLAG_ENABLE)) { + SET_FLAG(isis->ldp_sync_cmd.flags, + LDP_SYNC_FLAG_ENABLE); + + /* turn on LDP-IGP Sync on all ptop ISIS interfaces */ + FOR_ALL_INTERFACES (vrf, ifp) { + for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, + area)) { + circuit = circuit_lookup_by_ifp( + ifp, area->circuit_list); + if (circuit == NULL) + continue; + isis_if_set_ldp_sync_enable(circuit); + } + } + } + break; + } + return NB_OK; +} + +int isis_instance_mpls_ldp_sync_destroy(struct nb_cb_destroy_args *args) +{ + if (args->event != NB_EV_APPLY) + return NB_OK; + + /* remove ldp-sync config */ + isis_ldp_sync_gbl_exit(false); + + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/mpls/ldp-sync/holddown + */ +int isis_instance_mpls_ldp_sync_holddown_modify(struct nb_cb_modify_args *args) +{ + struct isis_area *area; + struct listnode *node; + struct isis_circuit *circuit; + struct interface *ifp; + struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); + uint16_t holddown = LDP_IGP_SYNC_HOLDDOWN_DEFAULT; + struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); + + switch (args->event) { + case NB_EV_VALIDATE: + if (isis == NULL) + return NB_ERR_VALIDATION; + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + holddown = yang_dnode_get_uint16(args->dnode, NULL); + + if (holddown == LDP_IGP_SYNC_HOLDDOWN_DEFAULT) + UNSET_FLAG(isis->ldp_sync_cmd.flags, + LDP_SYNC_FLAG_HOLDDOWN); + else + SET_FLAG(isis->ldp_sync_cmd.flags, + LDP_SYNC_FLAG_HOLDDOWN); + isis->ldp_sync_cmd.holddown = holddown; + + /* set holddown time on all ISIS interfaces */ + FOR_ALL_INTERFACES (vrf, ifp) { + for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, + area)) { + circuit = circuit_lookup_by_ifp(ifp, + area->circuit_list); + if (circuit == NULL) + continue; + isis_if_set_ldp_sync_holddown(circuit); + } + } + break; + } + return NB_OK; +} + +/* * XPath: /frr-interface:lib/interface/frr-isisd:isis */ int lib_interface_isis_create(struct nb_cb_create_args *args) @@ -1915,6 +2031,9 @@ int lib_interface_isis_destroy(struct nb_cb_destroy_args *args) if (!circuit) return NB_ERR_INCONSISTENCY; + /* remove ldp-sync config */ + isis_ldp_sync_if_remove(circuit, true); + /* disable both AFs for this circuit. this will also update the * CSM state by sending an ISIS_DISABLED signal. If there is no * area associated to the circuit there is nothing to do @@ -2646,3 +2765,130 @@ int lib_interface_isis_multi_topology_ipv6_dstsrc_modify( args->event, args->dnode, args->errmsg, args->errmsg_len, ISIS_MT_IPV6_DSTSRC); } + +/* + * XPath: /frr-interface:lib/interface/frr-isisd:isis/mpls/ldp-sync + */ +int lib_interface_isis_mpls_ldp_sync_modify(struct nb_cb_modify_args *args) +{ + struct isis_circuit *circuit; + struct ldp_sync_info *ldp_sync_info; + bool ldp_sync_enable; + struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); + + switch (args->event) { + case NB_EV_VALIDATE: + if (isis == NULL) + return NB_ERR_VALIDATION; + break; + + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + circuit = nb_running_get_entry(args->dnode, NULL, true); + ldp_sync_enable = yang_dnode_get_bool(args->dnode, NULL); + + if (circuit->ldp_sync_info == NULL) + isis_ldp_sync_if_init(circuit, isis); + ldp_sync_info = circuit->ldp_sync_info; + + if (ldp_sync_enable) { + /* enable LDP-SYNC on an interface + * if ptop interface send message to LDP to get state + */ + SET_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_IF_CONFIG); + ldp_sync_info->enabled = LDP_IGP_SYNC_ENABLED; + if (circuit->circ_type == CIRCUIT_T_P2P) { + ldp_sync_info->state = + LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP; + isis_ldp_sync_state_req_msg(circuit); + } else { + zlog_debug("ldp_sync: only runs on P2P links %s", + circuit->interface->name); + ldp_sync_info->state = + LDP_IGP_SYNC_STATE_NOT_REQUIRED; + } + } else { + /* disable LDP-SYNC on an interface + * stop holddown timer if running + * restore isis metric + */ + SET_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_IF_CONFIG); + ldp_sync_info->enabled = LDP_IGP_SYNC_DEFAULT; + ldp_sync_info->state = LDP_IGP_SYNC_STATE_NOT_REQUIRED; + THREAD_TIMER_OFF(ldp_sync_info->t_holddown); + ldp_sync_info->t_holddown = NULL; + isis_ldp_sync_set_if_metric(circuit, true); + } + break; + } + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-isisd:isis/mpls/holddown + */ +int lib_interface_isis_mpls_holddown_modify(struct nb_cb_modify_args *args) +{ + struct isis_circuit *circuit; + struct ldp_sync_info *ldp_sync_info; + uint16_t holddown; + struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); + + switch (args->event) { + case NB_EV_VALIDATE: + if (isis == NULL) + return NB_ERR_VALIDATION; + break; + + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + circuit = nb_running_get_entry(args->dnode, NULL, true); + holddown = yang_dnode_get_uint16(args->dnode, NULL); + + if (circuit->ldp_sync_info == NULL) + isis_ldp_sync_if_init(circuit, isis); + ldp_sync_info = circuit->ldp_sync_info; + + SET_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_HOLDDOWN); + ldp_sync_info->holddown = holddown; + break; + } + return NB_OK; +} + +int lib_interface_isis_mpls_holddown_destroy(struct nb_cb_destroy_args *args) +{ + struct isis_circuit *circuit; + struct ldp_sync_info *ldp_sync_info; + struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); + + switch (args->event) { + case NB_EV_VALIDATE: + if (isis == NULL) + return NB_ERR_VALIDATION; + circuit = nb_running_get_entry(args->dnode, NULL, true); + if (circuit->ldp_sync_info == NULL) + return NB_ERR_VALIDATION; + + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + circuit = nb_running_get_entry(args->dnode, NULL, true); + ldp_sync_info = circuit->ldp_sync_info; + UNSET_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_HOLDDOWN); + + if (CHECK_FLAG(isis->ldp_sync_cmd.flags, + LDP_SYNC_FLAG_HOLDDOWN)) + ldp_sync_info->holddown = isis->ldp_sync_cmd.holddown; + else + ldp_sync_info->holddown = LDP_IGP_SYNC_HOLDDOWN_DEFAULT; + break; + } + return NB_OK; +} diff --git a/isisd/isis_route.c b/isisd/isis_route.c index c83a7c04bb..fa06572555 100644 --- a/isisd/isis_route.c +++ b/isisd/isis_route.c @@ -239,41 +239,51 @@ static void isis_route_info_delete(struct isis_route_info *route_info) XFREE(MTYPE_ISIS_ROUTE_INFO, route_info); } -static int isis_route_info_same_attrib(struct isis_route_info *new, - struct isis_route_info *old) -{ - if (new->cost != old->cost) - return 0; - if (new->depth != old->depth) - return 0; - - return 1; -} - static int isis_route_info_same(struct isis_route_info *new, - struct isis_route_info *old, uint8_t family) + struct isis_route_info *old, char *buf, + size_t buf_size) { struct listnode *node; struct isis_nexthop *nexthop; - if (!CHECK_FLAG(old->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED)) + if (new->cost != old->cost) { + if (buf) + snprintf(buf, buf_size, "cost (old: %u, new: %u)", + old->cost, new->cost); return 0; + } - if (CHECK_FLAG(new->flag, ISIS_ROUTE_FLAG_ZEBRA_RESYNC)) + if (new->depth != old->depth) { + if (buf) + snprintf(buf, buf_size, "depth (old: %u, new: %u)", + old->depth, new->depth); return 0; + } - if (!isis_route_info_same_attrib(new, old)) + if (new->nexthops->count != old->nexthops->count) { + if (buf) + snprintf(buf, buf_size, "nhops num (old: %u, new: %u)", + old->nexthops->count, new->nexthops->count); return 0; + } - for (ALL_LIST_ELEMENTS_RO(new->nexthops, node, nexthop)) + for (ALL_LIST_ELEMENTS_RO(new->nexthops, node, nexthop)) { if (!nexthoplookup(old->nexthops, nexthop->family, &nexthop->ip, - nexthop->ifindex)) + nexthop->ifindex)) { + if (buf) + snprintf(buf, buf_size, + "new nhop"); /* TODO: print nhop */ return 0; + } + } - for (ALL_LIST_ELEMENTS_RO(old->nexthops, node, nexthop)) - if (!nexthoplookup(new->nexthops, nexthop->family, &nexthop->ip, - nexthop->ifindex)) - return 0; + /* only the resync flag needs to be checked */ + if (CHECK_FLAG(new->flag, ISIS_ROUTE_FLAG_ZEBRA_RESYNC) + != CHECK_FLAG(old->flag, ISIS_ROUTE_FLAG_ZEBRA_RESYNC)) { + if (buf) + snprintf(buf, buf_size, "resync flag"); + return 0; + } return 1; } @@ -289,9 +299,8 @@ struct isis_route_info *isis_route_create(struct prefix *prefix, struct route_node *route_node; struct isis_route_info *rinfo_new, *rinfo_old, *route_info = NULL; char buff[PREFIX2STR_BUFFER]; - uint8_t family; + char change_buf[64]; - family = prefix->family; /* for debugs */ prefix2str(prefix, buff, sizeof(buff)); @@ -311,19 +320,25 @@ struct isis_route_info *isis_route_create(struct prefix *prefix, UNSET_FLAG(route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED); } else { route_unlock_node(route_node); +#ifdef EXTREME_DEBUG if (IS_DEBUG_RTE_EVENTS) zlog_debug("ISIS-Rte (%s) route already exists: %s", area->area_tag, buff); - if (isis_route_info_same(rinfo_new, rinfo_old, family)) { +#endif /* EXTREME_DEBUG */ + if (isis_route_info_same(rinfo_new, rinfo_old, change_buf, + sizeof(change_buf))) { +#ifdef EXTREME_DEBUG if (IS_DEBUG_RTE_EVENTS) zlog_debug("ISIS-Rte (%s) route unchanged: %s", area->area_tag, buff); +#endif /* EXTREME_DEBUG */ isis_route_info_delete(rinfo_new); route_info = rinfo_old; } else { if (IS_DEBUG_RTE_EVENTS) - zlog_debug("ISIS-Rte (%s) route changed: %s", - area->area_tag, buff); + zlog_debug( + "ISIS-Rte (%s): route changed: %s, change: %s", + area->area_tag, buff, change_buf); isis_route_info_delete(rinfo_old); route_info = rinfo_new; UNSET_FLAG(route_info->flag, @@ -401,7 +416,9 @@ static void _isis_route_verify_table(struct isis_area *area, { struct route_node *rnode, *drnode; struct isis_route_info *rinfo; +#ifdef EXTREME_DEBUG char buff[SRCDEST2STR_BUFFER]; +#endif /* EXTREME_DEBUG */ for (rnode = route_top(table); rnode; rnode = srcdest_route_next(rnode)) { @@ -416,6 +433,7 @@ static void _isis_route_verify_table(struct isis_area *area, (const struct prefix **)&dst_p, (const struct prefix **)&src_p); +#ifdef EXTREME_DEBUG if (IS_DEBUG_RTE_EVENTS) { srcdest2str(dst_p, src_p, buff, sizeof(buff)); zlog_debug( @@ -434,6 +452,7 @@ static void _isis_route_verify_table(struct isis_area *area, : "inactive"), buff); } +#endif /* EXTREME_DEBUG */ isis_route_update(area, dst_p, src_p, rinfo); diff --git a/isisd/isis_zebra.c b/isisd/isis_zebra.c index a50eb607d9..15b51589ae 100644 --- a/isisd/isis_zebra.c +++ b/isisd/isis_zebra.c @@ -52,6 +52,7 @@ #include "isisd/isis_adjacency.h" #include "isisd/isis_te.h" #include "isisd/isis_sr.h" +#include "isisd/isis_ldp_sync.h" struct zclient *zclient; static struct zclient *zclient_sync; @@ -596,6 +597,44 @@ static void isis_zebra_connected(struct zclient *zclient) zclient_send_reg_requests(zclient, VRF_DEFAULT); } +/* + * opaque messages between processes + */ +static int isis_opaque_msg_handler(ZAPI_CALLBACK_ARGS) +{ + struct stream *s; + struct zapi_opaque_msg info; + struct ldp_igp_sync_if_state state; + struct ldp_igp_sync_announce announce; + struct ldp_igp_sync_hello hello; + int ret = 0; + + s = zclient->ibuf; + if (zclient_opaque_decode(s, &info) != 0) + return -1; + + switch (info.type) { + case LDP_IGP_SYNC_IF_STATE_UPDATE: + STREAM_GET(&state, s, sizeof(state)); + ret = isis_ldp_sync_state_update(state); + break; + case LDP_IGP_SYNC_ANNOUNCE_UPDATE: + STREAM_GET(&announce, s, sizeof(announce)); + ret = isis_ldp_sync_announce_update(announce); + break; + case LDP_IGP_SYNC_HELLO_UPDATE: + STREAM_GET(&hello, s, sizeof(hello)); + ret = isis_ldp_sync_hello_update(hello); + break; + default: + break; + } + +stream_failure: + + return ret; +} + void isis_zebra_init(struct thread_master *master, int instance) { /* Initialize asynchronous zclient. */ @@ -622,6 +661,8 @@ void isis_zebra_init(struct thread_master *master, int instance) */ zclient_sync->session_id = 1; zclient_sync->privs = &isisd_privs; + + zclient->opaque_msg_handler = isis_opaque_msg_handler; } void isis_zebra_stop(void) diff --git a/isisd/isisd.c b/isisd/isisd.c index 2a2c71b1fd..7bba783b39 100644 --- a/isisd/isisd.c +++ b/isisd/isisd.c @@ -76,6 +76,7 @@ unsigned long debug_flooding; unsigned long debug_bfd; unsigned long debug_tx_queue; unsigned long debug_sr; +unsigned long debug_ldp_sync; DEFINE_QOBJ_TYPE(isis_area) @@ -1207,6 +1208,8 @@ void print_debug(struct vty *vty, int flags, int onoff) vty_out(vty, "IS-IS Flooding debugging is %s\n", onoffs); if (flags & DEBUG_BFD) vty_out(vty, "IS-IS BFD debugging is %s\n", onoffs); + if (flags & DEBUG_LDP_SYNC) + vty_out(vty, "IS-IS ldp-sync debugging is %s\n", onoffs); } DEFUN_NOSH (show_debugging, @@ -1244,6 +1247,8 @@ DEFUN_NOSH (show_debugging, print_debug(vty, DEBUG_FLOODING, 1); if (IS_DEBUG_BFD) print_debug(vty, DEBUG_BFD, 1); + if (IS_DEBUG_LDP_SYNC) + print_debug(vty, DEBUG_LDP_SYNC, 1); return CMD_SUCCESS; } @@ -1312,6 +1317,10 @@ static int config_write_debug(struct vty *vty) vty_out(vty, "debug " PROTO_NAME " bfd\n"); write++; } + if (IS_DEBUG_LDP_SYNC) { + vty_out(vty, "debug " PROTO_NAME " ldp-sync\n"); + write++; + } write += spf_backoff_write_config(vty); return write; @@ -1668,11 +1677,32 @@ DEFUN (no_debug_isis_bfd, return CMD_SUCCESS; } -DEFUN(show_hostname, show_hostname_cmd, - "show " PROTO_NAME " [vrf <NAME|all>] hostname", - SHOW_STR PROTO_HELP VRF_CMD_HELP_STR - "All VRFs\n" - "IS-IS Dynamic hostname mapping\n") +DEFUN(debug_isis_ldp_sync, debug_isis_ldp_sync_cmd, + "debug " PROTO_NAME " ldp-sync", + DEBUG_STR PROTO_HELP PROTO_NAME " interaction with LDP-Sync\n") +{ + debug_ldp_sync |= DEBUG_LDP_SYNC; + print_debug(vty, DEBUG_LDP_SYNC, 1); + + return CMD_SUCCESS; +} + +DEFUN(no_debug_isis_ldp_sync, no_debug_isis_ldp_sync_cmd, + "no debug " PROTO_NAME " ldp-sync", + NO_STR UNDEBUG_STR PROTO_HELP PROTO_NAME " interaction with LDP-Sync\n") +{ + debug_ldp_sync &= ~DEBUG_LDP_SYNC; + print_debug(vty, DEBUG_LDP_SYNC, 0); + + return CMD_SUCCESS; +} + +DEFUN (show_hostname, + show_hostname_cmd, + "show " PROTO_NAME " hostname", + SHOW_STR + PROTO_HELP + "IS-IS Dynamic hostname mapping\n") { struct listnode *node; const char *vrf_name = VRF_DEFAULT_NAME; @@ -2830,6 +2860,8 @@ void isis_init(void) install_element(ENABLE_NODE, &no_debug_isis_lsp_sched_cmd); install_element(ENABLE_NODE, &debug_isis_bfd_cmd); install_element(ENABLE_NODE, &no_debug_isis_bfd_cmd); + install_element(ENABLE_NODE, &debug_isis_ldp_sync_cmd); + install_element(ENABLE_NODE, &no_debug_isis_ldp_sync_cmd); install_element(CONFIG_NODE, &debug_isis_adj_cmd); install_element(CONFIG_NODE, &no_debug_isis_adj_cmd); @@ -2857,6 +2889,8 @@ void isis_init(void) install_element(CONFIG_NODE, &no_debug_isis_lsp_sched_cmd); install_element(CONFIG_NODE, &debug_isis_bfd_cmd); install_element(CONFIG_NODE, &no_debug_isis_bfd_cmd); + install_element(CONFIG_NODE, &debug_isis_ldp_sync_cmd); + install_element(CONFIG_NODE, &no_debug_isis_ldp_sync_cmd); install_default(ROUTER_NODE); diff --git a/isisd/isisd.h b/isisd/isisd.h index c26a62dfac..d8df6eead9 100644 --- a/isisd/isisd.h +++ b/isisd/isisd.h @@ -35,6 +35,7 @@ #include "isis_lsp.h" #include "isis_memory.h" #include "qobj.h" +#include "ldp_sync.h" #ifdef FABRICD static const bool fabricd = true; @@ -93,6 +94,7 @@ struct isis { uint32_t circuit_ids_used[8]; /* 256 bits to track circuit ids 1 through 255 */ struct route_table *ext_info[REDIST_PROTOCOL_COUNT]; + struct ldp_sync_info_cmd ldp_sync_cmd; /* MPLS LDP-IGP Sync */ }; extern struct isis_master *im; @@ -275,6 +277,7 @@ extern unsigned long debug_flooding; extern unsigned long debug_bfd; extern unsigned long debug_tx_queue; extern unsigned long debug_sr; +extern unsigned long debug_ldp_sync; #define DEBUG_ADJ_PACKETS (1<<0) #define DEBUG_SNP_PACKETS (1<<1) @@ -289,6 +292,7 @@ extern unsigned long debug_sr; #define DEBUG_BFD (1<<10) #define DEBUG_TX_QUEUE (1<<11) #define DEBUG_SR (1<<12) +#define DEBUG_LDP_SYNC (1 << 13) /* Debug related macro. */ #define IS_DEBUG_ADJ_PACKETS (debug_adj_pkt & DEBUG_ADJ_PACKETS) @@ -304,6 +308,7 @@ extern unsigned long debug_sr; #define IS_DEBUG_BFD (debug_bfd & DEBUG_BFD) #define IS_DEBUG_TX_QUEUE (debug_tx_queue & DEBUG_TX_QUEUE) #define IS_DEBUG_SR (debug_sr & DEBUG_SR) +#define IS_DEBUG_LDP_SYNC (debug_ldp_sync & DEBUG_LDP_SYNC) #define lsp_debug(...) \ do { \ diff --git a/isisd/subdir.am b/isisd/subdir.am index 9e855ad8cf..50d4746534 100644 --- a/isisd/subdir.am +++ b/isisd/subdir.am @@ -8,6 +8,7 @@ sbin_PROGRAMS += isisd/isisd dist_examples_DATA += isisd/isisd.conf.sample vtysh_scan += \ isisd/isis_cli.c \ + isisd/isis_ldp_sync.c \ isisd/isis_redist.c \ isisd/isis_spf.c \ isisd/isis_te.c \ @@ -36,6 +37,7 @@ noinst_HEADERS += \ isisd/isis_errors.h \ isisd/isis_events.h \ isisd/isis_flags.h \ + isisd/isis_ldp_sync.h \ isisd/isis_lsp.h \ isisd/isis_memory.h \ isisd/isis_misc.h \ @@ -69,6 +71,7 @@ LIBISIS_SOURCES = \ isisd/isis_errors.c \ isisd/isis_events.c \ isisd/isis_flags.c \ + isisd/isis_ldp_sync.c \ isisd/isis_lsp.c \ isisd/isis_memory.c \ isisd/isis_misc.c \ diff --git a/ldpd/adjacency.c b/ldpd/adjacency.c index 0bdd2423c7..4e09a6c4c9 100644 --- a/ldpd/adjacency.c +++ b/ldpd/adjacency.c @@ -125,6 +125,9 @@ adj_del(struct adj *adj, uint32_t notif_status) switch (adj->source.type) { case HELLO_LINK: RB_REMOVE(ia_adj_head, &adj->source.link.ia->adj_tree, adj); + + if (nbr) + ldp_sync_fsm_adj_event(adj, LDP_SYNC_EVT_ADJ_DEL); break; case HELLO_TARGETED: adj->source.target->adj = NULL; diff --git a/ldpd/control.c b/ldpd/control.c index cde99dc8a9..6554f0a6f1 100644 --- a/ldpd/control.c +++ b/ldpd/control.c @@ -263,6 +263,9 @@ control_dispatch_imsg(struct thread *thread) nbr_clear_ctl(imsg.data); break; + case IMSG_CTL_SHOW_LDP_SYNC: + ldpe_ldp_sync_ctl(c); + break; case IMSG_CTL_LOG_VERBOSE: /* ignore */ break; diff --git a/ldpd/hello.c b/ldpd/hello.c index ac24704bca..caf63c13d7 100644 --- a/ldpd/hello.c +++ b/ldpd/hello.c @@ -378,6 +378,7 @@ recv_hello(struct in_addr lsr_id, struct ldp_msg *msg, int af, adj->nbr = nbr; RB_INSERT(nbr_adj_head, &nbr->adj_tree, adj); } + ldp_sync_fsm_adj_event(adj, LDP_SYNC_EVT_ADJ_NEW); } adj->ds_tlv = ds_tlv; diff --git a/ldpd/interface.c b/ldpd/interface.c index 371c7d0bb1..bc8f26bc58 100644 --- a/ldpd/interface.c +++ b/ldpd/interface.c @@ -23,6 +23,7 @@ #include "ldpd.h" #include "ldpe.h" #include "log.h" +#include "ldp_debug.h" #include "sockopt.h" @@ -40,6 +41,17 @@ static int if_leave_ipv4_group(struct iface *, struct in_addr *); static int if_join_ipv6_group(struct iface *, struct in6_addr *); static int if_leave_ipv6_group(struct iface *, struct in6_addr *); +static int ldp_sync_fsm_init(struct iface *iface, int state); +static int ldp_sync_act_iface_start_sync(struct iface *iface); +static int iface_wait_for_ldp_sync_timer(struct thread *thread); +static void start_wait_for_ldp_sync_timer(struct iface *iface); +static void stop_wait_for_ldp_sync_timer(struct iface *iface); +static int ldp_sync_act_ldp_start_sync(struct iface *iface); +static int ldp_sync_act_ldp_complete_sync(struct iface *iface); +static int iface_to_oper_nbr_count(struct iface *iface, unsigned int type); +static void ldp_sync_get_peer_ldp_id(struct iface *iface, + struct in_addr *peer_ldp_id); + RB_GENERATE(iface_head, iface, entry, iface_compare) static __inline int @@ -87,6 +99,9 @@ ldpe_if_init(struct iface *iface) iface->ipv6.iface = iface; iface->ipv6.state = IF_STA_DOWN; RB_INIT(ia_adj_head, &iface->ipv6.adj_tree); + + /* LGP IGP Sync */ + ldp_sync_fsm_init(iface, LDP_SYNC_STA_NOT_ACH); } void @@ -96,6 +111,8 @@ ldpe_if_exit(struct iface *iface) log_debug("%s: interface %s", __func__, iface->name); + ldp_sync_fsm(iface, LDP_SYNC_EVT_CONFIG_LDP_OFF); + if (iface->ipv4.state == IF_STA_ACTIVE) if_reset(iface, AF_INET); if (iface->ipv6.state == IF_STA_ACTIVE) @@ -138,6 +155,10 @@ if_update_info(struct iface *iface, struct kif *kif) kif->flags & IFF_MULTICAST) iface->type = IF_TYPE_BROADCAST; + if (ldpd_process == PROC_LDP_ENGINE && iface->operative && + !kif->operative) + ldp_sync_fsm(iface, LDP_SYNC_EVT_IFACE_SHUTDOWN); + /* get index and flags */ iface->ifindex = kif->ifindex; iface->operative = kif->operative; @@ -426,6 +447,12 @@ if_get_hello_interval(struct iface_af *ia) return (leconf->lhello_interval); } +uint16_t +if_get_wait_for_sync_interval(void) +{ + return (leconf->wait_for_sync_interval); +} + /* timers */ /* ARGSUSED */ static int @@ -484,6 +511,55 @@ if_to_ctl(struct iface_af *ia) return (&ictl); } +static void +ldp_sync_get_peer_ldp_id(struct iface *iface, struct in_addr *peer_ldp_id) +{ + struct iface_af *ia; + struct adj *adj; + + if (iface->ipv4.state == IF_STA_ACTIVE) { + ia = iface_af_get(iface, AF_INET); + RB_FOREACH(adj, ia_adj_head, &ia->adj_tree) + if (adj->nbr && adj->nbr->state == NBR_STA_OPER) { + *peer_ldp_id = adj->nbr->id; + return; + } + } + + if (iface->ipv6.state == IF_STA_ACTIVE) { + ia = iface_af_get(iface, AF_INET6); + RB_FOREACH(adj, ia_adj_head, &ia->adj_tree) + if (adj->nbr && adj->nbr->state == NBR_STA_OPER) { + *peer_ldp_id = adj->nbr->id; + return; + } + } +} + +struct ctl_ldp_sync * +ldp_sync_to_ctl(struct iface *iface) +{ + static struct ctl_ldp_sync ictl; + + memcpy(ictl.name, iface->name, sizeof(ictl.name)); + ictl.ifindex = iface->ifindex; + ictl.in_sync = (iface->ldp_sync.state == LDP_SYNC_STA_ACH); + ictl.wait_time = if_get_wait_for_sync_interval(); + ictl.timer_running = iface->ldp_sync.wait_for_sync_timer ? true : false; + + if (iface->ldp_sync.wait_for_sync_timer) + ictl.wait_time_remaining = + thread_timer_remain_second(iface->ldp_sync.wait_for_sync_timer); + else + ictl.wait_time_remaining = 0; + + memset(&ictl.peer_ldp_id, 0, sizeof(ictl.peer_ldp_id)); + + ldp_sync_get_peer_ldp_id(iface, &ictl.peer_ldp_id); + + return (&ictl); +} + /* multicast membership sockopts */ in_addr_t if_get_ipv4_addr(struct iface *iface) @@ -576,3 +652,344 @@ if_leave_ipv6_group(struct iface *iface, struct in6_addr *addr) return (0); } + +const struct { + int state; + enum ldp_sync_event event; + enum ldp_sync_action action; + int new_state; +} ldp_sync_fsm_tbl[] = { + /* current state event that happened action to take resulting state */ +/* LDP IGP Sync not achieved */ + {LDP_SYNC_STA_NOT_ACH, LDP_SYNC_EVT_LDP_SYNC_START, LDP_SYNC_ACT_LDP_START_SYNC, 0}, + {LDP_SYNC_STA_NOT_ACH, LDP_SYNC_EVT_LDP_SYNC_COMPLETE, LDP_SYNC_ACT_LDP_COMPLETE_SYNC, LDP_SYNC_STA_ACH}, + {LDP_SYNC_STA_NOT_ACH, LDP_SYNC_EVT_CONFIG_LDP_OFF, LDP_SYNC_ACT_CONFIG_LDP_OFF, 0}, + {LDP_SYNC_STA_NOT_ACH, LDP_SYNC_EVT_IFACE_SHUTDOWN, LDP_SYNC_ACT_IFACE_SHUTDOWN, 0}, + {LDP_SYNC_STA_NOT_ACH, LDP_SYNC_EVT_SESSION_CLOSE, LDP_SYNC_ACT_NOTHING, 0}, + {LDP_SYNC_STA_NOT_ACH, LDP_SYNC_EVT_ADJ_DEL, LDP_SYNC_ACT_NOTHING, 0}, + {LDP_SYNC_STA_NOT_ACH, LDP_SYNC_EVT_ADJ_NEW, LDP_SYNC_ACT_NOTHING, 0}, +/* LDP IGP Sync achieved */ + {LDP_SYNC_STA_ACH, LDP_SYNC_EVT_CONFIG_LDP_OFF, LDP_SYNC_ACT_CONFIG_LDP_OFF, LDP_SYNC_STA_NOT_ACH}, + {LDP_SYNC_STA_ACH, LDP_SYNC_EVT_LDP_SYNC_COMPLETE, LDP_SYNC_ACT_NOTHING, 0}, + {LDP_SYNC_STA_ACH, LDP_SYNC_EVT_LDP_SYNC_START, LDP_SYNC_ACT_NOTHING, 0}, + {LDP_SYNC_STA_ACH, LDP_SYNC_EVT_IFACE_SHUTDOWN, LDP_SYNC_ACT_IFACE_SHUTDOWN, LDP_SYNC_STA_NOT_ACH}, + {LDP_SYNC_STA_ACH, LDP_SYNC_EVT_SESSION_CLOSE, LDP_SYNC_ACT_IFACE_START_SYNC, LDP_SYNC_STA_NOT_ACH}, + {LDP_SYNC_STA_ACH, LDP_SYNC_EVT_ADJ_DEL, LDP_SYNC_ACT_IFACE_START_SYNC, LDP_SYNC_STA_NOT_ACH}, + {LDP_SYNC_STA_ACH, LDP_SYNC_EVT_ADJ_NEW, LDP_SYNC_ACT_NOTHING, 0}, + {-1, LDP_SYNC_EVT_NOTHING, LDP_SYNC_ACT_NOTHING, 0}, +}; + +const char * const ldp_sync_event_names[] = { + "NOTHING", + "LDP SYNC START", + "LDP SYNC COMPLETE", + "CONFIG LDP OFF", + "IFACE SYNC START (ADJ DEL)", + "IFACE SYNC START (ADJ NEW)", + "IFACE SYNC START (SESSION CLOSE)", + "IFACE SYNC START (CONFIG LDP ON)", + "IFACE SHUTDOWN", + "N/A" +}; + +const char * const ldp_sync_action_names[] = { + "NOTHING", + "IFACE SYNC START", + "LDP START SYNC", + "LDP COMPLETE SYNC", + "CONFIG LDP OFF", + "IFACE SHUTDOWN", + "N/A" +}; + +const char * +ldp_sync_state_name(int state) +{ + switch (state) { + case LDP_SYNC_STA_NOT_ACH: + return ("NOT ACHIEVED"); + case LDP_SYNC_STA_ACH: + return ("ACHIEVED"); + default: + return ("UNKNOWN"); + } +} + +static int +send_ldp_sync_state_update(char *name, int ifindex, int sync_start) +{ + debug_evt_ldp_sync("%s: interface %s (%d), sync_start=%d", + __func__, name, ifindex, sync_start); + + struct ldp_igp_sync_if_state state; + + state.ifindex = ifindex; + state.sync_start = sync_start; + + return ldpe_imsg_compose_parent(IMSG_LDP_SYNC_IF_STATE_UPDATE, + getpid(), &state, sizeof(state)); +} + +static int +ldp_sync_act_iface_start_sync(struct iface *iface) +{ + send_ldp_sync_state_update(iface->name, iface->ifindex, true); + + return (0); +} + +static int +iface_wait_for_ldp_sync_timer(struct thread *thread) +{ + struct iface *iface = THREAD_ARG(thread); + + ldp_sync_fsm(iface, LDP_SYNC_EVT_LDP_SYNC_COMPLETE); + + return (0); +} + +static void start_wait_for_ldp_sync_timer(struct iface *iface) +{ + if (iface->ldp_sync.wait_for_sync_timer) + return; + + THREAD_TIMER_OFF(iface->ldp_sync.wait_for_sync_timer); + iface->ldp_sync.wait_for_sync_timer = NULL; + thread_add_timer(master, iface_wait_for_ldp_sync_timer, iface, + if_get_wait_for_sync_interval(), + &iface->ldp_sync.wait_for_sync_timer); +} + +static void stop_wait_for_ldp_sync_timer(struct iface *iface) +{ + THREAD_TIMER_OFF(iface->ldp_sync.wait_for_sync_timer); + iface->ldp_sync.wait_for_sync_timer = NULL; +} + +static int +ldp_sync_act_ldp_start_sync(struct iface *iface) +{ + start_wait_for_ldp_sync_timer(iface); + + return 0; +} + +static int +ldp_sync_act_ldp_complete_sync(struct iface *iface) +{ + send_ldp_sync_state_update(iface->name, iface->ifindex, false); + + return 0; +} + +static int +iface_to_oper_nbr_count(struct iface *iface, unsigned int type) +{ + int oper_nbr_count = 0; + struct adj *adj; + + RB_FOREACH(adj, ia_adj_head, &iface->ipv4.adj_tree) { + if (type == adj->source.type && adj->nbr && + adj->nbr->state == NBR_STA_OPER) + oper_nbr_count++; + } + + RB_FOREACH(adj, ia_adj_head, &iface->ipv6.adj_tree) { + if (type == adj->source.type && adj->nbr && + adj->nbr->state == NBR_STA_OPER) + oper_nbr_count++; + } + + return oper_nbr_count; +} + +int +ldp_sync_fsm_adj_event(struct adj *adj, enum ldp_sync_event event) +{ + if (adj->source.type != HELLO_LINK) + return -1; + + struct iface *iface = adj->source.link.ia->iface; + + if (!iface->operative) + return 0; + + if (event == LDP_SYNC_EVT_ADJ_NEW) { + struct nbr *nbr = adj->nbr; + if (nbr && nbr->state == NBR_STA_OPER) { + event = LDP_SYNC_EVT_LDP_SYNC_START; + } + } else if (event == LDP_SYNC_EVT_ADJ_DEL) { + /* Ignore if an operational neighbor exists. + */ + int oper_nbr_count = iface_to_oper_nbr_count(iface, HELLO_LINK); + if (oper_nbr_count > 0) + return 0; + } + + debug_evt_ldp_sync("%s: event %s, " + "adj iface %s (%d) lsr-id %s " + "source address %s transport address %s", + __func__, ldp_sync_event_names[event], + adj->source.link.ia->iface->name, + adj->source.link.ia->iface->ifindex, + inet_ntoa(adj->lsr_id), + log_addr(adj_get_af(adj), &adj->source.link.src_addr), + log_addr(adj_get_af(adj), &adj->trans_addr)); + + return ldp_sync_fsm(iface, event); +} + +int +ldp_sync_fsm_nbr_event(struct nbr *nbr, enum ldp_sync_event event) +{ + struct adj *adj; + struct iface *iface = NULL; + RB_FOREACH(adj, nbr_adj_head, &nbr->adj_tree) { + if (HELLO_LINK != adj->source.type) + continue; + + iface = adj->source.link.ia->iface; + + if (!iface || !iface->operative) + continue; + + int oper_nbr_count = iface_to_oper_nbr_count(iface, HELLO_LINK); + + if (event == LDP_SYNC_EVT_SESSION_CLOSE && oper_nbr_count > 0) + /* Ignore if an operational neighbor exists. + */ + continue; + + debug_evt_ldp_sync("%s: event %s, iface %s, lsr-id %s", + __func__, ldp_sync_event_names[event], + iface->name, inet_ntoa(nbr->id)); + + ldp_sync_fsm(iface, event); + } + + return 0; +} + +int +ldp_sync_fsm_state_req(struct ldp_igp_sync_if_state_req *state_req) +{ + debug_evt_ldp_sync("%s: interface %s (%d) proto %s", + __func__, state_req->name, state_req->ifindex, + zebra_route_string(state_req->proto)); + + struct iface *iface = if_lookup_name(leconf, state_req->name); + + if (!iface) { + debug_evt_ldp_sync("%s: Warning: Ignoring LDP IGP SYNC " + "interface state request for interface %s (%d). " + "Interface does not exist in LDP.", + __func__, state_req->name, state_req->ifindex); + + return 0; + } + + return send_ldp_sync_state_update(state_req->name, + state_req->ifindex, + (iface->ldp_sync.state != LDP_SYNC_STA_ACH)); +} + +static int +ldp_sync_fsm_init(struct iface *iface, int state) +{ + int old_state = iface->ldp_sync.state; + + iface->ldp_sync.state = state; + stop_wait_for_ldp_sync_timer(iface); + + send_ldp_sync_state_update(iface->name, iface->ifindex, + (iface->ldp_sync.state != LDP_SYNC_STA_ACH)); + + if (old_state != iface->ldp_sync.state) { + debug_evt_ldp_sync("%s: resulted in " + "changing state for interface %s (%d) from %s to %s", + __func__, + iface->name, iface->ifindex, + ldp_sync_state_name(old_state), + ldp_sync_state_name(iface->ldp_sync.state)); + } + + return 0; +} + +int +ldp_sync_fsm(struct iface *iface, enum ldp_sync_event event) +{ + int old_state = iface->ldp_sync.state; + int new_state = 0; + int i; + + for (i = 0; ldp_sync_fsm_tbl[i].state != -1; i++) + if ((ldp_sync_fsm_tbl[i].state & old_state) && + (ldp_sync_fsm_tbl[i].event == event)) { + new_state = ldp_sync_fsm_tbl[i].new_state; + break; + } + + if (ldp_sync_fsm_tbl[i].state == -1) { + /* event outside of the defined fsm, ignore it. */ + log_warnx("%s: interface %s, event %s not expected in " + "state %s ", __func__, iface->name, + ldp_sync_event_names[event], + ldp_sync_state_name(old_state)); + return (0); + } + + if (new_state != 0) + iface->ldp_sync.state = new_state; + + switch (ldp_sync_fsm_tbl[i].action) { + case LDP_SYNC_ACT_IFACE_START_SYNC: + ldp_sync_act_iface_start_sync(iface); + break; + case LDP_SYNC_ACT_LDP_START_SYNC: + ldp_sync_act_ldp_start_sync(iface); + break; + case LDP_SYNC_ACT_LDP_COMPLETE_SYNC: + ldp_sync_act_ldp_complete_sync(iface); + break; + case LDP_SYNC_ACT_CONFIG_LDP_OFF: + ldp_sync_fsm_init(iface, LDP_SYNC_STA_NOT_ACH); + break; + case LDP_SYNC_ACT_IFACE_SHUTDOWN: + ldp_sync_fsm_init(iface, iface->ldp_sync.state); + break; + case LDP_SYNC_ACT_NOTHING: + /* do nothing */ + break; + } + + if (old_state != iface->ldp_sync.state) { + + debug_evt_ldp_sync("%s: event %s resulted in action %s " + "for interface %s, changing state from %s to %s", + __func__, ldp_sync_event_names[event], + ldp_sync_action_names[ldp_sync_fsm_tbl[i].action], + iface->name, ldp_sync_state_name(old_state), + ldp_sync_state_name(iface->ldp_sync.state)); + + } else { + debug_evt_ldp_sync("%s: event %s resulted in action %s " + "for interface %s, remaining in state %s", + __func__, ldp_sync_event_names[event], + ldp_sync_action_names[ldp_sync_fsm_tbl[i].action], + iface->name, + ldp_sync_state_name(iface->ldp_sync.state)); + } + + return (0); +} + +void +ldp_sync_fsm_reset_all(void) +{ + struct iface *iface; + + RB_FOREACH(iface, iface_head, &leconf->iface_tree) + ldp_sync_fsm(iface, LDP_SYNC_EVT_CONFIG_LDP_OFF); +} diff --git a/ldpd/ldp.h b/ldpd/ldp.h index 5d1bf81ec7..4bad3afc3c 100644 --- a/ldpd/ldp.h +++ b/ldpd/ldp.h @@ -51,6 +51,8 @@ #define INIT_DELAY_TMR 15 #define MAX_DELAY_TMR 120 +#define DFLT_WAIT_FOR_SYNC 10 + #define MIN_PWID_ID 1 #define MAX_PWID_ID 0xffffffff diff --git a/ldpd/ldp_debug.c b/ldpd/ldp_debug.c index b9ef60ff94..a8d9833dde 100644 --- a/ldpd/ldp_debug.c +++ b/ldpd/ldp_debug.c @@ -99,6 +99,11 @@ ldp_vty_debug(struct vty *vty, const char *negate, const char *type_str, DEBUG_ON(msg, LDP_DEBUG_MSG_SEND_ALL); } } + } else if (strcmp(type_str, "sync") == 0) { + if (negate) + DEBUG_OFF(sync, LDP_DEBUG_SYNC); + else + DEBUG_ON(sync, LDP_DEBUG_SYNC); } else if (strcmp(type_str, "zebra") == 0) { if (negate) DEBUG_OFF(zebra, LDP_DEBUG_ZEBRA); @@ -137,6 +142,8 @@ ldp_vty_show_debugging(struct vty *vty) " LDP detailed messages debugging is on (outbound)\n"); else if (LDP_DEBUG(msg, LDP_DEBUG_MSG_SEND)) vty_out (vty," LDP messages debugging is on (outbound)\n"); + if (LDP_DEBUG(sync, LDP_DEBUG_SYNC)) + vty_out (vty, " LDP sync debugging is on\n"); if (LDP_DEBUG(zebra, LDP_DEBUG_ZEBRA)) vty_out (vty, " LDP zebra debugging is on\n"); vty_out (vty, "\n"); @@ -195,5 +202,10 @@ ldp_debug_config_write(struct vty *vty) write = 1; } + if (CONF_LDP_DEBUG(sync, LDP_DEBUG_SYNC)) { + vty_out (vty, "debug mpls ldp sync\n"); + write = 1; + } + return (write); } diff --git a/ldpd/ldp_debug.h b/ldpd/ldp_debug.h index 8ae144d93a..977734bddf 100644 --- a/ldpd/ldp_debug.h +++ b/ldpd/ldp_debug.h @@ -42,6 +42,10 @@ struct ldp_debug { int zebra; #define LDP_DEBUG_ZEBRA 0x01 + + int sync; +#define LDP_DEBUG_SYNC 0x01 + }; extern struct ldp_debug conf_ldp_debug; extern struct ldp_debug ldp_debug; @@ -143,4 +147,10 @@ do { \ log_debug("zebra[out]: " emsg, __VA_ARGS__); \ } while (0) +#define debug_evt_ldp_sync(emsg, ...) \ +do { \ + if (LDP_DEBUG(sync, LDP_DEBUG_SYNC)) \ + log_debug("sync: " emsg, __VA_ARGS__); \ +} while (0) + #endif /* _LDP_DEBUG_H_ */ diff --git a/ldpd/ldp_vty.h b/ldpd/ldp_vty.h index f6ba8f8c97..882874f1be 100644 --- a/ldpd/ldp_vty.h +++ b/ldpd/ldp_vty.h @@ -50,6 +50,7 @@ int ldp_vty_label_accept(struct vty *, const char *, const char *, const char * int ldp_vty_ttl_security(struct vty *, const char *); int ldp_vty_router_id(struct vty *, const char *, struct in_addr); int ldp_vty_ordered_control(struct vty *, const char *); +int ldp_vty_wait_for_sync_interval(struct vty *, const char *, long); int ldp_vty_ds_cisco_interop(struct vty *, const char *); int ldp_vty_trans_pref_ipv4(struct vty *, const char *); int ldp_vty_neighbor_password(struct vty *, const char *, struct in_addr, const char *); @@ -73,6 +74,7 @@ int ldp_vty_show_discovery(struct vty *, const char *, const char *, const char int ldp_vty_show_interface(struct vty *, const char *, const char *); int ldp_vty_show_capabilities(struct vty *, const char *); int ldp_vty_show_neighbor(struct vty *, const char *, int, const char *, const char *); +int ldp_vty_show_ldp_sync(struct vty *, const char *); int ldp_vty_show_atom_binding(struct vty *, const char *, unsigned long, unsigned long, const char *); int ldp_vty_show_atom_vc(struct vty *, const char *, const char *, diff --git a/ldpd/ldp_vty_cmds.c b/ldpd/ldp_vty_cmds.c index 53a384fe55..1f102f86fa 100644 --- a/ldpd/ldp_vty_cmds.c +++ b/ldpd/ldp_vty_cmds.c @@ -230,6 +230,17 @@ DEFPY (ldp_ordered_control, return (ldp_vty_ordered_control(vty, no)); } +DEFPY (ldp_wait_for_sync, + ldp_wait_for_sync_cmd, + "[no] wait-for-sync (1-10000)$waitforsync", + NO_STR + "Time to wait for LDP-IGP Sync to complete label exchange\n" + "Time (seconds)\n") +{ + return (ldp_vty_wait_for_sync_interval(vty, no, waitforsync)); + +} + DEFPY (ldp_discovery_targeted_hello_accept, ldp_discovery_targeted_hello_accept_cmd, "[no] discovery targeted-hello accept [from <(1-199)|(1300-2699)|WORD>$from_acl]", @@ -547,7 +558,7 @@ DEFPY (ldp_debug_mpls_ldp_discovery_hello, DEFPY (ldp_debug_mpls_ldp_type, ldp_debug_mpls_ldp_type_cmd, - "[no] debug mpls ldp <errors|event|labels|zebra>$type", + "[no] debug mpls ldp <errors|event|labels|sync|zebra>$type", NO_STR "Debugging functions\n" "MPLS information\n" @@ -555,6 +566,7 @@ DEFPY (ldp_debug_mpls_ldp_type, "Errors\n" "LDP event information\n" "LDP label allocation information\n" + "LDP sync information\n" "LDP zebra information\n") { return (ldp_vty_debug(vty, no, type, NULL, NULL)); @@ -695,6 +707,18 @@ DEFPY (ldp_show_mpls_ldp_neighbor_capabilities, return (ldp_vty_show_neighbor(vty, lsr_id_str, 1, NULL, json)); } +DEFPY (ldp_show_mpls_ldp_igp_sync, + ldp_show_mpls_ldp_igp_sync_cmd, + "show mpls ldp igp-sync [json]$json", + "Show mpls ldp ldp-sync information\n" + "MPLS information\n" + "Label Distribution Protocol\n" + "LDP-IGP Sync information\n" + JSON_STR) +{ + return (ldp_vty_show_ldp_sync(vty, json)); +} + DEFPY (ldp_show_l2vpn_atom_binding, ldp_show_l2vpn_atom_binding_cmd, "show l2vpn atom binding\ @@ -819,6 +843,7 @@ ldp_vty_init (void) install_element(LDP_NODE, &ldp_neighbor_ttl_security_cmd); install_element(LDP_NODE, &ldp_router_id_cmd); install_element(LDP_NODE, &ldp_ordered_control_cmd); + install_element(LDP_NODE, &ldp_wait_for_sync_cmd); install_element(LDP_IPV4_NODE, &ldp_discovery_link_holdtime_cmd); install_element(LDP_IPV4_NODE, &ldp_discovery_targeted_holdtime_cmd); @@ -888,4 +913,5 @@ ldp_vty_init (void) install_element(VIEW_NODE, &ldp_show_l2vpn_atom_binding_cmd); install_element(VIEW_NODE, &ldp_show_l2vpn_atom_vc_cmd); install_element(VIEW_NODE, &ldp_show_debugging_mpls_ldp_cmd); + install_element(VIEW_NODE, &ldp_show_mpls_ldp_igp_sync_cmd); } diff --git a/ldpd/ldp_vty_conf.c b/ldpd/ldp_vty_conf.c index 03dee23b47..c217cfc78a 100644 --- a/ldpd/ldp_vty_conf.c +++ b/ldpd/ldp_vty_conf.c @@ -285,6 +285,11 @@ ldp_config_write(struct vty *vty) if (ldpd_conf->flags & F_LDPD_ORDERED_CONTROL) vty_out (vty, " ordered-control\n"); + if (ldpd_conf->wait_for_sync_interval != DFLT_WAIT_FOR_SYNC && + ldpd_conf->wait_for_sync_interval != 0) + vty_out (vty, " wait-for-sync %u\n", + ldpd_conf->wait_for_sync_interval); + RB_FOREACH(nbrp, nbrp_head, &ldpd_conf->nbrp_tree) { if (nbrp->flags & F_NBRP_KEEPALIVE) vty_out (vty, " neighbor %s session holdtime %u\n", @@ -477,7 +482,6 @@ int ldp_vty_disc_holdtime(struct vty *vty, const char *negate, struct iface *iface; struct iface_af *ia; int af; - switch (vty->node) { case LDP_NODE: if (negate) { @@ -1014,6 +1018,24 @@ ldp_vty_ordered_control(struct vty *vty, const char *negate) return (CMD_SUCCESS); } +int ldp_vty_wait_for_sync_interval(struct vty *vty, const char *negate, + long secs) +{ + switch (vty->node) { + case LDP_NODE: + if (negate) + vty_conf->wait_for_sync_interval = DFLT_WAIT_FOR_SYNC; + else + vty_conf->wait_for_sync_interval = secs; + + ldp_config_apply(vty, vty_conf); + break; + default: + fatalx("ldp_vty_wait_for_sync_interval: unexpected node"); + } + return (CMD_SUCCESS); +} + int ldp_vty_ds_cisco_interop(struct vty *vty, const char * negate) { diff --git a/ldpd/ldp_vty_exec.c b/ldpd/ldp_vty_exec.c index d8ed5ccccc..609598a768 100644 --- a/ldpd/ldp_vty_exec.c +++ b/ldpd/ldp_vty_exec.c @@ -37,7 +37,8 @@ enum show_command { SHOW_NBR, SHOW_LIB, SHOW_L2VPN_PW, - SHOW_L2VPN_BINDING + SHOW_L2VPN_BINDING, + SHOW_LDP_SYNC }; struct show_params { @@ -86,6 +87,10 @@ static void show_discovery_detail_adj_json(json_object *, struct ctl_adj *); static int show_discovery_detail_msg_json(struct imsg *, struct show_params *, json_object *); +static int show_ldp_sync_msg(struct vty *, struct imsg *, + struct show_params *); +static int show_ldp_sync_msg_json(struct imsg *, + struct show_params *, json_object *); static int show_nbr_msg(struct vty *, struct imsg *, struct show_params *); @@ -122,7 +127,6 @@ static int show_l2vpn_pw_msg(struct vty *, struct imsg *, struct show_params *); static int show_l2vpn_pw_msg_json(struct imsg *, struct show_params *, json_object *); -static int ldp_vty_connect(struct imsgbuf *); static int ldp_vty_dispatch_msg(struct vty *, struct imsg *, enum show_command, struct show_params *, json_object *); @@ -207,6 +211,87 @@ show_interface_msg_json(struct imsg *imsg, struct show_params *params, } static int +show_ldp_sync_msg(struct vty *vty, struct imsg *imsg, + struct show_params *params) +{ + struct ctl_ldp_sync *iface; + + switch (imsg->hdr.type) { + case IMSG_CTL_SHOW_LDP_SYNC: + iface = imsg->data; + + vty_out (vty, "%s:\n", iface->name); + if (iface->in_sync) + vty_out (vty, " Status: initial label exchange complete\n"); + else + vty_out (vty, " Status: label exchange not complete\n"); + + if (iface->timer_running) { + vty_out (vty, " Wait time: %d seconds (%d seconds left)\n", + iface->wait_time, iface->wait_time_remaining); + vty_out (vty, " Timer is running\n"); + } else { + vty_out (vty, " Wait time: %d seconds\n", + iface->wait_time); + vty_out (vty, " Timer is not running\n"); + } + + if (iface->peer_ldp_id.s_addr) + vty_out (vty, " Peer LDP Identifier: %s:0\n", + inet_ntoa(iface->peer_ldp_id)); + + break; + case IMSG_CTL_END: + return (1); + default: + break; + } + + return (0); +} + +static int +show_ldp_sync_msg_json(struct imsg *imsg, struct show_params *params, + json_object *json) +{ + struct ctl_ldp_sync *iface; + json_object *json_iface; + + switch (imsg->hdr.type) { + case IMSG_CTL_SHOW_LDP_SYNC: + iface = imsg->data; + + json_iface = json_object_new_object(); + json_object_string_add(json_iface, "state", + iface->in_sync + ? "labelExchangeComplete" + : "labelExchangeNotComplete"); + json_object_int_add(json_iface, "waitTime", + iface->wait_time); + json_object_int_add(json_iface, "waitTimeRemaining", + iface->wait_time_remaining); + + if (iface->timer_running) + json_object_boolean_true_add(json_iface, "timerRunning"); + else + json_object_boolean_false_add(json_iface, "timerRunning"); + + json_object_string_add(json_iface, "peerLdpId", + iface->peer_ldp_id.s_addr ? + inet_ntoa(iface->peer_ldp_id) : ""); + + json_object_object_add(json, iface->name, json_iface); + break; + case IMSG_CTL_END: + return (1); + default: + break; + } + + return (0); +} + +static int show_discovery_msg(struct vty *vty, struct imsg *imsg, struct show_params *params) { @@ -1437,6 +1522,20 @@ ldp_vty_dispatch_iface(struct vty *vty, struct imsg *imsg, } static int +ldp_vty_dispatch_ldp_sync(struct vty *vty, struct imsg *imsg, + struct show_params *params, json_object *json) +{ + int ret; + + if (params->json) + ret = show_ldp_sync_msg_json(imsg, params, json); + else + ret = show_ldp_sync_msg(vty, imsg, params); + + return (ret); +} + +static int ldp_vty_dispatch_disc(struct vty *vty, struct imsg *imsg, struct show_params *params, json_object *json) { @@ -1684,6 +1783,8 @@ ldp_vty_dispatch_msg(struct vty *vty, struct imsg *imsg, enum show_command cmd, case SHOW_L2VPN_BINDING: return (ldp_vty_dispatch_l2vpn_binding(vty, imsg, params, json)); + case SHOW_LDP_SYNC: + return (ldp_vty_dispatch_ldp_sync(vty, imsg, params, json)); default: return (0); } @@ -1946,6 +2047,22 @@ ldp_vty_show_neighbor(struct vty *vty, const char *lsr_id, int capabilities, } int +ldp_vty_show_ldp_sync(struct vty *vty, const char *json) +{ + struct imsgbuf ibuf; + struct show_params params; + + if (ldp_vty_connect(&ibuf) < 0) + return (CMD_WARNING); + + memset(¶ms, 0, sizeof(params)); + params.json = (json) ? 1 : 0; + + imsg_compose(&ibuf, IMSG_CTL_SHOW_LDP_SYNC, 0, 0, -1, NULL, 0); + return (ldp_vty_dispatch(vty, &ibuf, SHOW_LDP_SYNC, ¶ms)); +} + +int ldp_vty_show_atom_binding(struct vty *vty, const char *peer, unsigned long local_label, unsigned long remote_label, const char *json) { diff --git a/ldpd/ldp_zebra.c b/ldpd/ldp_zebra.c index d828fbe977..16e9adc9d9 100644 --- a/ldpd/ldp_zebra.c +++ b/ldpd/ldp_zebra.c @@ -31,6 +31,7 @@ #include "ldpd.h" #include "ldpe.h" #include "lde.h" +#include "ldp_sync.h" #include "log.h" #include "ldp_debug.h" @@ -46,6 +47,14 @@ static int ldp_zebra_read_pw_status_update(ZAPI_CALLBACK_ARGS); static void ldp_zebra_connected(struct zclient *); static void ldp_zebra_filter_update(struct access_list *access); +static void ldp_zebra_opaque_register(void); +static void ldp_zebra_opaque_unregister(void); +static int ldp_sync_zebra_send_announce(void); +static int ldp_zebra_opaque_msg_handler(ZAPI_CALLBACK_ARGS); +static void ldp_sync_zebra_start_hello_timer(void); +static int ldp_sync_zebra_hello(struct thread *thread); +static void ldp_sync_zebra_init(void); + static struct zclient *zclient; static void @@ -103,6 +112,95 @@ pw2zpw(struct l2vpn_pw *pw, struct zapi_pw *zpw) sizeof(zpw->data.ldp.vpn_name)); } +static void +ldp_zebra_opaque_register(void) +{ + zclient_register_opaque(zclient, LDP_IGP_SYNC_IF_STATE_REQUEST); +} + +static void +ldp_zebra_opaque_unregister(void) +{ + zclient_unregister_opaque(zclient, LDP_IGP_SYNC_IF_STATE_REQUEST); +} + +int +ldp_sync_zebra_send_state_update(struct ldp_igp_sync_if_state *state) +{ + return zclient_send_opaque(zclient, LDP_IGP_SYNC_IF_STATE_UPDATE, + (const uint8_t *) state, sizeof(*state)); +} + +static int +ldp_sync_zebra_send_announce(void) +{ + struct ldp_igp_sync_announce announce; + announce.proto = ZEBRA_ROUTE_LDP; + + return zclient_send_opaque(zclient, LDP_IGP_SYNC_ANNOUNCE_UPDATE, + (const uint8_t *) &announce, sizeof(announce)); +} + +static int +ldp_zebra_opaque_msg_handler(ZAPI_CALLBACK_ARGS) +{ + struct stream *s; + struct zapi_opaque_msg info; + struct ldp_igp_sync_if_state_req state_req; + + s = zclient->ibuf; + + if (zclient_opaque_decode(s, &info) != 0) + return -1; + + switch (info.type) { + case LDP_IGP_SYNC_IF_STATE_REQUEST: + STREAM_GET(&state_req, s, sizeof(state_req)); + main_imsg_compose_ldpe(IMSG_LDP_SYNC_IF_STATE_REQUEST, 0, &state_req, + sizeof(state_req)); + break; + default: + break; + } + +stream_failure: + return 0; +} + +static void +ldp_sync_zebra_start_hello_timer(void) +{ + thread_add_timer_msec(master, ldp_sync_zebra_hello, NULL, 250, NULL); +} + +static int +ldp_sync_zebra_hello(struct thread *thread) +{ + static unsigned int sequence = 0; + struct ldp_igp_sync_hello hello; + + sequence++; + + hello.proto = ZEBRA_ROUTE_LDP; + hello.sequence = sequence; + + zclient_send_opaque(zclient, LDP_IGP_SYNC_HELLO_UPDATE, + (const uint8_t *) &hello, sizeof(hello)); + + ldp_sync_zebra_start_hello_timer(); + + return (0); +} + +static void +ldp_sync_zebra_init(void) +{ + ldp_sync_zebra_send_announce(); + + ldp_sync_zebra_start_hello_timer(); +} + + static int ldp_zebra_send_mpls_labels(int cmd, struct kroute *kr) { @@ -299,7 +397,7 @@ ldp_ifp_destroy(struct interface *ifp) } static int -ldp_interface_status_change_helper(struct interface *ifp) +ldp_interface_status_change(struct interface *ifp) { struct listnode *node; struct connected *ifc; @@ -330,12 +428,12 @@ ldp_interface_status_change_helper(struct interface *ifp) static int ldp_ifp_up(struct interface *ifp) { - return ldp_interface_status_change_helper(ifp); + return ldp_interface_status_change(ifp); } static int ldp_ifp_down(struct interface *ifp) { - return ldp_interface_status_change_helper(ifp); + return ldp_interface_status_change(ifp); } static int @@ -525,6 +623,10 @@ ldp_zebra_connected(struct zclient *zclient) ZEBRA_ROUTE_ALL, 0, VRF_DEFAULT); zebra_redistribute_send(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP6, ZEBRA_ROUTE_ALL, 0, VRF_DEFAULT); + + ldp_zebra_opaque_register(); + + ldp_sync_zebra_init(); } static void @@ -563,6 +665,7 @@ ldp_zebra_init(struct thread_master *master) zclient->redistribute_route_add = ldp_zebra_read_route; zclient->redistribute_route_del = ldp_zebra_read_route; zclient->pw_status_update = ldp_zebra_read_pw_status_update; + zclient->opaque_msg_handler = ldp_zebra_opaque_msg_handler; /* Access list initialize. */ access_list_add_hook(ldp_zebra_filter_update); @@ -572,6 +675,7 @@ ldp_zebra_init(struct thread_master *master) void ldp_zebra_destroy(void) { + ldp_zebra_opaque_unregister(); zclient_stop(zclient); zclient_free(zclient); zclient = NULL; diff --git a/ldpd/ldpd.c b/ldpd/ldpd.c index dca379e4eb..940333f83c 100644 --- a/ldpd/ldpd.c +++ b/ldpd/ldpd.c @@ -586,6 +586,13 @@ main_dispatch_ldpe(struct thread *thread) fatalx("IMSG_ACL_CHECK imsg with wrong len"); ldp_acl_reply(iev, (struct acl_check *)imsg.data); break; + case IMSG_LDP_SYNC_IF_STATE_UPDATE: + if (imsg.hdr.len != IMSG_HEADER_SIZE + + sizeof(struct ldp_igp_sync_if_state)) + fatalx("IMSG_LDP_SYNC_IF_STATE_UPDATE imsg with wrong len"); + + ldp_sync_zebra_send_state_update((struct ldp_igp_sync_if_state *)imsg.data); + break; default: log_debug("%s: error handling imsg %d", __func__, imsg.hdr.type); @@ -1148,6 +1155,7 @@ ldp_config_reset_main(struct ldpd_conf *conf) conf->lhello_interval = DEFAULT_HELLO_INTERVAL; conf->thello_holdtime = TARGETED_DFLT_HOLDTIME; conf->thello_interval = DEFAULT_HELLO_INTERVAL; + conf->wait_for_sync_interval = DFLT_WAIT_FOR_SYNC; conf->trans_pref = DUAL_STACK_LDPOV6; conf->flags = 0; } @@ -1278,6 +1286,14 @@ merge_config(struct ldpd_conf *conf, struct ldpd_conf *xconf) static void merge_global(struct ldpd_conf *conf, struct ldpd_conf *xconf) { + /* Removing global LDP config requires resetting LDP IGP Sync FSM */ + if ((conf->flags & F_LDPD_ENABLED) && + (!(xconf->flags & F_LDPD_ENABLED))) + { + if (ldpd_process == PROC_LDP_ENGINE) + ldp_sync_fsm_reset_all(); + } + /* change of router-id requires resetting all neighborships */ if (conf->rtr_id.s_addr != xconf->rtr_id.s_addr) { if (ldpd_process == PROC_LDP_ENGINE) { @@ -1303,6 +1319,7 @@ merge_global(struct ldpd_conf *conf, struct ldpd_conf *xconf) conf->lhello_interval = xconf->lhello_interval; conf->thello_holdtime = xconf->thello_holdtime; conf->thello_interval = xconf->thello_interval; + conf->wait_for_sync_interval = xconf->wait_for_sync_interval; if (conf->trans_pref != xconf->trans_pref) { if (ldpd_process == PROC_LDP_ENGINE) diff --git a/ldpd/ldpd.h b/ldpd/ldpd.h index c1bcc56c44..f8a94b4e2a 100644 --- a/ldpd/ldpd.h +++ b/ldpd/ldpd.h @@ -34,6 +34,7 @@ #include "zclient.h" #include "ldp.h" +#include "lib/ldp_sync.h" #define CONF_FILE "/etc/ldpd.conf" #define LDPD_USER "_ldpd" @@ -93,6 +94,7 @@ enum imsg_type { IMSG_CTL_SHOW_LIB_END, IMSG_CTL_SHOW_L2VPN_PW, IMSG_CTL_SHOW_L2VPN_BINDING, + IMSG_CTL_SHOW_LDP_SYNC, IMSG_CTL_CLEAR_NBR, IMSG_CTL_FIB_COUPLE, IMSG_CTL_FIB_DECOUPLE, @@ -153,7 +155,9 @@ enum imsg_type { IMSG_INIT, IMSG_PW_UPDATE, IMSG_FILTER_UPDATE, - IMSG_NBR_SHUTDOWN + IMSG_NBR_SHUTDOWN, + IMSG_LDP_SYNC_IF_STATE_REQUEST, + IMSG_LDP_SYNC_IF_STATE_UPDATE }; struct ldpd_init { @@ -227,6 +231,34 @@ enum nbr_action { NBR_ACT_CLOSE_SESSION }; +/* LDP IGP Sync states */ +#define LDP_SYNC_STA_UNKNOWN 0x0000 +#define LDP_SYNC_STA_NOT_ACH 0x0001 +#define LDP_SYNC_STA_ACH 0x0002 + +/* LDP IGP Sync events */ +enum ldp_sync_event { + LDP_SYNC_EVT_NOTHING, + LDP_SYNC_EVT_LDP_SYNC_START, + LDP_SYNC_EVT_LDP_SYNC_COMPLETE, + LDP_SYNC_EVT_CONFIG_LDP_OFF, + LDP_SYNC_EVT_ADJ_DEL, + LDP_SYNC_EVT_ADJ_NEW, + LDP_SYNC_EVT_SESSION_CLOSE, + LDP_SYNC_EVT_CONFIG_LDP_ON, + LDP_SYNC_EVT_IFACE_SHUTDOWN +}; + +/* LDP IGP Sync actions */ +enum ldp_sync_action { + LDP_SYNC_ACT_NOTHING, + LDP_SYNC_ACT_IFACE_START_SYNC, + LDP_SYNC_ACT_LDP_START_SYNC, + LDP_SYNC_ACT_LDP_COMPLETE_SYNC, + LDP_SYNC_ACT_CONFIG_LDP_OFF, + LDP_SYNC_ACT_IFACE_SHUTDOWN +}; + /* forward declarations */ RB_HEAD(global_adj_head, adj); RB_HEAD(nbr_adj_head, adj); @@ -310,6 +342,11 @@ struct iface_af { uint16_t hello_interval; }; +struct iface_ldp_sync { + int state; + struct thread *wait_for_sync_timer; +}; + struct iface { RB_ENTRY(iface) entry; char name[IF_NAMESIZE]; @@ -320,6 +357,7 @@ struct iface { int operative; struct iface_af ipv4; struct iface_af ipv6; + struct iface_ldp_sync ldp_sync; QOBJ_FIELDS }; RB_HEAD(iface_head, iface); @@ -518,6 +556,7 @@ struct ldpd_conf { uint16_t thello_holdtime; uint16_t thello_interval; uint16_t trans_pref; + uint16_t wait_for_sync_interval; int flags; QOBJ_FIELDS }; @@ -672,6 +711,16 @@ struct ctl_pw { uint8_t reason; }; +struct ctl_ldp_sync { + char name[IF_NAMESIZE]; + ifindex_t ifindex; + bool in_sync; + bool timer_running; + uint16_t wait_time; + uint16_t wait_time_remaining; + struct in_addr peer_ldp_id; +}; + extern struct ldpd_conf *ldpd_conf, *vty_conf; extern struct ldpd_global global; extern struct ldpd_init init; @@ -825,6 +874,7 @@ extern char ctl_sock_path[MAXPATHLEN]; /* ldp_zebra.c */ void ldp_zebra_init(struct thread_master *); void ldp_zebra_destroy(void); +int ldp_sync_zebra_send_state_update(struct ldp_igp_sync_if_state *); /* compatibility */ #ifndef __OpenBSD__ diff --git a/ldpd/ldpe.c b/ldpd/ldpe.c index 9078e711fb..d3374a62db 100644 --- a/ldpd/ldpe.c +++ b/ldpd/ldpe.c @@ -297,6 +297,7 @@ ldpe_dispatch_main(struct thread *thread) #endif int n, shut = 0; struct ldp_access *laccess; + struct ldp_igp_sync_if_state_req *ldp_sync_if_state_req; iev->ev_read = NULL; @@ -559,6 +560,15 @@ ldpe_dispatch_main(struct thread *thread) ldpe_check_filter_af(AF_INET6, &leconf->ipv6, laccess->name); break; + case IMSG_LDP_SYNC_IF_STATE_REQUEST: + if (imsg.hdr.len != IMSG_HEADER_SIZE + + sizeof(struct ldp_igp_sync_if_state_req)) { + log_warnx("%s: wrong imsg len", __func__); + break; + } + ldp_sync_if_state_req = imsg.data; + ldp_sync_fsm_state_req(ldp_sync_if_state_req); + break; default: log_debug("ldpe_dispatch_main: error handling imsg %d", imsg.hdr.type); @@ -975,6 +985,20 @@ ldpe_nbr_ctl(struct ctl_conn *c) } void +ldpe_ldp_sync_ctl(struct ctl_conn *c) +{ + struct iface *iface; + struct ctl_ldp_sync *ictl; + + RB_FOREACH(iface, iface_head, &leconf->iface_tree) { + ictl = ldp_sync_to_ctl(iface); + imsg_compose_event(&c->iev, IMSG_CTL_SHOW_LDP_SYNC, + 0, 0, -1, ictl, sizeof(struct ctl_ldp_sync)); + } + imsg_compose_event(&c->iev, IMSG_CTL_END, 0, 0, -1, NULL, 0); +} + +void mapping_list_add(struct mapping_head *mh, struct map *map) { struct mapping_entry *me; diff --git a/ldpd/ldpe.h b/ldpd/ldpe.h index 11650069b7..ef4702341b 100644 --- a/ldpd/ldpe.h +++ b/ldpd/ldpe.h @@ -28,6 +28,7 @@ #endif #include "ldpd.h" +#include "lib/ldp_sync.h" #define min(x,y) ((x) <= (y) ? (x) : (y)) #define max(x,y) ((x) > (y) ? (x) : (y)) @@ -212,6 +213,7 @@ void ldpe_iface_ctl(struct ctl_conn *c, ifindex_t ifidx); void ldpe_adj_ctl(struct ctl_conn *); void ldpe_adj_detail_ctl(struct ctl_conn *); void ldpe_nbr_ctl(struct ctl_conn *); +void ldpe_ldp_sync_ctl(struct ctl_conn *); void mapping_list_add(struct mapping_head *, struct map *); void mapping_list_clr(struct mapping_head *); @@ -229,8 +231,17 @@ void ldp_if_update(struct iface *, int); void if_update_all(int); uint16_t if_get_hello_holdtime(struct iface_af *); uint16_t if_get_hello_interval(struct iface_af *); +uint16_t if_get_wait_for_sync_interval(void); struct ctl_iface *if_to_ctl(struct iface_af *); in_addr_t if_get_ipv4_addr(struct iface *); +int ldp_sync_fsm_adj_event(struct adj *, enum ldp_sync_event); +int ldp_sync_fsm_nbr_event(struct nbr *, enum ldp_sync_event); +int ldp_sync_fsm_state_req(struct ldp_igp_sync_if_state_req *); +int ldp_sync_fsm(struct iface *, enum ldp_sync_event); +void ldp_sync_fsm_reset_all(void); +const char *ldp_sync_state_name(int); +const char *ldp_sync_event_name(int); +struct ctl_ldp_sync *ldp_sync_to_ctl(struct iface *); /* adjacency.c */ struct adj *adj_new(struct in_addr, struct hello_source *, diff --git a/ldpd/neighbor.c b/ldpd/neighbor.c index 6143beb6b9..236d3eaa58 100644 --- a/ldpd/neighbor.c +++ b/ldpd/neighbor.c @@ -764,6 +764,8 @@ nbr_act_session_operational(struct nbr *nbr) /* this is necessary to avoid ipc synchronization issues */ nbr_update_peerid(nbr); + ldp_sync_fsm_nbr_event(nbr, LDP_SYNC_EVT_LDP_SYNC_START); + memset(&lde_nbr, 0, sizeof(lde_nbr)); lde_nbr.id = nbr->id; lde_nbr.v4_enabled = nbr->v4_enabled; diff --git a/ldpd/packet.c b/ldpd/packet.c index c00008d120..3f73f8cd88 100644 --- a/ldpd/packet.c +++ b/ldpd/packet.c @@ -683,6 +683,8 @@ session_close(struct nbr *nbr) log_debug("%s: closing session with lsr-id %s", __func__, inet_ntoa(nbr->id)); + ldp_sync_fsm_nbr_event(nbr, LDP_SYNC_EVT_SESSION_CLOSE); + tcp_close(nbr->tcp); nbr_stop_ktimer(nbr); nbr_stop_ktimeout(nbr); diff --git a/lib/command.h b/lib/command.h index e20bfe3318..d828989118 100644 --- a/lib/command.h +++ b/lib/command.h @@ -421,6 +421,11 @@ struct cmd_node { #define CMD_VNI_RANGE "(1-16777215)" #define CONF_BACKUP_EXT ".sav" +#define MPLS_LDP_SYNC_STR "Enable MPLS LDP-SYNC\n" +#define NO_MPLS_LDP_SYNC_STR "Disable MPLS LDP-SYNC\n" +#define MPLS_LDP_SYNC_HOLDDOWN_STR \ + "Time to wait for LDP-SYNC to occur before restoring if cost\n" +#define NO_MPLS_LDP_SYNC_HOLDDOWN_STR "holddown timer disable\n" /* Command warnings. */ #define NO_PASSWD_CMD_WARNING \ diff --git a/lib/ldp_sync.c b/lib/ldp_sync.c new file mode 100644 index 0000000000..5dd045d88d --- /dev/null +++ b/lib/ldp_sync.c @@ -0,0 +1,93 @@ +/* + * ldp_sync.c: LDP-SYNC handling routines + * Copyright (C) 2020 Volta Networks, Inc. + * + * This program 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 of the License, or (at your option) + * any later version. + * + * This program 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 "command.h" +#include "memory.h" +#include "prefix.h" +#include "log.h" +#include "thread.h" +#include "stream.h" +#include "zclient.h" +#include "table.h" +#include "vty.h" +#include "ldp_sync.h" + +/* Library code */ +DEFINE_MTYPE_STATIC(LIB, LDP_SYNC_INFO, "LDP SYNC info") + +/* + * ldp_sync_info_create - Allocate the LDP_SYNC information + */ +struct ldp_sync_info *ldp_sync_info_create(void) +{ + struct ldp_sync_info *ldp_sync_info; + + ldp_sync_info = XCALLOC(MTYPE_LDP_SYNC_INFO, + sizeof(struct ldp_sync_info)); + assert(ldp_sync_info); + + ldp_sync_info->flags = 0; + ldp_sync_info->enabled = LDP_IGP_SYNC_DEFAULT; + ldp_sync_info->state = LDP_IGP_SYNC_STATE_NOT_REQUIRED; + ldp_sync_info->holddown = LDP_IGP_SYNC_HOLDDOWN_DEFAULT; + ldp_sync_info->t_holddown = NULL; + return ldp_sync_info; +} + +/* + * ldp_sync_info_free - Free the LDP_SYNC information. + */ +void ldp_sync_info_free(struct ldp_sync_info **ldp_sync_info) +{ + if (*ldp_sync_info) + XFREE(MTYPE_LDP_SYNC_INFO, *ldp_sync_info); +} + +bool ldp_sync_if_is_enabled(struct ldp_sync_info *ldp_sync_info) +{ + /* return true if LDP-SYNC is configured on this interface */ + if (ldp_sync_info && + ldp_sync_info->enabled == LDP_IGP_SYNC_ENABLED && + ldp_sync_info->state == LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP) + return true; + + return false; +} + +bool ldp_sync_if_down(struct ldp_sync_info *ldp_sync_info) +{ + /* Stop LDP-SYNC on this interface: + * if holddown timer is running stop it + * update state + */ + if (ldp_sync_info && ldp_sync_info->enabled == LDP_IGP_SYNC_ENABLED) { + if (ldp_sync_info->t_holddown != NULL) { + THREAD_TIMER_OFF(ldp_sync_info->t_holddown); + ldp_sync_info->t_holddown = NULL; + } + if (ldp_sync_info->state == LDP_IGP_SYNC_STATE_REQUIRED_UP) + ldp_sync_info->state = + LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP; + return true; + } + + return false; +} diff --git a/lib/ldp_sync.h b/lib/ldp_sync.h new file mode 100644 index 0000000000..daede566f0 --- /dev/null +++ b/lib/ldp_sync.h @@ -0,0 +1,91 @@ +/* + * Defines and structures common to LDP-Sync for OSPFv2 and OSPFv3 and ISIS + * Copyright (C) 2020 Volta Networks, Inc. + * + * This program 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 of the License, or (at your option) + * any later version. + * + * This program 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 _LIBLDPSYNC_H +#define _LIBLDPSYNC_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* LDP-IGP Sync values */ +#define LDP_SYNC_FLAG_ENABLE (1 << 0) /* LDP-SYNC enabled */ +#define LDP_SYNC_FLAG_HOLDDOWN (1 << 1) /* Holddown timer enabled */ +#define LDP_SYNC_FLAG_IF_CONFIG (1 << 2) /* LDP-SYNC enabled on interface */ +#define LDP_SYNC_FLAG_SET_METRIC (1 << 3) /* Metric has been set on ISIS intf */ + +#define LDP_IGP_SYNC_DEFAULT 0 +#define LDP_IGP_SYNC_ENABLED 1 + +#define LDP_IGP_SYNC_STATE_NOT_REQUIRED 0 +#define LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP 1 +#define LDP_IGP_SYNC_STATE_REQUIRED_UP 2 + +#define LDP_IGP_SYNC_HOLDDOWN_DEFAULT 0 + +#define LDP_IGP_SYNC_HELLO_TIMEOUT 1 + +/* LDP-IGP Sync structures */ +struct ldp_sync_info_cmd { + uint16_t flags; + uint16_t holddown; /* timer value */ + uint32_t sequence; /* hello sequence number */ + struct thread *t_hello; /* hello timer for detecting LDP going down */ +}; + +struct ldp_sync_info { + uint16_t flags; /* indicate if set on interface or globally */ + uint8_t enabled; /* enabled */ + uint8_t state; /* running state */ + uint16_t holddown; /* timer value */ + struct thread *t_holddown; /* holddown timer*/ + uint32_t metric[2]; /* isis interface metric */ +}; + +/* Prototypes. */ +extern struct ldp_sync_info *ldp_sync_info_create(void); +extern bool ldp_sync_if_is_enabled(struct ldp_sync_info *ldp_sync_info); +extern bool ldp_sync_if_down(struct ldp_sync_info *ldp_sync_info); +extern void ldp_sync_info_free(struct ldp_sync_info **ldp_sync_info); + +struct ldp_igp_sync_announce { + int proto; +}; + +struct ldp_igp_sync_if_state { + ifindex_t ifindex; + bool sync_start; +}; + +struct ldp_igp_sync_if_state_req { + int proto; + ifindex_t ifindex; + char name[INTERFACE_NAMSIZ]; +}; + +struct ldp_igp_sync_hello { + int proto; + unsigned int sequence; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBLDPSYNC_H */ diff --git a/lib/libfrr.c b/lib/libfrr.c index 500a02aacd..800596c563 100644 --- a/lib/libfrr.c +++ b/lib/libfrr.c @@ -45,6 +45,7 @@ #include "defaults.h" DEFINE_HOOK(frr_late_init, (struct thread_master * tm), (tm)) +DEFINE_HOOK(frr_very_late_init, (struct thread_master * tm), (tm)) DEFINE_KOOH(frr_early_fini, (), ()) DEFINE_KOOH(frr_fini, (), ()) @@ -913,6 +914,8 @@ static int frr_config_read_in(struct thread *t) __func__, nb_err_name(ret), errmsg); } + hook_call(frr_very_late_init, master); + return 0; } diff --git a/lib/libfrr.h b/lib/libfrr.h index 9d91ea9154..ab72299206 100644 --- a/lib/libfrr.h +++ b/lib/libfrr.h @@ -136,6 +136,7 @@ extern const char *frr_get_progname(void); extern enum frr_cli_mode frr_get_cli_mode(void); DECLARE_HOOK(frr_late_init, (struct thread_master * tm), (tm)) +DECLARE_HOOK(frr_very_late_init, (struct thread_master * tm), (tm)) extern void frr_config_fork(void); extern void frr_run(struct thread_master *master); diff --git a/lib/netns_linux.c b/lib/netns_linux.c index 98f359401e..e8d549b4e0 100644 --- a/lib/netns_linux.c +++ b/lib/netns_linux.c @@ -379,12 +379,20 @@ struct ns *ns_lookup(ns_id_t ns_id) return ns_lookup_internal(ns_id); } -void ns_walk_func(int (*func)(struct ns *)) +void ns_walk_func(int (*func)(struct ns *, + void *param_in, + void **param_out), + void *param_in, + void **param_out) { struct ns *ns = NULL; + int ret; - RB_FOREACH (ns, ns_head, &ns_tree) - func(ns); + RB_FOREACH (ns, ns_head, &ns_tree) { + ret = func(ns, param_in, param_out); + if (ret == NS_WALK_STOP) + return; + } } const char *ns_get_name(struct ns *ns) @@ -584,9 +592,35 @@ int ns_socket(int domain, int type, int protocol, ns_id_t ns_id) return ret; } +/* if relative link_nsid matches default netns, + * then return default absolute netns value + * otherwise, return NS_UNKNOWN + */ +ns_id_t ns_id_get_absolute(ns_id_t ns_id_reference, ns_id_t link_nsid) +{ + struct ns *ns; + + ns = ns_lookup(ns_id_reference); + if (!ns) + return NS_UNKNOWN; + + if (ns->relative_default_ns != link_nsid) + return NS_UNKNOWN; + + ns = ns_get_default(); + assert(ns); + return ns->ns_id; +} + ns_id_t ns_get_default_id(void) { if (default_ns) return default_ns->ns_id; return NS_DEFAULT_INTERNAL; } + +struct ns *ns_get_default(void) +{ + return default_ns; +} + diff --git a/lib/northbound.c b/lib/northbound.c index 18500a8bd2..99c6ab57ec 100644 --- a/lib/northbound.c +++ b/lib/northbound.c @@ -122,8 +122,8 @@ static int nb_node_new_cb(const struct lys_node *snode, void *arg) if (CHECK_FLAG(snode->nodetype, LYS_CONTAINER | LYS_LIST)) { bool config_only = true; - yang_snodes_iterate_subtree(snode, nb_node_check_config_only, - YANG_ITER_ALLOW_AUGMENTATIONS, + yang_snodes_iterate_subtree(snode, NULL, + nb_node_check_config_only, 0, &config_only); if (config_only) SET_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY); @@ -141,6 +141,7 @@ static int nb_node_new_cb(const struct lys_node *snode, void *arg) * another. */ nb_node->snode = snode; + assert(snode->priv == NULL); lys_set_private(snode, nb_node); return YANG_ITER_CONTINUE; @@ -383,6 +384,10 @@ static void nb_config_diff_add_change(struct nb_config_cbs *changes, { struct nb_config_change *change; + /* Ignore unimplemented nodes. */ + if (!dnode->schema->priv) + return; + change = XCALLOC(MTYPE_TMP, sizeof(*change)); change->cb.operation = operation; change->cb.seq = *seq; @@ -416,6 +421,10 @@ static void nb_config_diff_created(const struct lyd_node *dnode, uint32_t *seq, enum nb_operation operation; struct lyd_node *child; + /* Ignore unimplemented nodes. */ + if (!dnode->schema->priv) + return; + switch (dnode->schema->nodetype) { case LYS_LEAF: case LYS_LEAFLIST: @@ -450,6 +459,10 @@ static void nb_config_diff_created(const struct lyd_node *dnode, uint32_t *seq, static void nb_config_diff_deleted(const struct lyd_node *dnode, uint32_t *seq, struct nb_config_cbs *changes) { + /* Ignore unimplemented nodes. */ + if (!dnode->schema->priv) + return; + if (nb_operation_is_valid(NB_OP_DESTROY, dnode->schema)) nb_config_diff_add_change(changes, NB_OP_DESTROY, seq, dnode); else if (CHECK_FLAG(dnode->schema->nodetype, LYS_CONTAINER)) { @@ -618,7 +631,7 @@ static int nb_candidate_validate_code(struct nb_context *context, struct nb_node *nb_node; nb_node = child->schema->priv; - if (!nb_node->cbs.pre_validate) + if (!nb_node || !nb_node->cbs.pre_validate) goto next; ret = nb_callback_pre_validate(context, nb_node, child, @@ -1385,7 +1398,7 @@ static void nb_transaction_apply_finish(struct nb_transaction *transaction, struct nb_node *nb_node; nb_node = dnode->schema->priv; - if (!nb_node->cbs.apply_finish) + if (!nb_node || !nb_node->cbs.apply_finish) goto next; /* diff --git a/lib/northbound_cli.c b/lib/northbound_cli.c index ee080bca3f..6ce520149a 100644 --- a/lib/northbound_cli.c +++ b/lib/northbound_cli.c @@ -573,7 +573,7 @@ void nb_cli_show_dnode_cmds(struct vty *vty, struct lyd_node *root, struct nb_node *nb_node; nb_node = child->schema->priv; - if (!nb_node->cbs.cli_show) + if (!nb_node || !nb_node->cbs.cli_show) goto next; /* Skip default values. */ @@ -591,7 +591,7 @@ void nb_cli_show_dnode_cmds(struct vty *vty, struct lyd_node *root, parent = ly_iter_next_up(child); if (parent != NULL) { nb_node = parent->schema->priv; - if (nb_node->cbs.cli_show_end) + if (nb_node && nb_node->cbs.cli_show_end) (*nb_node->cbs.cli_show_end)(vty, parent); } diff --git a/lib/northbound_sysrepo.c b/lib/northbound_sysrepo.c index 3dec685927..b5ef040a3f 100644 --- a/lib/northbound_sysrepo.c +++ b/lib/northbound_sysrepo.c @@ -174,6 +174,9 @@ static int frr_sr_process_change(struct nb_config *candidate, xpath = sr_data->xpath; + DEBUGD(&nb_dbg_client_sysrepo, "sysrepo: processing change [xpath %s]", + xpath); + /* Non-presence container - nothing to do. */ if (sr_data->type == SR_CONTAINER_T) return NB_OK; @@ -223,7 +226,7 @@ static int frr_sr_process_change(struct nb_config *candidate, ret = nb_candidate_edit(candidate, nb_node, nb_op, xpath, NULL, data); yang_data_free(data); - if (ret != NB_OK) { + if (ret != NB_OK && ret != NB_ERR_NOT_FOUND) { flog_warn( EC_LIB_NB_CANDIDATE_EDIT_ERROR, "%s: failed to edit candidate configuration: operation [%s] xpath [%s]", @@ -235,8 +238,7 @@ static int frr_sr_process_change(struct nb_config *candidate, } static int frr_sr_config_change_cb_prepare(sr_session_ctx_t *session, - const char *module_name, - bool startup_config) + const char *module_name) { sr_change_iter_t *it; int ret; @@ -275,33 +277,17 @@ static int frr_sr_config_change_cb_prepare(sr_session_ctx_t *session, transaction = NULL; context.client = NB_CLIENT_SYSREPO; - if (startup_config) { - /* - * sysrepod sends the entire startup configuration using a - * single event (SR_EV_ENABLED). This means we need to perform - * the full two-phase commit protocol in one go here. - */ - ret = nb_candidate_commit(&context, candidate, true, NULL, NULL, - errmsg, sizeof(errmsg)); - if (ret != NB_OK && ret != NB_ERR_NO_CHANGES) - flog_warn( - EC_LIB_LIBSYSREPO, - "%s: failed to apply startup configuration: %s (%s)", - __func__, nb_err_name(ret), errmsg); - } else { - /* - * Validate the configuration changes and allocate all resources - * required to apply them. - */ - ret = nb_candidate_commit_prepare(&context, candidate, NULL, - &transaction, errmsg, - sizeof(errmsg)); - if (ret != NB_OK && ret != NB_ERR_NO_CHANGES) - flog_warn( - EC_LIB_LIBSYSREPO, - "%s: failed to prepare configuration transaction: %s (%s)", - __func__, nb_err_name(ret), errmsg); - } + /* + * Validate the configuration changes and allocate all resources + * required to apply them. + */ + ret = nb_candidate_commit_prepare(&context, candidate, NULL, + &transaction, errmsg, sizeof(errmsg)); + if (ret != NB_OK && ret != NB_ERR_NO_CHANGES) + flog_warn( + EC_LIB_LIBSYSREPO, + "%s: failed to prepare configuration transaction: %s (%s)", + __func__, nb_err_name(ret), errmsg); if (!transaction) nb_config_free(candidate); @@ -360,11 +346,8 @@ static int frr_sr_config_change_cb(sr_session_ctx_t *session, { switch (sr_ev) { case SR_EV_ENABLED: - return frr_sr_config_change_cb_prepare(session, module_name, - true); case SR_EV_CHANGE: - return frr_sr_config_change_cb_prepare(session, module_name, - false); + return frr_sr_config_change_cb_prepare(session, module_name); case SR_EV_DONE: return frr_sr_config_change_cb_apply(session, module_name); case SR_EV_ABORT: @@ -563,6 +546,10 @@ static void frr_sr_subscribe_config(struct yang_module *module) { int ret; + DEBUGD(&nb_dbg_client_sysrepo, + "sysrepo: subscribing for configuration changes made in the '%s' module", + module->name); + ret = sr_module_change_subscribe( session, module->name, NULL, frr_sr_config_change_cb, NULL, 0, SR_SUBSCR_DEFAULT | SR_SUBSCR_ENABLED | SR_SUBSCR_NO_THREAD, @@ -586,7 +573,7 @@ static int frr_sr_subscribe_state(const struct lys_node *snode, void *arg) nb_node = snode->priv; - DEBUGD(&nb_dbg_client_sysrepo, "%s: providing data to '%s'", __func__, + DEBUGD(&nb_dbg_client_sysrepo, "sysrepo: providing data to '%s'", nb_node->xpath); ret = sr_oper_get_items_subscribe( @@ -610,7 +597,7 @@ static int frr_sr_subscribe_rpc(const struct lys_node *snode, void *arg) nb_node = snode->priv; - DEBUGD(&nb_dbg_client_sysrepo, "%s: providing RPC to '%s'", __func__, + DEBUGD(&nb_dbg_client_sysrepo, "sysrepo: providing RPC to '%s'", nb_node->xpath); ret = sr_rpc_subscribe(session, nb_node->xpath, frr_sr_config_rpc_cb, @@ -742,7 +729,7 @@ static int frr_sr_finish(void) return 0; } -static int frr_sr_module_late_init(struct thread_master *tm) +static int frr_sr_module_very_late_init(struct thread_master *tm) { master = tm; @@ -753,6 +740,12 @@ static int frr_sr_module_late_init(struct thread_master *tm) } hook_register(frr_fini, frr_sr_finish); + + return 0; +} + +static int frr_sr_module_late_init(struct thread_master *tm) +{ frr_sr_cli_init(); return 0; @@ -761,6 +754,7 @@ static int frr_sr_module_late_init(struct thread_master *tm) static int frr_sr_module_init(void) { hook_register(frr_late_init, frr_sr_module_late_init); + hook_register(frr_very_late_init, frr_sr_module_very_late_init); return 0; } @@ -53,6 +53,11 @@ struct ns { /* Identifier, mapped on the NSID value */ ns_id_t internal_ns_id; + /* Identifier, value of NSID of default netns, + * relative value in that local netns + */ + ns_id_t relative_default_ns; + /* Name */ char *name; @@ -120,7 +125,14 @@ int ns_socket(int domain, int type, int protocol, ns_id_t ns_id); extern char *ns_netns_pathname(struct vty *vty, const char *name); /* Parse and execute a function on all the NETNS */ -extern void ns_walk_func(int (*func)(struct ns *)); +#define NS_WALK_CONTINUE 0 +#define NS_WALK_STOP 1 + +extern void ns_walk_func(int (*func)(struct ns *, + void *, + void **), + void *param_in, + void **param_out); /* API to get the NETNS name, from the ns pointer */ extern const char *ns_get_name(struct ns *ns); @@ -174,7 +186,9 @@ extern struct ns *ns_lookup_name(const char *name); */ extern int ns_enable(struct ns *ns, void (*func)(ns_id_t, void *)); extern struct ns *ns_get_created(struct ns *ns, char *name, ns_id_t ns_id); +extern ns_id_t ns_id_get_absolute(ns_id_t ns_id_reference, ns_id_t link_nsid); extern void ns_disable(struct ns *ns); +extern struct ns *ns_get_default(void); #ifdef __cplusplus } @@ -97,7 +97,8 @@ struct pbr_rule { uint32_t unique; struct pbr_filter filter; struct pbr_action action; - ifindex_t ifindex; + + char ifname[INTERFACE_NAMSIZ + 1]; }; /* TCP flags value shared diff --git a/lib/subdir.am b/lib/subdir.am index 1feaa56d13..b8bcee139b 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -42,6 +42,7 @@ lib_libfrr_la_SOURCES = \ lib/jhash.c \ lib/json.c \ lib/keychain.c \ + lib/ldp_sync.c \ lib/lib_errors.c \ lib/lib_vty.c \ lib/libfrr.c \ @@ -198,6 +199,7 @@ pkginclude_HEADERS += \ lib/jhash.h \ lib/json.h \ lib/keychain.h \ + lib/ldp_sync.h \ lib/lib_errors.h \ lib/lib_vty.h \ lib/libfrr.h \ diff --git a/lib/thread.c b/lib/thread.c index 19e4827283..db35a3f031 100644 --- a/lib/thread.c +++ b/lib/thread.c @@ -438,7 +438,8 @@ struct thread_master *thread_master_create(const char *name) pthread_cond_init(&rv->cancel_cond, NULL); /* Set name */ - rv->name = name ? XSTRDUP(MTYPE_THREAD_MASTER, name) : NULL; + name = name ? name : "default"; + rv->name = XSTRDUP(MTYPE_THREAD_MASTER, name); /* Initialize I/O task data structures */ getrlimit(RLIMIT_NOFILE, &limit); @@ -449,10 +450,13 @@ struct thread_master *thread_master_create(const char *name) rv->write = XCALLOC(MTYPE_THREAD_POLL, sizeof(struct thread *) * rv->fd_limit); + char tmhashname[strlen(name) + 32]; + snprintf(tmhashname, sizeof(tmhashname), "%s - threadmaster event hash", + name); rv->cpu_record = hash_create_size( 8, (unsigned int (*)(const void *))cpu_record_hash_key, (bool (*)(const void *, const void *))cpu_record_hash_cmp, - "Thread Hash"); + tmhashname); thread_list_init(&rv->event); thread_list_init(&rv->ready); @@ -159,10 +159,6 @@ struct vrf *vrf_get(vrf_id_t vrf_id, const char *name) struct vrf *vrf = NULL; int new = 0; - if (debug_vrf) - zlog_debug("VRF_GET: %s(%u)", name == NULL ? "(NULL)" : name, - vrf_id); - /* Nothing to see, move along here */ if (!name && vrf_id == VRF_UNKNOWN) return NULL; @@ -225,7 +221,8 @@ struct vrf *vrf_get(vrf_id_t vrf_id, const char *name) void vrf_delete(struct vrf *vrf) { if (debug_vrf) - zlog_debug("VRF %u is to be deleted.", vrf->vrf_id); + zlog_debug("VRF %s(%u) is to be deleted.", vrf->name, + vrf->vrf_id); if (vrf_is_enabled(vrf)) vrf_disable(vrf); @@ -282,7 +279,7 @@ int vrf_enable(struct vrf *vrf) return 1; if (debug_vrf) - zlog_debug("VRF %u is enabled.", vrf->vrf_id); + zlog_debug("VRF %s(%u) is enabled.", vrf->name, vrf->vrf_id); SET_FLAG(vrf->status, VRF_ACTIVE); @@ -312,11 +309,20 @@ void vrf_disable(struct vrf *vrf) UNSET_FLAG(vrf->status, VRF_ACTIVE); if (debug_vrf) - zlog_debug("VRF %u is to be disabled.", vrf->vrf_id); + zlog_debug("VRF %s(%u) is to be disabled.", vrf->name, + vrf->vrf_id); /* Till now, nothing to be done for the default VRF. */ // Pending: see why this statement. + + /* + * When the vrf is disabled let's + * handle all nexthop-groups associated + * with this vrf + */ + nexthop_group_disable_vrf(vrf); + if (vrf_master.vrf_disable_hook) (*vrf_master.vrf_disable_hook)(vrf); } @@ -653,7 +659,8 @@ int vrf_handler_create(struct vty *vty, const char *vrfname, } int vrf_netns_handler_create(struct vty *vty, struct vrf *vrf, char *pathname, - ns_id_t ns_id, ns_id_t internal_ns_id) + ns_id_t ns_id, ns_id_t internal_ns_id, + ns_id_t rel_def_ns_id) { struct ns *ns = NULL; @@ -700,6 +707,7 @@ int vrf_netns_handler_create(struct vty *vty, struct vrf *vrf, char *pathname, } ns = ns_get_created(ns, pathname, ns_id); ns->internal_ns_id = internal_ns_id; + ns->relative_default_ns = rel_def_ns_id; ns->vrf_ctxt = (void *)vrf; vrf->ns_ctxt = (void *)ns; /* update VRF netns NAME */ @@ -795,7 +803,9 @@ DEFUN_NOSH (vrf_netns, frr_with_privs(vrf_daemon_privs) { ret = vrf_netns_handler_create(vty, vrf, pathname, - NS_UNKNOWN, NS_UNKNOWN); + NS_UNKNOWN, + NS_UNKNOWN, + NS_UNKNOWN); } return ret; } @@ -315,7 +315,7 @@ extern int vrf_handler_create(struct vty *vty, const char *name, */ extern int vrf_netns_handler_create(struct vty *vty, struct vrf *vrf, char *pathname, ns_id_t ext_ns_id, - ns_id_t ns_id); + ns_id_t ns_id, ns_id_t rel_def_ns_id); /* used internally to enable or disable VRF. * Notify a change in the VRF ID of the VRF diff --git a/lib/yang.c b/lib/yang.c index 9bfdcb858c..5bf7758e18 100644 --- a/lib/yang.c +++ b/lib/yang.c @@ -147,11 +147,15 @@ struct yang_module *yang_module_find(const char *module_name) } int yang_snodes_iterate_subtree(const struct lys_node *snode, + const struct lys_module *module, yang_iterate_cb cb, uint16_t flags, void *arg) { struct lys_node *child; int ret = YANG_ITER_CONTINUE; + if (module && snode->module != module) + goto next; + if (CHECK_FLAG(flags, YANG_ITER_FILTER_IMPLICIT)) { switch (snode->nodetype) { case LYS_CASE: @@ -214,11 +218,8 @@ next: return YANG_ITER_CONTINUE; LY_TREE_FOR (snode->child, child) { - if (!CHECK_FLAG(flags, YANG_ITER_ALLOW_AUGMENTATIONS) - && child->parent != snode) - continue; - - ret = yang_snodes_iterate_subtree(child, cb, flags, arg); + ret = yang_snodes_iterate_subtree(child, module, cb, flags, + arg); if (ret == YANG_ITER_STOP) return ret; } @@ -233,15 +234,16 @@ int yang_snodes_iterate_module(const struct lys_module *module, int ret = YANG_ITER_CONTINUE; LY_TREE_FOR (module->data, snode) { - ret = yang_snodes_iterate_subtree(snode, cb, flags, arg); + ret = yang_snodes_iterate_subtree(snode, module, cb, flags, + arg); if (ret == YANG_ITER_STOP) return ret; } for (uint8_t i = 0; i < module->augment_size; i++) { ret = yang_snodes_iterate_subtree( - (const struct lys_node *)&module->augment[i], cb, flags, - arg); + (const struct lys_node *)&module->augment[i], module, + cb, flags, arg); if (ret == YANG_ITER_STOP) return ret; } @@ -255,9 +257,14 @@ int yang_snodes_iterate_all(yang_iterate_cb cb, uint16_t flags, void *arg) int ret = YANG_ITER_CONTINUE; RB_FOREACH (module, yang_modules, &yang_modules) { - ret = yang_snodes_iterate_module(module->info, cb, flags, arg); - if (ret == YANG_ITER_STOP) - return ret; + struct lys_node *snode; + + LY_TREE_FOR (module->info->data, snode) { + ret = yang_snodes_iterate_subtree(snode, NULL, cb, + flags, arg); + if (ret == YANG_ITER_STOP) + return ret; + } } return ret; diff --git a/lib/yang.h b/lib/yang.h index 94bbed233d..867ade9676 100644 --- a/lib/yang.h +++ b/lib/yang.h @@ -102,9 +102,6 @@ enum yang_iter_flags { /* Filter implicitely created nodes. */ YANG_ITER_FILTER_IMPLICIT = (1<<3), - - /* Allow iteration over augmentations. */ - YANG_ITER_ALLOW_AUGMENTATIONS = (1<<4), }; /* Callback used by the yang_snodes_iterate_*() family of functions. */ @@ -168,6 +165,9 @@ extern void yang_module_embed(struct yang_module_embed *embed); * snode * YANG schema node to operate on. * + * module + * When set, iterate over all nodes of the specified module only. + * * cb * Function to call with each schema node. * @@ -181,6 +181,7 @@ extern void yang_module_embed(struct yang_module_embed *embed); * The return value of the last called callback. */ extern int yang_snodes_iterate_subtree(const struct lys_node *snode, + const struct lys_module *module, yang_iterate_cb cb, uint16_t flags, void *arg); diff --git a/lib/zclient.c b/lib/zclient.c index b842e7c31b..c5016d22e2 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -1424,7 +1424,7 @@ int zapi_pbr_rule_encode(uint8_t cmd, struct stream *s, struct pbr_rule *zrule) stream_putw(s, zrule->filter.fwmark); /* fwmark */ stream_putl(s, zrule->action.table); - stream_putl(s, zrule->ifindex); + stream_put(s, zrule->ifname, INTERFACE_NAMSIZ); /* Put length at the first point of the stream. */ stream_putw_at(s, 0, stream_get_endp(s)); @@ -1454,26 +1454,23 @@ stream_failure: } bool zapi_rule_notify_decode(struct stream *s, uint32_t *seqno, - uint32_t *priority, uint32_t *unique, - ifindex_t *ifindex, + uint32_t *priority, uint32_t *unique, char *ifname, enum zapi_rule_notify_owner *note) { uint32_t prio, seq, uni; - ifindex_t ifi; STREAM_GET(note, s, sizeof(*note)); STREAM_GETL(s, seq); STREAM_GETL(s, prio); STREAM_GETL(s, uni); - STREAM_GETL(s, ifi); + STREAM_GET(ifname, s, INTERFACE_NAMSIZ); if (zclient_debug) - zlog_debug("%s: %u %u %u %u", __func__, seq, prio, uni, ifi); + zlog_debug("%s: %u %u %u %s", __func__, seq, prio, uni, ifname); *seqno = seq; *priority = prio; *unique = uni; - *ifindex = ifi; return true; diff --git a/lib/zclient.h b/lib/zclient.h index c6a67790a1..f99b3ad743 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -844,8 +844,7 @@ bool zapi_route_notify_decode(struct stream *s, struct prefix *p, uint32_t *tableid, enum zapi_route_notify_owner *note); bool zapi_rule_notify_decode(struct stream *s, uint32_t *seqno, - uint32_t *priority, uint32_t *unique, - ifindex_t *ifindex, + uint32_t *priority, uint32_t *unique, char *ifname, enum zapi_rule_notify_owner *note); bool zapi_ipset_notify_decode(struct stream *s, uint32_t *unique, @@ -955,6 +954,14 @@ enum zapi_opaque_registry { LINK_STATE_REQUEST = 1, /* Update containing link-state db info */ LINK_STATE_UPDATE = 2, + /* Request LDP-SYNC state from LDP */ + LDP_IGP_SYNC_IF_STATE_REQUEST = 3, + /* Update containing LDP IGP Sync State info */ + LDP_IGP_SYNC_IF_STATE_UPDATE = 4, + /* Announce that LDP is up */ + LDP_IGP_SYNC_ANNOUNCE_UPDATE = 5, + /* Heartbeat indicating that LDP is running */ + LDP_IGP_SYNC_HELLO_UPDATE = 6, }; /* Send the hello message. diff --git a/lib/zlog_targets.c b/lib/zlog_targets.c index b23ab073b4..8f4c2a46a8 100644 --- a/lib/zlog_targets.c +++ b/lib/zlog_targets.c @@ -225,10 +225,11 @@ static bool zlog_file_cycle(struct zlog_cfg_file *zcf) zlt->zt.logfn_sigsafe = zlog_fd_sigsafe; } while (0); - old = zlog_target_replace(&zcf->active->zt, &zlt->zt); + old = zlog_target_replace(zcf->active ? &zcf->active->zt : NULL, + zlt ? &zlt->zt : NULL); zcf->active = zlt; - zlog_file_target_free(container_of(old, struct zlt_fd, zt)); + zlog_file_target_free(container_of_null(old, struct zlt_fd, zt)); return rv; } diff --git a/ospfd/ospf_abr.c b/ospfd/ospf_abr.c index f6c0504999..5a9adcba0d 100644 --- a/ospfd/ospf_abr.c +++ b/ospfd/ospf_abr.c @@ -642,6 +642,15 @@ static int ospf_abr_translate_nssa(struct ospf_area *area, struct ospf_lsa *lsa) old = ospf_external_info_find_lsa(area->ospf, &p); if (old) { + /* Do not continue if type 5 LSA not approved */ + if (!CHECK_FLAG(old->flags, OSPF_LSA_APPROVED)) { + if (IS_DEBUG_OSPF_NSSA) + zlog_debug( + "ospf_abr_translate_nssa(): LSA Id %s type 5 is not approved", + inet_ntoa(old->data->id)); + return 1; + } + if (IS_DEBUG_OSPF_NSSA) zlog_debug( "ospf_abr_translate_nssa(): found old translated LSA Id %s, refreshing", diff --git a/ospfd/ospf_dump.c b/ospfd/ospf_dump.c index dcc479def6..e8798e023e 100644 --- a/ospfd/ospf_dump.c +++ b/ospfd/ospf_dump.c @@ -54,6 +54,7 @@ unsigned long conf_debug_ospf_te = 0; unsigned long conf_debug_ospf_ext = 0; unsigned long conf_debug_ospf_sr = 0; unsigned long conf_debug_ospf_defaultinfo = 0; +unsigned long conf_debug_ospf_ldp_sync = 0; /* Enable debug option variables -- valid only session. */ unsigned long term_debug_ospf_packet[5] = {0, 0, 0, 0, 0}; @@ -67,6 +68,7 @@ unsigned long term_debug_ospf_te = 0; unsigned long term_debug_ospf_ext = 0; unsigned long term_debug_ospf_sr = 0; unsigned long term_debug_ospf_defaultinfo; +unsigned long term_debug_ospf_ldp_sync; const char *ospf_redist_string(unsigned int route_type) { @@ -1476,6 +1478,32 @@ DEFUN (no_debug_ospf_default_info, return CMD_SUCCESS; } +DEFUN(debug_ospf_ldp_sync, + debug_ospf_ldp_sync_cmd, + "debug ospf ldp-sync", + DEBUG_STR OSPF_STR + "OSPF LDP-Sync information\n") +{ + if (vty->node == CONFIG_NODE) + CONF_DEBUG_ON(ldp_sync, LDP_SYNC); + TERM_DEBUG_ON(ldp_sync, LDP_SYNC); + return CMD_SUCCESS; +} + +DEFUN(no_debug_ospf_ldp_sync, + no_debug_ospf_ldp_sync_cmd, + "no debug ospf ldp-sync", + NO_STR + DEBUG_STR + OSPF_STR + "OSPF LDP-Sync information\n") +{ + if (vty->node == CONFIG_NODE) + CONF_DEBUG_OFF(ldp_sync, LDP_SYNC); + TERM_DEBUG_OFF(ldp_sync, LDP_SYNC); + return CMD_SUCCESS; +} + DEFUN (no_debug_ospf, no_debug_ospf_cmd, "no debug ospf", @@ -1505,6 +1533,7 @@ DEFUN (no_debug_ospf, DEBUG_OFF(zebra, ZEBRA_INTERFACE); DEBUG_OFF(zebra, ZEBRA_REDISTRIBUTE); DEBUG_OFF(defaultinfo, DEFAULTINFO); + DEBUG_OFF(ldp_sync, LDP_SYNC); for (i = 0; i < 5; i++) DEBUG_PACKET_OFF(i, flag); @@ -1532,6 +1561,7 @@ DEFUN (no_debug_ospf, TERM_DEBUG_OFF(zebra, ZEBRA_INTERFACE); TERM_DEBUG_OFF(zebra, ZEBRA_REDISTRIBUTE); TERM_DEBUG_OFF(defaultinfo, DEFAULTINFO); + TERM_DEBUG_OFF(ldp_sync, LDP_SYNC); return CMD_SUCCESS; } @@ -1633,6 +1663,10 @@ static int show_debugging_ospf_common(struct vty *vty, struct ospf *ospf) if (IS_DEBUG_OSPF(nssa, NSSA) == OSPF_DEBUG_NSSA) vty_out(vty, " OSPF NSSA debugging is on\n"); + /* Show debug status for LDP-SYNC. */ + if (IS_DEBUG_OSPF(ldp_sync, LDP_SYNC) == OSPF_DEBUG_LDP_SYNC) + vty_out(vty, " OSPF ldp-sync debugging is on\n"); + vty_out(vty, "\n"); return CMD_SUCCESS; @@ -1814,6 +1848,11 @@ static int config_write_debug(struct vty *vty) write = 1; } + /* debug ospf ldp-sync */ + if (IS_CONF_DEBUG_OSPF(ldp_sync, LDP_SYNC) == OSPF_DEBUG_LDP_SYNC) { + vty_out(vty, "debug ospf%s ldp-sync\n", str); + write = 1; + } return write; } @@ -1832,6 +1871,7 @@ void ospf_debug_init(void) install_element(ENABLE_NODE, &debug_ospf_te_cmd); install_element(ENABLE_NODE, &debug_ospf_sr_cmd); install_element(ENABLE_NODE, &debug_ospf_default_info_cmd); + install_element(ENABLE_NODE, &debug_ospf_ldp_sync_cmd); install_element(ENABLE_NODE, &no_debug_ospf_ism_cmd); install_element(ENABLE_NODE, &no_debug_ospf_nsm_cmd); install_element(ENABLE_NODE, &no_debug_ospf_lsa_cmd); @@ -1841,6 +1881,7 @@ void ospf_debug_init(void) install_element(ENABLE_NODE, &no_debug_ospf_te_cmd); install_element(ENABLE_NODE, &no_debug_ospf_sr_cmd); install_element(ENABLE_NODE, &no_debug_ospf_default_info_cmd); + install_element(ENABLE_NODE, &no_debug_ospf_ldp_sync_cmd); install_element(ENABLE_NODE, &show_debugging_ospf_instance_cmd); install_element(ENABLE_NODE, &debug_ospf_packet_cmd); @@ -1871,6 +1912,7 @@ void ospf_debug_init(void) install_element(CONFIG_NODE, &debug_ospf_te_cmd); install_element(CONFIG_NODE, &debug_ospf_sr_cmd); install_element(CONFIG_NODE, &debug_ospf_default_info_cmd); + install_element(CONFIG_NODE, &debug_ospf_ldp_sync_cmd); install_element(CONFIG_NODE, &no_debug_ospf_nsm_cmd); install_element(CONFIG_NODE, &no_debug_ospf_lsa_cmd); install_element(CONFIG_NODE, &no_debug_ospf_zebra_cmd); @@ -1879,6 +1921,7 @@ void ospf_debug_init(void) install_element(CONFIG_NODE, &no_debug_ospf_te_cmd); install_element(CONFIG_NODE, &no_debug_ospf_sr_cmd); install_element(CONFIG_NODE, &no_debug_ospf_default_info_cmd); + install_element(CONFIG_NODE, &no_debug_ospf_ldp_sync_cmd); install_element(CONFIG_NODE, &debug_ospf_instance_nsm_cmd); install_element(CONFIG_NODE, &debug_ospf_instance_lsa_cmd); diff --git a/ospfd/ospf_dump.h b/ospfd/ospf_dump.h index 8c01977ff8..faae27e2cf 100644 --- a/ospfd/ospf_dump.h +++ b/ospfd/ospf_dump.h @@ -60,6 +60,7 @@ #define OSPF_DEBUG_EXT 0x08 #define OSPF_DEBUG_SR 0x10 #define OSPF_DEBUG_DEFAULTINFO 0x20 +#define OSPF_DEBUG_LDP_SYNC 0x40 /* Macro for setting debug option. */ #define CONF_DEBUG_PACKET_ON(a, b) conf_debug_ospf_packet[a] |= (b) @@ -107,6 +108,8 @@ #define IS_DEBUG_OSPF_DEFAULT_INFO IS_DEBUG_OSPF(defaultinfo, DEFAULTINFO) +#define IS_DEBUG_OSPF_LDP_SYNC IS_DEBUG_OSPF(ldp_sync, LDP_SYNC) + #define IS_CONF_DEBUG_OSPF_PACKET(a, b) \ (conf_debug_ospf_packet[a] & OSPF_DEBUG_##b) #define IS_CONF_DEBUG_OSPF(a, b) (conf_debug_ospf_##a & OSPF_DEBUG_##b) @@ -126,6 +129,7 @@ extern unsigned long term_debug_ospf_te; extern unsigned long term_debug_ospf_ext; extern unsigned long term_debug_ospf_sr; extern unsigned long term_debug_ospf_defaultinfo; +extern unsigned long term_debug_ospf_ldp_sync; /* Message Strings. */ extern char *ospf_lsa_type_str[]; diff --git a/ospfd/ospf_interface.c b/ospfd/ospf_interface.c index af801da8d7..adc3037598 100644 --- a/ospfd/ospf_interface.c +++ b/ospfd/ospf_interface.c @@ -32,6 +32,7 @@ #include "log.h" #include "zclient.h" #include "bfd.h" +#include "ldp_sync.h" #include "ospfd/ospfd.h" #include "ospfd/ospf_spf.h" @@ -46,6 +47,7 @@ #include "ospfd/ospf_abr.h" #include "ospfd/ospf_network.h" #include "ospfd/ospf_dump.h" +#include "ospfd/ospf_ldp_sync.h" DEFINE_QOBJ_TYPE(ospf_interface) DEFINE_HOOK(ospf_vl_add, (struct ospf_vl_data * vd), (vd)) @@ -81,6 +83,12 @@ int ospf_if_get_output_cost(struct ospf_interface *oi) uint32_t cost; uint32_t bw, refbw; + /* if LDP-IGP Sync is running on interface set cost so interface + * is used only as last resort + */ + if (ldp_sync_if_is_enabled(IF_DEF_PARAMS(oi->ifp)->ldp_sync_info)) + return (LDP_OSPF_LSINFINITY); + /* ifp speed and bw can be 0 in some platforms, use ospf default bw if bw is configured under interface it would be used. */ @@ -539,6 +547,7 @@ void ospf_del_if_params(struct ospf_if_params *oip) { list_delete(&oip->auth_crypt); bfd_info_free(&(oip->bfd_info)); + ldp_sync_info_free(&(oip->ldp_sync_info)); XFREE(MTYPE_OSPF_IF_PARAMS, oip); } diff --git a/ospfd/ospf_interface.h b/ospfd/ospf_interface.h index 4b3dbcc5c2..1d28eac6b3 100644 --- a/ospfd/ospf_interface.h +++ b/ospfd/ospf_interface.h @@ -104,6 +104,9 @@ struct ospf_if_params { /* BFD configuration */ struct bfd_info *bfd_info; + + /* MPLS LDP-IGP Sync configuration */ + struct ldp_sync_info *ldp_sync_info; }; enum { MEMBER_ALLROUTERS = 0, diff --git a/ospfd/ospf_ldp_sync.c b/ospfd/ospf_ldp_sync.c new file mode 100644 index 0000000000..a8c9df1c56 --- /dev/null +++ b/ospfd/ospf_ldp_sync.c @@ -0,0 +1,1142 @@ +/* + * ospf_ldp_sync.c: OSPF LDP-IGP Sync handling routines + * Copyright (C) 2020 Volta Networks, Inc. + * + * This program 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 of the License, or (at your option) + * any later version. + * + * This program 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 <string.h> + +#include "monotime.h" +#include "memory.h" +#include "thread.h" +#include "prefix.h" +#include "table.h" +#include "vty.h" +#include "command.h" +#include "plist.h" +#include "log.h" +#include "zclient.h" +#include <lib/json.h> +#include "defaults.h" +#include "ldp_sync.h" + +#include "ospfd.h" +#include "ospf_interface.h" +#include "ospf_vty.h" +#include "ospf_ldp_sync.h" +#include "ospf_dump.h" +#include "ospf_ism.h" + +extern struct zclient *zclient; + +/* + * LDP-SYNC msg between IGP and LDP + */ +int ospf_ldp_sync_state_update(struct ldp_igp_sync_if_state state) +{ + struct ospf *ospf; + struct interface *ifp; + + /* if ospf is not enabled or LDP-SYNC is not configured ignore */ + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if (ospf == NULL || + !CHECK_FLAG(ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE)) + return 0; + + /* received ldp-sync interface state from LDP */ + ifp = if_lookup_by_index(state.ifindex, VRF_DEFAULT); + if (ifp == NULL || if_is_loopback(ifp)) + return 0; + + ols_debug("ldp_sync: rcvd %s from LDP if %s", + state.sync_start ? "sync-start" : "sync-complete", + ifp->name); + if (state.sync_start) + ospf_ldp_sync_if_start(ifp, false); + else + ospf_ldp_sync_if_complete(ifp); + + return 0; +} + +int ospf_ldp_sync_announce_update(struct ldp_igp_sync_announce announce) +{ + struct ospf *ospf; + struct vrf *vrf; + struct interface *ifp; + + /* if ospf is not enabled or LDP-SYNC is not configured ignore */ + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if (ospf == NULL || + !CHECK_FLAG(ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE)) + return 0; + + if (announce.proto != ZEBRA_ROUTE_LDP) + return 0; + + ols_debug("ldp_sync: rcvd announce from LDP"); + + /* LDP just started up: + * set cost to LSInfinity + * send request to LDP for LDP-SYNC state for each interface + * start hello timer + */ + vrf = vrf_lookup_by_id(ospf->vrf_id); + FOR_ALL_INTERFACES (vrf, ifp) + ospf_ldp_sync_if_start(ifp, true); + + THREAD_TIMER_OFF(ospf->ldp_sync_cmd.t_hello); + ospf->ldp_sync_cmd.t_hello = NULL; + ospf->ldp_sync_cmd.sequence = 0; + ospf_ldp_sync_hello_timer_add(ospf); + + return 0; +} + +int ospf_ldp_sync_hello_update(struct ldp_igp_sync_hello hello) +{ + struct ospf *ospf; + struct vrf *vrf; + struct interface *ifp; + + /* if ospf is not enabled or LDP-SYNC is not configured ignore */ + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if (ospf == NULL || + !CHECK_FLAG(ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE)) + return 0; + + if (hello.proto != ZEBRA_ROUTE_LDP) + return 0; + + /* Received Hello from LDP: + * if current sequence number is greater than received hello + * sequence number then assume LDP restarted + * set cost to LSInfinity + * send request to LDP for LDP-SYNC state for each interface + * else all is fine just restart hello timer + */ + if (hello.sequence == 0) + /* rolled over */ + ospf->ldp_sync_cmd.sequence = 0; + + if (ospf->ldp_sync_cmd.sequence > hello.sequence) { + zlog_err("ldp_sync: LDP restarted"); + + vrf = vrf_lookup_by_id(ospf->vrf_id); + FOR_ALL_INTERFACES (vrf, ifp) + ospf_ldp_sync_if_start(ifp, true); + } else { + THREAD_TIMER_OFF(ospf->ldp_sync_cmd.t_hello); + ospf_ldp_sync_hello_timer_add(ospf); + } + ospf->ldp_sync_cmd.sequence = hello.sequence; + + return 0; +} + +void ospf_ldp_sync_state_req_msg(struct interface *ifp) +{ + struct ldp_igp_sync_if_state_req request; + + ols_debug("ldp_sync: send state request to LDP for %s", ifp->name); + + strlcpy(request.name, ifp->name, sizeof(ifp->name)); + request.proto = LDP_IGP_SYNC_IF_STATE_REQUEST; + request.ifindex = ifp->ifindex; + + zclient_send_opaque(zclient, LDP_IGP_SYNC_IF_STATE_REQUEST, + (uint8_t *)&request, sizeof(request)); +} + +/* + * LDP-SYNC general interface routines + */ +void ospf_ldp_sync_if_init(struct ospf_interface *oi) +{ + struct ospf_if_params *params; + struct ldp_sync_info *ldp_sync_info; + struct interface *ifp = oi->ifp; + + /* called when OSPF is configured on an interface: + * if LDP-IGP Sync is configured globally set state + * if ptop interface inform LDP LDP-SYNC is enabled + */ + if (if_is_loopback(ifp) || (ifp->vrf_id != VRF_DEFAULT) || + !(CHECK_FLAG(oi->ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE))) + return; + + ols_debug("ldp_sync: init if %s",ifp->name); + params = IF_DEF_PARAMS(ifp); + if (params->ldp_sync_info == NULL) + params->ldp_sync_info = ldp_sync_info_create(); + + ldp_sync_info = params->ldp_sync_info; + + /* specifed on interface overrides global config. */ + if (!CHECK_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_HOLDDOWN)) + ldp_sync_info->holddown = oi->ospf->ldp_sync_cmd.holddown; + + if (!CHECK_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_IF_CONFIG)) + ldp_sync_info->enabled = LDP_IGP_SYNC_ENABLED; + + if ((params->type == OSPF_IFTYPE_POINTOPOINT || + if_is_pointopoint(ifp)) && + ldp_sync_info->enabled == LDP_IGP_SYNC_ENABLED) + ldp_sync_info->state = LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP; +} + +void ospf_ldp_sync_if_start(struct interface *ifp, bool send_state_req) +{ + struct ospf_if_params *params; + struct ldp_sync_info *ldp_sync_info; + + if (if_is_loopback(ifp)) + return; + + params = IF_DEF_PARAMS(ifp); + ldp_sync_info = params->ldp_sync_info; + + /* Start LDP-SYNC on this interface: + * set cost of interface to LSInfinity so traffic will use different + * interface until LDP has learned all labels from peer + * start holddown timer if configured + * send msg to LDP to get LDP-SYNC state + */ + if (ldp_sync_info && + ldp_sync_info->enabled == LDP_IGP_SYNC_ENABLED && + ldp_sync_info->state != LDP_IGP_SYNC_STATE_NOT_REQUIRED) { + ols_debug("ldp_sync: start on if %s state: %s", + ifp->name, "Holding down until Sync"); + ldp_sync_info->state = LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP; + ospf_if_recalculate_output_cost(ifp); + ospf_ldp_sync_holddown_timer_add(ifp); + + if (send_state_req) + ospf_ldp_sync_state_req_msg(ifp); + } +} + +void ospf_ldp_sync_if_complete(struct interface *ifp) +{ + struct ospf_if_params *params; + struct ldp_sync_info *ldp_sync_info; + + if (if_is_loopback(ifp)) + return; + + params = IF_DEF_PARAMS(ifp); + ldp_sync_info = params->ldp_sync_info; + + /* received sync-complete from LDP: + * set state to up + * stop timer + * restore interface cost to original value + */ + if (ldp_sync_info && ldp_sync_info->enabled == LDP_IGP_SYNC_ENABLED) { + if (ldp_sync_info->state == LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP) + ldp_sync_info->state = LDP_IGP_SYNC_STATE_REQUIRED_UP; + THREAD_TIMER_OFF(ldp_sync_info->t_holddown); + ldp_sync_info->t_holddown = NULL; + ospf_if_recalculate_output_cost(ifp); + } +} + +void ospf_ldp_sync_ldp_fail(struct interface *ifp) +{ + struct ospf_if_params *params; + struct ldp_sync_info *ldp_sync_info; + + if (if_is_loopback(ifp)) + return; + + params = IF_DEF_PARAMS(ifp); + ldp_sync_info = params->ldp_sync_info; + + /* LDP failed to send hello: + * stop holddown timer + * set cost of interface to LSInfinity so traffic will use different + * interface until LDP has learned all labels from peer + */ + if (ldp_sync_info && + ldp_sync_info->enabled == LDP_IGP_SYNC_ENABLED && + ldp_sync_info->state != LDP_IGP_SYNC_STATE_NOT_REQUIRED) { + if (ldp_sync_info->t_holddown != NULL) { + THREAD_TIMER_OFF(ldp_sync_info->t_holddown); + ldp_sync_info->t_holddown = NULL; + } + ldp_sync_info->state = LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP; + ospf_if_recalculate_output_cost(ifp); + } +} + +void ospf_ldp_sync_if_down(struct interface *ifp) +{ + struct ospf_if_params *params; + struct ldp_sync_info *ldp_sync_info; + + if (if_is_loopback(ifp)) + return; + + params = IF_DEF_PARAMS(ifp); + ldp_sync_info = params->ldp_sync_info; + + if (ldp_sync_if_down(ldp_sync_info) == false) + return; + + ols_debug("ldp_sync: down on if %s", ifp->name); + + /* Interface down: + * can occur from a link down or changing config + * ospf network type change interface is brought down/up + */ + switch (ldp_sync_info->state) { + case LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP: + case LDP_IGP_SYNC_STATE_REQUIRED_UP: + if (params->type != OSPF_IFTYPE_POINTOPOINT && + !if_is_pointopoint(ifp)) + /* LDP-SYNC not able to run on non-ptop interface */ + ldp_sync_info->state = LDP_IGP_SYNC_STATE_NOT_REQUIRED; + break; + case LDP_IGP_SYNC_STATE_NOT_REQUIRED: + if (params->type == OSPF_IFTYPE_POINTOPOINT || + if_is_pointopoint(ifp)) + /* LDP-SYNC is able to run on ptop interface */ + ldp_sync_info->state = + LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP; + break; + default: + break; + } +} + +void ospf_ldp_sync_if_remove(struct interface *ifp, bool remove) +{ + struct ospf_if_params *params; + struct ldp_sync_info *ldp_sync_info; + + params = IF_DEF_PARAMS(ifp); + if (params->ldp_sync_info == NULL) + return; + + ldp_sync_info = params->ldp_sync_info; + + /* Stop LDP-SYNC on this interface: + * if holddown timer is running stop it + * delete ldp instance on interface + * restore cost + */ + ols_debug("ldp_sync: Removed from if %s", ifp->name); + if (ldp_sync_info->t_holddown) + THREAD_TIMER_OFF(ldp_sync_info->t_holddown); + ldp_sync_info->state = LDP_IGP_SYNC_STATE_NOT_REQUIRED; + ospf_if_recalculate_output_cost(ifp); + if (!CHECK_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_IF_CONFIG)) + ldp_sync_info->enabled = LDP_IGP_SYNC_DEFAULT; + if (remove) { + ldp_sync_info_free((struct ldp_sync_info **)&(ldp_sync_info)); + params->ldp_sync_info = NULL; + } +} + +static int ospf_ldp_sync_ism_change(struct ospf_interface *oi, int state, + int old_state) +{ + /* Terminal state or regression */ + switch (state) { + case ISM_PointToPoint: + /* If LDP-SYNC is configure on interface then start */ + ospf_ldp_sync_if_start(oi->ifp, true); + break; + case ISM_Down: + /* If LDP-SYNC is configure on this interface then stop it */ + ospf_ldp_sync_if_down(oi->ifp); + break; + default: + break; + } + return 0; +} + +/* + * LDP-SYNC holddown timer routines + */ +static int ospf_ldp_sync_holddown_timer(struct thread *thread) +{ + struct interface *ifp; + struct ospf_if_params *params; + struct ldp_sync_info *ldp_sync_info; + + /* holddown timer expired: + * didn't receive msg from LDP indicating sync-complete + * restore interface cost to original value + */ + ifp = THREAD_ARG(thread); + params = IF_DEF_PARAMS(ifp); + if (params->ldp_sync_info) { + ldp_sync_info = params->ldp_sync_info; + + ldp_sync_info->state = LDP_IGP_SYNC_STATE_REQUIRED_UP; + ldp_sync_info->t_holddown = NULL; + + ols_debug("ldp_sync: holddown timer expired for %s state: %s", + ifp->name, "Sync achieved"); + + ospf_if_recalculate_output_cost(ifp); + } + return 0; +} + +void ospf_ldp_sync_holddown_timer_add(struct interface *ifp) +{ + struct ospf_if_params *params; + struct ldp_sync_info *ldp_sync_info; + + params = IF_DEF_PARAMS(ifp); + ldp_sync_info = params->ldp_sync_info; + + /* Start holddown timer: + * this timer is used to keep interface cost at LSInfinity + * once expires returns cost to original value + * if timer is already running or holddown time is off just return + */ + if (ldp_sync_info->t_holddown || + ldp_sync_info->holddown == LDP_IGP_SYNC_HOLDDOWN_DEFAULT) + return; + + ols_debug("ldp_sync: start holddown timer for %s time %d", + ifp->name, ldp_sync_info->holddown); + + thread_add_timer(master, ospf_ldp_sync_holddown_timer, + ifp, ldp_sync_info->holddown, + &ldp_sync_info->t_holddown); +} + +/* + * LDP-SYNC hello timer routines + */ +static int ospf_ldp_sync_hello_timer(struct thread *thread) +{ + struct ospf *ospf; + struct vrf *vrf; + struct interface *ifp; + + /* hello timer expired: + * didn't receive hello msg from LDP + * set cost of all interfaces to LSInfinity + */ + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if (ospf) { + ospf->ldp_sync_cmd.t_hello = NULL; + vrf = vrf_lookup_by_id(ospf->vrf_id); + + FOR_ALL_INTERFACES (vrf, ifp) + ospf_ldp_sync_ldp_fail(ifp); + + zlog_err("ldp_sync: hello timer expired, LDP down"); + } + return 0; +} + +void ospf_ldp_sync_hello_timer_add(struct ospf *ospf) +{ + + /* Start hello timer: + * this timer is used to make sure LDP is up + * if expires set interface cost to LSInfinity + */ + if (!CHECK_FLAG(ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE)) + return; + + thread_add_timer(master, ospf_ldp_sync_hello_timer, + NULL, LDP_IGP_SYNC_HELLO_TIMEOUT, + &ospf->ldp_sync_cmd.t_hello); +} + +/* + * LDP-SYNC exit routes. + */ +void ospf_ldp_sync_gbl_exit(struct ospf *ospf, bool remove) +{ + struct interface *ifp; + struct vrf *vrf; + + /* ospf is being removed + * stop hello timer + * stop any holddown timers + */ + if (CHECK_FLAG(ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE)) { + /* unregister with opaque client to recv LDP-IGP Sync msgs */ + zclient_unregister_opaque(zclient, + LDP_IGP_SYNC_IF_STATE_UPDATE); + zclient_unregister_opaque(zclient, + LDP_IGP_SYNC_ANNOUNCE_UPDATE); + zclient_unregister_opaque(zclient, LDP_IGP_SYNC_HELLO_UPDATE); + + /* disable LDP globally */ + UNSET_FLAG(ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE); + UNSET_FLAG(ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_HOLDDOWN); + ospf->ldp_sync_cmd.holddown = LDP_IGP_SYNC_HOLDDOWN_DEFAULT; + THREAD_TIMER_OFF(ospf->ldp_sync_cmd.t_hello); + ospf->ldp_sync_cmd.t_hello = NULL; + + /* turn off LDP-IGP Sync on all OSPF interfaces */ + vrf = vrf_lookup_by_id(ospf->vrf_id); + FOR_ALL_INTERFACES (vrf, ifp) + ospf_ldp_sync_if_remove(ifp, remove); + } +} + +/* + * LDP-SYNC routes used by set commands. + */ +void ospf_if_set_ldp_sync_enable(struct ospf *ospf, struct interface *ifp) +{ + struct ospf_if_params *params; + struct ldp_sync_info *ldp_sync_info; + + /* called when setting LDP-SYNC at the global level: + * specifed on interface overrides global config + * if ptop link send msg to LDP indicating ldp-sync enabled + */ + if (if_is_loopback(ifp)) + return; + + params = IF_DEF_PARAMS(ifp); + if (params->ldp_sync_info == NULL) + params->ldp_sync_info = ldp_sync_info_create(); + ldp_sync_info = params->ldp_sync_info; + + /* config on interface, overrides global config. */ + if (CHECK_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_IF_CONFIG)) + if (ldp_sync_info->enabled != LDP_IGP_SYNC_ENABLED) + return; + + ldp_sync_info->enabled = LDP_IGP_SYNC_ENABLED; + + ols_debug("ldp_sync: enable if %s", ifp->name); + + /* send message to LDP if ptop link */ + if (params->type == OSPF_IFTYPE_POINTOPOINT || + if_is_pointopoint(ifp)) { + ldp_sync_info->state = LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP; + ospf_ldp_sync_state_req_msg(ifp); + } else { + ldp_sync_info->state = LDP_IGP_SYNC_STATE_NOT_REQUIRED; + zlog_debug("ldp_sync: Sync only runs on P2P links %s", + ifp->name); + } +} + +void ospf_if_set_ldp_sync_holddown(struct ospf *ospf, struct interface *ifp) +{ + struct ospf_if_params *params; + struct ldp_sync_info *ldp_sync_info; + + /* called when setting LDP-SYNC at the global level: + * specifed on interface overrides global config. + */ + if (if_is_loopback(ifp)) + return; + + params = IF_DEF_PARAMS(ifp); + if (params->ldp_sync_info == NULL) + params->ldp_sync_info = ldp_sync_info_create(); + ldp_sync_info = params->ldp_sync_info; + + /* config on interface, overrides global config. */ + if (CHECK_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_HOLDDOWN)) + return; + if (CHECK_FLAG(ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_HOLDDOWN)) + ldp_sync_info->holddown = ospf->ldp_sync_cmd.holddown; + else + ldp_sync_info->holddown = LDP_IGP_SYNC_HOLDDOWN_DEFAULT; +} + +/* + * LDP-SYNC routines used by show commands. + */ + +void ospf_ldp_sync_show_info(struct vty *vty, struct ospf *ospf, + json_object *json_vrf, bool use_json) +{ + + if (CHECK_FLAG(ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE)) { + if (use_json) { + json_object_boolean_true_add(json_vrf, + "MplsLdpIgpSyncEnabled"); + json_object_int_add(json_vrf, "MplsLdpIgpSyncHolddown", + ospf->ldp_sync_cmd.holddown); + } else { + vty_out(vty, " MPLS LDP-IGP Sync is enabled\n"); + if (ospf->ldp_sync_cmd.holddown == 0) + vty_out(vty, + " MPLS LDP-IGP Sync holddown timer is disabled\n"); + else + vty_out(vty, + " MPLS LDP-IGP Sync holddown timer %d sec\n", + ospf->ldp_sync_cmd.holddown); + } + } +} + +static void show_ip_ospf_mpls_ldp_interface_sub(struct vty *vty, + struct ospf_interface *oi, + struct interface *ifp, + json_object *json_interface_sub, + bool use_json) +{ + const char *ldp_state; + struct ospf_if_params *params; + char timebuf[OSPF_TIME_DUMP_SIZE]; + struct ldp_sync_info *ldp_sync_info; + + params = IF_DEF_PARAMS(oi->ifp); + if (params->ldp_sync_info == NULL) + return; + + ldp_sync_info = params->ldp_sync_info; + if (use_json) { + if (ldp_sync_info->enabled == LDP_IGP_SYNC_ENABLED) + json_object_boolean_true_add(json_interface_sub, + "ldpIgpSyncEnabled"); + else + json_object_boolean_false_add(json_interface_sub, + "ldpIgpSyncEnabled"); + + json_object_int_add(json_interface_sub, "holdDownTimeInSec", + ldp_sync_info->holddown); + + } else { + vty_out(vty, "%-10s\n", ifp->name); + vty_out(vty, " LDP-IGP Synchronization enabled: %s\n", + ldp_sync_info->enabled == LDP_IGP_SYNC_ENABLED + ? "yes" + : "no"); + vty_out(vty, " Holddown timer in seconds: %u\n", + ldp_sync_info->holddown); + } + + switch (ldp_sync_info->state) { + case LDP_IGP_SYNC_STATE_REQUIRED_UP: + if (use_json) + json_object_string_add(json_interface_sub, + "ldpIgpSyncState", + "Sync achieved"); + else + vty_out(vty, " State: Sync achieved\n"); + break; + case LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP: + if (ldp_sync_info->t_holddown != NULL) { + if (use_json) { + long time_store; + + time_store = monotime_until( + &ldp_sync_info->t_holddown->u.sands, + NULL) + /1000LL; + + json_object_int_add(json_interface_sub, + "ldpIgpSyncTimeRemainInMsec", + time_store); + + json_object_string_add(json_interface_sub, + "ldpIgpSyncState", + "Holding down until Sync"); + } else { + vty_out(vty, + " Holddown timer is running %s remaining\n", + ospf_timer_dump( + ldp_sync_info->t_holddown, + timebuf, + sizeof(timebuf))); + + vty_out(vty, + " State: Holding down until Sync\n"); + } + } else { + if (use_json) + json_object_string_add(json_interface_sub, + "ldpIgpSyncState", + "Sync not achieved"); + else + vty_out(vty, " State: Sync not achieved\n"); + } + break; + case LDP_IGP_SYNC_STATE_NOT_REQUIRED: + default: + if (IF_DEF_PARAMS(ifp)->type != OSPF_IFTYPE_POINTOPOINT && + !if_is_pointopoint(ifp)) + ldp_state = "Sync not required: non-p2p link"; + else + ldp_state = "Sync not required"; + + if (use_json) + json_object_string_add(json_interface_sub, + "ldpIgpSyncState", + ldp_state); + else + vty_out(vty, " State: %s\n", ldp_state); + break; + } +} + +static int show_ip_ospf_mpls_ldp_interface_common(struct vty *vty, + struct ospf *ospf, + char *intf_name, + json_object *json, + bool use_json) +{ + struct interface *ifp; + struct vrf *vrf = vrf_lookup_by_id(ospf->vrf_id); + json_object *json_interface_sub = NULL; + + if (intf_name == NULL) { + /* Show All Interfaces.*/ + FOR_ALL_INTERFACES (vrf, ifp) { + struct route_node *rn; + struct ospf_interface *oi; + + if (ospf_oi_count(ifp) == 0) + continue; + for (rn = route_top(IF_OIFS(ifp)); rn; + rn = route_next(rn)) { + oi = rn->info; + + if (use_json) { + json_interface_sub = + json_object_new_object(); + } + show_ip_ospf_mpls_ldp_interface_sub( + vty, oi, ifp, json_interface_sub, + use_json); + + if (use_json) { + json_object_object_add( + json, ifp->name, + json_interface_sub); + } + } + } + } else { + /* Interface name is specified. */ + ifp = if_lookup_by_name(intf_name, ospf->vrf_id); + if (ifp != NULL) { + struct route_node *rn; + struct ospf_interface *oi; + + if (ospf_oi_count(ifp) == 0 && !use_json) { + vty_out(vty, + " OSPF not enabled on this interface %s\n", + ifp->name); + return CMD_SUCCESS; + } + for (rn = route_top(IF_OIFS(ifp)); rn; + rn = route_next(rn)) { + oi = rn->info; + + if (use_json) + json_interface_sub = + json_object_new_object(); + + show_ip_ospf_mpls_ldp_interface_sub( + vty, oi, ifp, json_interface_sub, + use_json); + + if (use_json) { + json_object_object_add( + json, ifp->name, + json_interface_sub); + } + } + } + } + return CMD_SUCCESS; +} + +/* + * Write the global LDP-SYNC configuration. + */ +void ospf_ldp_sync_write_config(struct vty *vty, struct ospf *ospf) +{ + if (CHECK_FLAG(ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE)) + vty_out(vty, " mpls ldp-sync\n"); + if (CHECK_FLAG(ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_HOLDDOWN)) + vty_out(vty, " mpls ldp-sync holddown %u\n", + ospf->ldp_sync_cmd.holddown); +} + +/* + * Write the interface LDP-SYNC configuration. + */ +void ospf_ldp_sync_if_write_config(struct vty *vty, + struct ospf_if_params *params) + +{ + struct ldp_sync_info *ldp_sync_info; + + ldp_sync_info = params->ldp_sync_info; + if (ldp_sync_info == NULL) + return; + + if (CHECK_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_IF_CONFIG)) { + if (ldp_sync_info->enabled == LDP_IGP_SYNC_ENABLED) + vty_out(vty, " ip ospf mpls ldp-sync\n"); + else + vty_out(vty, " no ip ospf mpls ldp-sync\n"); + } + if (CHECK_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_HOLDDOWN)) + vty_out(vty, " ip ospf mpls ldp-sync holddown %u\n", + ldp_sync_info->holddown); +} + +/* + * LDP-SYNC commands. + */ +#ifndef VTYSH_EXTRACT_PL +#include "ospfd/ospf_ldp_sync_clippy.c" +#endif + +DEFPY (ospf_mpls_ldp_sync, + ospf_mpls_ldp_sync_cmd, + "mpls ldp-sync", + "MPLS specific commands\n" + "Enable MPLS LDP-IGP Sync\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + struct vrf *vrf = vrf_lookup_by_id(ospf->vrf_id); + struct interface *ifp; + + if (ospf->vrf_id != VRF_DEFAULT) { + vty_out(vty, "ldp-sync only runs on DEFAULT VRF\n"); + return CMD_ERR_NOTHING_TODO; + } + + /* register with opaque client to recv LDP-IGP Sync msgs */ + zclient_register_opaque(zclient, LDP_IGP_SYNC_IF_STATE_UPDATE); + zclient_register_opaque(zclient, LDP_IGP_SYNC_ANNOUNCE_UPDATE); + zclient_register_opaque(zclient, LDP_IGP_SYNC_HELLO_UPDATE); + + if (!CHECK_FLAG(ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE)) { + SET_FLAG(ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE); + /* turn on LDP-IGP Sync on all ptop OSPF interfaces */ + FOR_ALL_INTERFACES (vrf, ifp) + ospf_if_set_ldp_sync_enable(ospf, ifp); + } + return CMD_SUCCESS; +} + +DEFPY (no_ospf_mpls_ldp_sync, + no_ospf_mpls_ldp_sync_cmd, + "no mpls ldp-sync", + NO_STR + "MPLS specific commands\n" + "Disable MPLS LDP-IGP Sync\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + ospf_ldp_sync_gbl_exit(ospf, false); + return CMD_SUCCESS; +} + +DEFPY (ospf_mpls_ldp_sync_holddown, + ospf_mpls_ldp_sync_holddown_cmd, + "mpls ldp-sync holddown (1-10000)", + "MPLS specific commands\n" + "Enable MPLS LDP-IGP Sync\n" + "Set holddown timer\n" + "seconds\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + struct vrf *vrf = vrf_lookup_by_id(ospf->vrf_id); + struct interface *ifp; + + if (ospf->vrf_id != VRF_DEFAULT) { + vty_out(vty, "ldp-sync only runs on DEFAULT VRF\n"); + return CMD_ERR_NOTHING_TODO; + } + + SET_FLAG(ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_HOLDDOWN); + ospf->ldp_sync_cmd.holddown = holddown; + /* set holddown time on all OSPF interfaces */ + FOR_ALL_INTERFACES (vrf, ifp) + ospf_if_set_ldp_sync_holddown(ospf, ifp); + + return CMD_SUCCESS; +} + +DEFPY (no_ospf_mpls_ldp_sync_holddown, + no_ospf_mpls_ldp_sync_holddown_cmd, + "no mpls ldp-sync holddown [<(1-10000)>]", + NO_STR + "MPLS specific commands\n" + "Disable MPLS LDP-IGP Sync\n" + "holddown timer disable\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + struct vrf *vrf = vrf_lookup_by_id(ospf->vrf_id); + struct interface *ifp; + + if (CHECK_FLAG(ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_HOLDDOWN)) { + UNSET_FLAG(ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_HOLDDOWN); + ospf->ldp_sync_cmd.holddown = LDP_IGP_SYNC_HOLDDOWN_DEFAULT; + /* turn off holddown timer on all OSPF interfaces */ + FOR_ALL_INTERFACES (vrf, ifp) + ospf_if_set_ldp_sync_holddown(ospf, ifp); + } + return CMD_SUCCESS; +} + + +DEFPY (mpls_ldp_sync, + mpls_ldp_sync_cmd, + "ip ospf mpls ldp-sync", + IP_STR + "OSPF interface commands\n" + MPLS_STR + MPLS_LDP_SYNC_STR + MPLS_LDP_SYNC_STR) +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf_if_params *params; + struct ldp_sync_info *ldp_sync_info; + + if (if_is_loopback(ifp)) { + vty_out(vty, "ldp-sync does not run on loopback interface\n"); + return CMD_ERR_NOTHING_TODO; + } + + if (ifp->vrf_id != VRF_DEFAULT) { + vty_out(vty, "ldp-sync only runs on DEFAULT VRF\n"); + return CMD_ERR_NOTHING_TODO; + } + + params = IF_DEF_PARAMS(ifp); + if (params->ldp_sync_info == NULL) + params->ldp_sync_info = ldp_sync_info_create(); + + ldp_sync_info = params->ldp_sync_info; + + SET_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_IF_CONFIG); + ldp_sync_info->enabled = LDP_IGP_SYNC_ENABLED; + if (params->type == OSPF_IFTYPE_POINTOPOINT || if_is_pointopoint(ifp)) { + ldp_sync_info->state = LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP; + ospf_ldp_sync_state_req_msg(ifp); + } else { + zlog_debug("ldp_sync: only runs on P2P links %s", ifp->name); + ldp_sync_info->state = LDP_IGP_SYNC_STATE_NOT_REQUIRED; + } + return CMD_SUCCESS; +} + +DEFPY (no_mpls_ldp_sync, + no_mpls_ldp_sync_cmd, + "no ip ospf mpls ldp-sync", + NO_STR + IP_STR + "OSPF interface commands\n" + MPLS_STR + NO_MPLS_LDP_SYNC_STR) +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf_if_params *params; + struct ldp_sync_info *ldp_sync_info; + + if (if_is_loopback(ifp)) { + vty_out(vty, "ldp-sync: does not run on loopback interface\n"); + return CMD_ERR_NOTHING_TODO; + } + + if (ifp->vrf_id != VRF_DEFAULT) { + vty_out(vty, "ldp-sync only runs on DEFAULT VRF\n"); + return CMD_ERR_NOTHING_TODO; + } + + params = IF_DEF_PARAMS(ifp); + if (params->ldp_sync_info == NULL) + params->ldp_sync_info = ldp_sync_info_create(); + + ldp_sync_info = params->ldp_sync_info; + + /* disable LDP-SYNC on an interface + * stop holddown timer if running + * restore ospf cost + */ + SET_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_IF_CONFIG); + ldp_sync_info->enabled = LDP_IGP_SYNC_DEFAULT; + ldp_sync_info->state = LDP_IGP_SYNC_STATE_NOT_REQUIRED; + THREAD_TIMER_OFF(ldp_sync_info->t_holddown); + ldp_sync_info->t_holddown = NULL; + ospf_if_recalculate_output_cost(ifp); + + return CMD_SUCCESS; +} + +DEFPY (mpls_ldp_sync_holddown, + mpls_ldp_sync_holddown_cmd, + "ip ospf mpls ldp-sync holddown (0-10000)", + IP_STR + "OSPF interface commands\n" + MPLS_STR + MPLS_LDP_SYNC_STR + "Time to wait for LDP-SYNC to occur before restoring interface cost\n" + "Time in seconds\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf_if_params *params; + struct ldp_sync_info *ldp_sync_info; + + if (if_is_loopback(ifp)) { + vty_out(vty, "ldp-sync: does not run on loopback interface\n"); + return CMD_ERR_NOTHING_TODO; + } + + if (ifp->vrf_id != VRF_DEFAULT) { + vty_out(vty, "ldp-sync only runs on DEFAULT VRF\n"); + return CMD_ERR_NOTHING_TODO; + } + + params = IF_DEF_PARAMS(ifp); + if (params->ldp_sync_info == NULL) + params->ldp_sync_info = ldp_sync_info_create(); + + ldp_sync_info = params->ldp_sync_info; + + SET_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_HOLDDOWN); + ldp_sync_info->holddown = holddown; + + return CMD_SUCCESS; +} + +DEFPY (no_mpls_ldp_sync_holddown, + no_mpls_ldp_sync_holddown_cmd, + "no ip ospf mpls ldp-sync holddown [<(1-10000)>]", + NO_STR + IP_STR + "OSPF interface commands\n" + MPLS_STR + NO_MPLS_LDP_SYNC_STR + NO_MPLS_LDP_SYNC_HOLDDOWN_STR) +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf_if_params *params; + struct ldp_sync_info *ldp_sync_info; + struct ospf *ospf; + + if (if_is_loopback(ifp)) { + vty_out(vty, "ldp-sync: does not run on loopback interface\n"); + return CMD_ERR_NOTHING_TODO; + } + + if (ifp->vrf_id != VRF_DEFAULT) { + vty_out(vty, "ldp-sync only runs on DEFAULT VRF\n"); + return CMD_ERR_NOTHING_TODO; + } + + params = IF_DEF_PARAMS(ifp); + ldp_sync_info = params->ldp_sync_info; + if (ldp_sync_info == NULL) + return CMD_SUCCESS; + + /* use global configured value if set */ + if (CHECK_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_HOLDDOWN)) { + UNSET_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_HOLDDOWN); + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if (ospf && CHECK_FLAG(ospf->ldp_sync_cmd.flags, + LDP_SYNC_FLAG_HOLDDOWN)) + ldp_sync_info->holddown = ospf->ldp_sync_cmd.holddown; + else + ldp_sync_info->holddown = LDP_IGP_SYNC_HOLDDOWN_DEFAULT; + } + return CMD_SUCCESS; +} + +DEFPY (show_ip_ospf_mpls_ldp_interface, + show_ip_ospf_mpls_ldp_interface_cmd, + "show ip ospf mpls ldp-sync [interface <INTERFACE|all>] [json]", + SHOW_STR + IP_STR + "OSPF information\n" + MPLS_STR + "LDP-IGP Sync information\n" + "Interface name\n" + JSON_STR) +{ + struct ospf *ospf; + bool uj = use_json(argc, argv); + char *intf_name = NULL; + int ret = CMD_SUCCESS; + int idx_intf = 0; + json_object *json = NULL; + + if (argv_find(argv, argc, "INTERFACE", &idx_intf)) + intf_name = argv[idx_intf]->arg; + + if (uj) + json = json_object_new_object(); + + /* Display default ospf (instance 0) info */ + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if (ospf == NULL || !ospf->oi_running) { + if (uj) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } else + vty_out(vty, "%% OSPF instance not found\n"); + return CMD_SUCCESS; + } + + if (!CHECK_FLAG(ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE)) { + if (uj) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } else + vty_out(vty, "LDP-sync is disabled\n"); + return CMD_SUCCESS; + } + + ret = show_ip_ospf_mpls_ldp_interface_common(vty, ospf, intf_name, + json, uj); + if (uj) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } + + return ret; +} + +void ospf_ldp_sync_init(void) +{ + /* Install global ldp-igp sync commands */ + install_element(OSPF_NODE, &ospf_mpls_ldp_sync_cmd); + install_element(OSPF_NODE, &no_ospf_mpls_ldp_sync_cmd); + install_element(OSPF_NODE, &ospf_mpls_ldp_sync_holddown_cmd); + install_element(OSPF_NODE, &no_ospf_mpls_ldp_sync_holddown_cmd); + + /* Interface lsp-igp sync commands */ + install_element(INTERFACE_NODE, &mpls_ldp_sync_cmd); + install_element(INTERFACE_NODE, &no_mpls_ldp_sync_cmd); + install_element(INTERFACE_NODE, &mpls_ldp_sync_holddown_cmd); + install_element(INTERFACE_NODE, &no_mpls_ldp_sync_holddown_cmd); + + /* "show ip ospf mpls ldp interface" commands. */ + install_element(VIEW_NODE, &show_ip_ospf_mpls_ldp_interface_cmd); + + hook_register(ospf_ism_change, ospf_ldp_sync_ism_change); + +} diff --git a/ospfd/ospf_ldp_sync.h b/ospfd/ospf_ldp_sync.h new file mode 100644 index 0000000000..d4efa55311 --- /dev/null +++ b/ospfd/ospf_ldp_sync.h @@ -0,0 +1,56 @@ +/* + * ospf_ldp_sync.h: OSPF LDP-IGP Sync handling routines + * Copyright (C) 2020 Volta Networks, Inc. + * + * This program 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 of the License, or (at your option) + * any later version. + * + * This program 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 _ZEBRA_OSPF_LDP_SYNC_H +#define _ZEBRA_OSPF_LDP_SYNC_H + +#define LDP_OSPF_LSINFINITY 65535 + +/* Macro to log debug message */ +#define ols_debug(...) \ + do { \ + if (IS_DEBUG_OSPF_LDP_SYNC) \ + zlog_debug(__VA_ARGS__); \ + } while (0) + + +extern void ospf_if_set_ldp_sync_enable(struct ospf *ospf, + struct interface *ifp); +extern void ospf_if_set_ldp_sync_holddown(struct ospf *ospf, + struct interface *ifp); +extern void ospf_ldp_sync_if_init(struct ospf_interface *ospf); +extern void ospf_ldp_sync_if_start(struct interface *ifp, bool send_state_req); +extern void ospf_ldp_sync_if_remove(struct interface *ifp, bool remove); +extern void ospf_ldp_sync_if_down(struct interface *ifp); +extern void ospf_ldp_sync_if_complete(struct interface *ifp); +extern void ospf_ldp_sync_holddown_timer_add(struct interface *ifp); +extern void ospf_ldp_sync_hello_timer_add(struct ospf *ospf); +extern void ospf_ldp_sync_ldp_fail(struct interface *ifp); +extern void ospf_ldp_sync_show_info(struct vty *vty, struct ospf *ospf, + json_object *json_vrf, bool use_json); +extern void ospf_ldp_sync_write_config(struct vty *vty, struct ospf *ospf); +extern void ospf_ldp_sync_if_write_config(struct vty *vty, + struct ospf_if_params *params); +extern int ospf_ldp_sync_state_update(struct ldp_igp_sync_if_state state); +extern int ospf_ldp_sync_announce_update(struct ldp_igp_sync_announce announce); +extern int ospf_ldp_sync_hello_update(struct ldp_igp_sync_hello hello); +extern void ospf_ldp_sync_state_req_msg(struct interface *ifp); +extern void ospf_ldp_sync_init(void); +extern void ospf_ldp_sync_gbl_exit(struct ospf *ospf, bool remove); +#endif /* _ZEBRA_OSPF_LDP_SYNC_H */ diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c index 376310e4ff..8095219146 100644 --- a/ospfd/ospf_lsa.c +++ b/ospfd/ospf_lsa.c @@ -1796,7 +1796,15 @@ struct ospf_lsa *ospf_translated_nssa_originate(struct ospf *ospf, return NULL; } - extnew = (struct as_external_lsa *)new; + extnew = (struct as_external_lsa *)new->data; + + if ((new = ospf_lsa_install(ospf, NULL, new)) == NULL) { + flog_warn( + EC_OSPF_LSA_INSTALL_FAILURE, + "ospf_lsa_translated_nssa_originate(): Could not install LSA id %s", + inet_ntoa(type7->data->id)); + return NULL; + } if (IS_DEBUG_OSPF_NSSA) { zlog_debug( @@ -1807,13 +1815,6 @@ struct ospf_lsa *ospf_translated_nssa_originate(struct ospf *ospf, inet_ntoa(extnew->e[0].fwd_addr)); } - if ((new = ospf_lsa_install(ospf, NULL, new)) == NULL) { - flog_warn(EC_OSPF_LSA_INSTALL_FAILURE, - "ospf_lsa_translated_nssa_originate(): Could not install LSA id %s", - inet_ntoa(type7->data->id)); - return NULL; - } - ospf->lsa_originate_count++; ospf_flood_through_as(ospf, NULL, new); diff --git a/ospfd/ospf_main.c b/ospfd/ospf_main.c index 45382e48d3..6be5486b55 100644 --- a/ospfd/ospf_main.c +++ b/ospfd/ospf_main.c @@ -54,6 +54,7 @@ #include "ospfd/ospf_vty.h" #include "ospfd/ospf_bfd.h" #include "ospfd/ospf_errors.h" +#include "ospfd/ospf_ldp_sync.h" /* ospfd privileges */ zebra_capabilities_t _caps_p[] = {ZCAP_NET_RAW, ZCAP_BIND, ZCAP_NET_ADMIN, @@ -217,6 +218,9 @@ int main(int argc, char **argv) /* OSPF BFD init */ ospf_bfd_init(); + /* OSPF LDP IGP Sync init */ + ospf_ldp_sync_init(); + ospf_route_map_init(); ospf_opaque_init(); diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index 8be7748c87..9d00ff65f9 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -52,6 +52,7 @@ #include "ospfd/ospf_vty.h" #include "ospfd/ospf_dump.h" #include "ospfd/ospf_bfd.h" +#include "ospfd/ospf_ldp_sync.h" FRR_CFG_DEFAULT_BOOL(OSPF_LOG_ADJACENCY_CHANGES, { .val_bool = true, .match_profile = "datacenter", }, @@ -3222,6 +3223,10 @@ static int show_ip_ospf_common(struct vty *vty, struct ospf *ospf, vty_out(vty, " Adjacency changes are logged\n"); } } + + /* show LDP-Sync status */ + ospf_ldp_sync_show_info(vty, ospf, json_vrf, json ? 1 : 0); + /* Show each area status. */ for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area)) show_ip_ospf_area(vty, area, json_areas, json ? 1 : 0); @@ -9975,6 +9980,9 @@ static int config_write_interface_one(struct vty *vty, struct vrf *vrf) vty_out(vty, "\n"); } + /* LDP-Sync print */ + if (params && params->ldp_sync_info) + ospf_ldp_sync_if_write_config(vty, params); while (1) { if (rn == NULL) @@ -10498,6 +10506,9 @@ static int ospf_config_write_one(struct vty *vty, struct ospf *ospf) ospf_opaque_config_write_router(vty, ospf); + /* LDP-Sync print */ + ospf_ldp_sync_write_config(vty, ospf); + write++; return write; } diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c index 8cf8430247..dc8a8dccd2 100644 --- a/ospfd/ospf_zebra.c +++ b/ospfd/ospf_zebra.c @@ -51,6 +51,7 @@ #include "ospfd/ospf_zebra.h" #include "ospfd/ospf_te.h" #include "ospfd/ospf_sr.h" +#include "ospfd/ospf_ldp_sync.h" DEFINE_MTYPE_STATIC(OSPFD, OSPF_EXTERNAL, "OSPF External route table") DEFINE_MTYPE_STATIC(OSPFD, OSPF_REDISTRIBUTE, "OSPF Redistriute") @@ -1354,16 +1355,26 @@ static int ospf_distribute_list_update_timer(struct thread *thread) else if ( (lsa = ospf_external_info_find_lsa( ospf, &ei->p))) { - if (!CHECK_FLAG( - lsa->flags, - OSPF_LSA_IN_MAXAGE)) - ospf_external_lsa_refresh( - ospf, lsa, ei, - LSA_REFRESH_IF_CHANGED); - else - ospf_external_lsa_refresh( - ospf, lsa, ei, - LSA_REFRESH_FORCE); + int force = + LSA_REFRESH_IF_CHANGED; + /* If this is a MaxAge LSA, we + * need to force refresh it + * because distribute settings + * might have changed and now, + * this LSA needs to be + * originated, not be removed. + * If we don't force refresh it, + * it will remain a MaxAge LSA + * because it will look like it + * hasn't changed. Neighbors + * will not receive updates for + * this LSA. + */ + if (IS_LSA_MAXAGE(lsa)) + force = LSA_REFRESH_FORCE; + + ospf_external_lsa_refresh( + ospf, lsa, ei, force); } else ospf_external_lsa_originate( ospf, ei); @@ -1833,6 +1844,45 @@ static void ospf_zebra_connected(struct zclient *zclient) zclient_send_reg_requests(zclient, VRF_DEFAULT); } +/* + * opaque messages between processes + */ +static int ospf_opaque_msg_handler(ZAPI_CALLBACK_ARGS) +{ + struct stream *s; + struct zapi_opaque_msg info; + struct ldp_igp_sync_if_state state; + struct ldp_igp_sync_announce announce; + struct ldp_igp_sync_hello hello; + int ret = 0; + + s = zclient->ibuf; + + if (zclient_opaque_decode(s, &info) != 0) + return -1; + + switch (info.type) { + case LDP_IGP_SYNC_IF_STATE_UPDATE: + STREAM_GET(&state, s, sizeof(state)); + ret = ospf_ldp_sync_state_update(state); + break; + case LDP_IGP_SYNC_ANNOUNCE_UPDATE: + STREAM_GET(&announce, s, sizeof(announce)); + ret = ospf_ldp_sync_announce_update(announce); + break; + case LDP_IGP_SYNC_HELLO_UPDATE: + STREAM_GET(&hello, s, sizeof(hello)); + ret = ospf_ldp_sync_hello_update(hello); + break; + default: + break; + } + +stream_failure: + + return ret; +} + void ospf_zebra_init(struct thread_master *master, unsigned short instance) { /* Allocate zebra structure. */ @@ -1866,6 +1916,8 @@ void ospf_zebra_init(struct thread_master *master, unsigned short instance) access_list_delete_hook(ospf_filter_update); prefix_list_add_hook(ospf_prefix_list_update); prefix_list_delete_hook(ospf_prefix_list_update); + + zclient->opaque_msg_handler = ospf_opaque_msg_handler; } void ospf_zebra_send_arp(const struct interface *ifp, const struct prefix *p) diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c index 31d8417eb6..cc5839a810 100644 --- a/ospfd/ospfd.c +++ b/ospfd/ospfd.c @@ -39,6 +39,7 @@ #include "libfrr.h" #include "defaults.h" #include "lib_errors.h" +#include "ldp_sync.h" #include "ospfd/ospfd.h" #include "ospfd/ospf_network.h" @@ -57,6 +58,7 @@ #include "ospfd/ospf_abr.h" #include "ospfd/ospf_flood.h" #include "ospfd/ospf_ase.h" +#include "ospfd/ospf_ldp_sync.h" DEFINE_QOBJ_TYPE(ospf) @@ -619,6 +621,10 @@ static void ospf_finish_final(struct ospf *ospf) list_delete(&ospf->vlinks); + /* shutdown LDP-Sync */ + if (ospf->vrf_id == VRF_DEFAULT) + ospf_ldp_sync_gbl_exit(ospf, true); + /* Remove any ospf interface config params */ FOR_ALL_INTERFACES (vrf, ifp) { struct ospf_if_params *params; @@ -948,6 +954,9 @@ static void add_ospf_interface(struct connected *co, struct ospf_area *area) ospf_area_add_if(oi->area, oi); + /* if LDP-IGP Sync is configured globally inherit config */ + ospf_ldp_sync_if_init(oi); + /* * if router_id is not configured, dont bring up * interfaces. diff --git a/ospfd/ospfd.h b/ospfd/ospfd.h index e5e07875e8..55bc64317e 100644 --- a/ospfd/ospfd.h +++ b/ospfd/ospfd.h @@ -25,6 +25,7 @@ #include <zebra.h> #include "qobj.h" #include "libospf.h" +#include "ldp_sync.h" #include "filter.h" #include "log.h" @@ -320,6 +321,9 @@ struct ospf { struct list *external[ZEBRA_ROUTE_MAX + 1]; #define EXTERNAL_INFO(E) (E->external_info) + /* MPLS LDP-IGP Sync */ + struct ldp_sync_info_cmd ldp_sync_cmd; + QOBJ_FIELDS }; DECLARE_QOBJ_TYPE(ospf) diff --git a/ospfd/subdir.am b/ospfd/subdir.am index 447ddf9cbb..236b76a1f7 100644 --- a/ospfd/subdir.am +++ b/ospfd/subdir.am @@ -9,6 +9,7 @@ dist_examples_DATA += ospfd/ospfd.conf.sample vtysh_scan += \ ospfd/ospf_bfd.c \ ospfd/ospf_dump.c \ + ospfd/ospf_ldp_sync.c \ ospfd/ospf_opaque.c \ ospfd/ospf_ri.c \ ospfd/ospf_routemap.c \ @@ -37,6 +38,7 @@ ospfd_libfrrospf_a_SOURCES = \ ospfd/ospf_ia.c \ ospfd/ospf_interface.c \ ospfd/ospf_ism.c \ + ospfd/ospf_ldp_sync.c \ ospfd/ospf_lsa.c \ ospfd/ospf_lsdb.c \ ospfd/ospf_memory.c \ @@ -74,6 +76,7 @@ endif clippy_scan += \ ospfd/ospf_vty.c \ + ospfd/ospf_ldp_sync.c \ # end noinst_HEADERS += \ @@ -86,6 +89,7 @@ noinst_HEADERS += \ ospfd/ospf_flood.h \ ospfd/ospf_ia.h \ ospfd/ospf_interface.h \ + ospfd/ospf_ldp_sync.h \ ospfd/ospf_memory.h \ ospfd/ospf_neighbor.h \ ospfd/ospf_network.h \ diff --git a/pbrd/pbr_map.c b/pbrd/pbr_map.c index 058881cbfc..fe2778c877 100644 --- a/pbrd/pbr_map.c +++ b/pbrd/pbr_map.c @@ -293,7 +293,7 @@ void pbr_map_policy_interface_update(const struct interface *ifp, bool state_up) for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, inode, pmi)) if (pmi->ifp == ifp && pbr_map_interface_is_valid(pmi)) - pbr_send_pbr_map(pbrms, pmi, state_up, false); + pbr_send_pbr_map(pbrms, pmi, state_up, true); } static void pbrms_vrf_update(struct pbr_map_sequence *pbrms, @@ -398,7 +398,7 @@ void pbr_map_delete_vrf(struct pbr_map_sequence *pbrms) pbr_map_delete_common(pbrms); } -struct pbr_map_sequence *pbrms_lookup_unique(uint32_t unique, ifindex_t ifindex, +struct pbr_map_sequence *pbrms_lookup_unique(uint32_t unique, char *ifname, struct pbr_map_interface **ppmi) { struct pbr_map_sequence *pbrms; @@ -408,7 +408,8 @@ struct pbr_map_sequence *pbrms_lookup_unique(uint32_t unique, ifindex_t ifindex, RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) { for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, inode, pmi)) { - if (pmi->ifp->ifindex != ifindex) + if (strncmp(pmi->ifp->name, ifname, INTERFACE_NAMSIZ) + != 0) continue; if (ppmi) diff --git a/pbrd/pbr_map.h b/pbrd/pbr_map.h index 43266f21e9..ad2db146b7 100644 --- a/pbrd/pbr_map.h +++ b/pbrd/pbr_map.h @@ -158,7 +158,7 @@ extern struct pbr_map_entry_head pbr_maps; extern struct pbr_map_sequence *pbrms_get(const char *name, uint32_t seqno); extern struct pbr_map_sequence * -pbrms_lookup_unique(uint32_t unique, ifindex_t ifindex, +pbrms_lookup_unique(uint32_t unique, char *ifname, struct pbr_map_interface **ppmi); extern struct pbr_map *pbrm_find(const char *name); diff --git a/pbrd/pbr_zebra.c b/pbrd/pbr_zebra.c index a7420974a9..269bd6da8d 100644 --- a/pbrd/pbr_zebra.c +++ b/pbrd/pbr_zebra.c @@ -208,15 +208,15 @@ static int rule_notify_owner(ZAPI_CALLBACK_ARGS) enum zapi_rule_notify_owner note; struct pbr_map_sequence *pbrms; struct pbr_map_interface *pmi; - ifindex_t ifi; + char ifname[INTERFACE_NAMSIZ + 1]; uint64_t installed; if (!zapi_rule_notify_decode(zclient->ibuf, &seqno, &priority, &unique, - &ifi, ¬e)) + ifname, ¬e)) return -1; pmi = NULL; - pbrms = pbrms_lookup_unique(unique, ifi, &pmi); + pbrms = pbrms_lookup_unique(unique, ifname, &pmi); if (!pbrms) { DEBUGD(&pbr_dbg_zebra, "%s: Failure to lookup pbrms based upon %u", __func__, @@ -546,7 +546,7 @@ static void pbr_encode_pbr_map_sequence(struct stream *s, stream_putl(s, pbr_nht_get_table(pbrms->nhgrp_name)); else if (pbrms->nhg) stream_putl(s, pbr_nht_get_table(pbrms->internal_nhg_name)); - stream_putl(s, ifp->ifindex); + stream_put(s, ifp->name, INTERFACE_NAMSIZ); } void pbr_send_pbr_map(struct pbr_map_sequence *pbrms, diff --git a/pimd/pimd.c b/pimd/pimd.c index 6c354a3cc8..811dc96b56 100644 --- a/pimd/pimd.c +++ b/pimd/pimd.c @@ -44,6 +44,10 @@ #include "pim_zebra.h" #include "pim_mlag.h" +#if MAXVIFS > 256 +CPP_NOTICE("Work needs to be done to make this work properly via the pim mroute socket\n"); +#endif /* MAXVIFS > 256 */ + const char *const PIM_ALL_SYSTEMS = MCAST_ALL_SYSTEMS; const char *const PIM_ALL_ROUTERS = MCAST_ALL_ROUTERS; const char *const PIM_ALL_PIM_ROUTERS = MCAST_ALL_PIM_ROUTERS; diff --git a/redhat/frr.spec.in b/redhat/frr.spec.in index 929214a142..bd0d5b27f4 100644 --- a/redhat/frr.spec.in +++ b/redhat/frr.spec.in @@ -170,7 +170,7 @@ BuildRequires: make BuildRequires: ncurses-devel BuildRequires: readline-devel BuildRequires: texinfo -BuildRequires: libyang-devel >= 0.16.74 +BuildRequires: libyang-devel >= 1.0.184 %if 0%{?rhel} && 0%{?rhel} < 7 #python27-devel is available from ius community repo for RedHat/CentOS 6 BuildRequires: python27-devel diff --git a/snapcraft/snapcraft.yaml.in b/snapcraft/snapcraft.yaml.in index b06e841f74..c0c31b5fd1 100644 --- a/snapcraft/snapcraft.yaml.in +++ b/snapcraft/snapcraft.yaml.in @@ -273,12 +273,12 @@ parts: - libpcre3 source: https://github.com/CESNET/libyang.git source-type: git - source-tag: v0.16-r3 + source-tag: v1.0.184 plugin: cmake configflags: - -DCMAKE_INSTALL_PREFIX:PATH=/usr - -DENABLE_LYD_PRIV=ON - - -DENABLE_CACHE=OFF + - -DENABLE_CACHE=ON - -DCMAKE_BUILD_TYPE:String="Release" frr: after: [rtrlib,libyang] diff --git a/staticd/static_vty.c b/staticd/static_vty.c index 4763406934..4821671554 100644 --- a/staticd/static_vty.c +++ b/staticd/static_vty.c @@ -81,11 +81,7 @@ static int static_route_leak(struct vty *vty, const char *svrf, ret = str2prefix(dest_str, &p); if (ret <= 0) { - if (vty) - vty_out(vty, "%% Malformed address\n"); - else - zlog_warn("%s: Malformed address: %s", __func__, - dest_str); + vty_out(vty, "%% Malformed address\n"); return CMD_WARNING_CONFIG_FAILED; } @@ -95,11 +91,7 @@ static int static_route_leak(struct vty *vty, const char *svrf, if (mask_str) { ret = inet_aton(mask_str, &mask); if (ret == 0) { - if (vty) - vty_out(vty, "%% Malformed address\n"); - else - zlog_warn("%s: Malformed address: %s", - __func__, mask_str); + vty_out(vty, "%% Malformed address\n"); return CMD_WARNING_CONFIG_FAILED; } p.prefixlen = ip_masklen(mask); @@ -110,13 +102,7 @@ static int static_route_leak(struct vty *vty, const char *svrf, if (src_str) { ret = str2prefix(src_str, &src); if (ret <= 0 || src.family != AF_INET6) { - if (vty) - vty_out(vty, - "%% Malformed source address\n"); - else - zlog_warn( - "%s: Malformed source address: %s", - __func__, src_str); + vty_out(vty, "%% Malformed source address\n"); return CMD_WARNING_CONFIG_FAILED; } } @@ -437,7 +423,8 @@ int static_config(struct vty *vty, struct static_vrf *svrf, afi_t afi, nh->snh_label.label, buf, sizeof(buf), 0)); - if (nh->nh_vrf_id != GET_STABLE_VRF_ID(info)) + if (!strmatch(nh->nh_vrfname, + info->svrf->vrf->name)) vty_out(vty, " nexthop-vrf %s", nh->nh_vrfname); diff --git a/tests/topotests/all-protocol-startup/r1/bgpd.conf b/tests/topotests/all-protocol-startup/r1/bgpd.conf index e000b4e625..d287b86175 100644 --- a/tests/topotests/all-protocol-startup/r1/bgpd.conf +++ b/tests/topotests/all-protocol-startup/r1/bgpd.conf @@ -7,9 +7,13 @@ router bgp 100 no bgp ebgp-requires-policy no bgp network import-check neighbor 192.168.7.10 remote-as 100 + neighbor 192.168.7.10 timers 3 10 neighbor 192.168.7.20 remote-as 200 + neighbor 192.168.7.20 timers 3 10 neighbor fc00:0:0:8::1000 remote-as 100 + neighbor fc00:0:0:8::1000 timers 3 10 neighbor fc00:0:0:8::2000 remote-as 200 + neighbor fc00:0:0:8::2000 timers 3 10 ! address-family ipv4 unicast network 192.168.0.0/24 diff --git a/tests/topotests/bfd-bgp-cbit-topo3/r1/bgpd.conf b/tests/topotests/bfd-bgp-cbit-topo3/r1/bgpd.conf index 82b189b292..689797a5e6 100644 --- a/tests/topotests/bfd-bgp-cbit-topo3/r1/bgpd.conf +++ b/tests/topotests/bfd-bgp-cbit-topo3/r1/bgpd.conf @@ -6,6 +6,7 @@ router bgp 101 timers bgp 8 24 bgp graceful-restart neighbor 2001:db8:4::1 remote-as 102 + neighbor 2001:db8:4::1 timers 3 10 neighbor 2001:db8:4::1 remote-as external neighbor 2001:db8:4::1 bfd neighbor 2001:db8:4::1 bfd check-control-plane-failure diff --git a/tests/topotests/bfd-bgp-cbit-topo3/r3/bgpd.conf b/tests/topotests/bfd-bgp-cbit-topo3/r3/bgpd.conf index e566e08f7d..1f5aac42ed 100644 --- a/tests/topotests/bfd-bgp-cbit-topo3/r3/bgpd.conf +++ b/tests/topotests/bfd-bgp-cbit-topo3/r3/bgpd.conf @@ -10,6 +10,7 @@ router bgp 102 bgp graceful-restart stalepath-time 900 bgp graceful-restart restart-time 900 neighbor 2001:db8:1::1 remote-as 101 + neighbor 2001:db8:1::1 timers 3 10 neighbor 2001:db8:1::1 remote-as external neighbor 2001:db8:1::1 update-source 2001:db8:4::1 neighbor 2001:db8:1::1 bfd diff --git a/tests/topotests/bfd-bgp-cbit-topo3/test_bfd_bgp_cbit_topo3.py b/tests/topotests/bfd-bgp-cbit-topo3/test_bfd_bgp_cbit_topo3.py index 186dac31a0..7d1521c8b2 100755..100644 --- a/tests/topotests/bfd-bgp-cbit-topo3/test_bfd_bgp_cbit_topo3.py +++ b/tests/topotests/bfd-bgp-cbit-topo3/test_bfd_bgp_cbit_topo3.py @@ -72,7 +72,7 @@ def setup_module(mod): router_list = tgen.routers() - for rname, router in router_list.iteritems(): + for rname, router in router_list.items(): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)), ) diff --git a/tests/topotests/bfd-isis-topo1/test_bfd_isis_topo1.py b/tests/topotests/bfd-isis-topo1/test_bfd_isis_topo1.py index a1ed0cc2af..1adfec76d8 100755..100644 --- a/tests/topotests/bfd-isis-topo1/test_bfd_isis_topo1.py +++ b/tests/topotests/bfd-isis-topo1/test_bfd_isis_topo1.py @@ -136,7 +136,7 @@ def setup_module(mod): router_list = tgen.routers() # For all registered routers, load the zebra configuration file - for rname, router in router_list.iteritems(): + for rname, router in router_list.items(): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ) diff --git a/tests/topotests/bfd-profiles-topo1/r2/bgpd.conf b/tests/topotests/bfd-profiles-topo1/r2/bgpd.conf index 824d01983f..0c3db97bc1 100644 --- a/tests/topotests/bfd-profiles-topo1/r2/bgpd.conf +++ b/tests/topotests/bfd-profiles-topo1/r2/bgpd.conf @@ -4,8 +4,10 @@ router bgp 100 bgp router-id 10.254.254.2 no bgp ebgp-requires-policy neighbor 172.16.1.1 remote-as 100 + neighbor 172.16.1.1 timers 3 10 neighbor 172.16.1.1 bfd profile fasttx neighbor 2001:db8:2::2 remote-as 200 + neighbor 2001:db8:2::2 timers 3 10 neighbor 2001:db8:2::2 ebgp-multihop 2 neighbor 2001:db8:2::2 bfd profile slowtx address-family ipv4 unicast diff --git a/tests/topotests/bfd-profiles-topo1/r3/bgpd.conf b/tests/topotests/bfd-profiles-topo1/r3/bgpd.conf index c7b75d2fde..65647b39e5 100644 --- a/tests/topotests/bfd-profiles-topo1/r3/bgpd.conf +++ b/tests/topotests/bfd-profiles-topo1/r3/bgpd.conf @@ -1,6 +1,7 @@ router bgp 100 bgp router-id 10.254.254.3 neighbor 172.16.1.2 remote-as 100 + neighbor 172.16.1.2 timers 3 10 neighbor 172.16.1.2 bfd profile DOES_NOT_EXIST address-family ipv4 unicast redistribute connected diff --git a/tests/topotests/bfd-profiles-topo1/r4/bgpd.conf b/tests/topotests/bfd-profiles-topo1/r4/bgpd.conf index aff1016dee..200937a9a5 100644 --- a/tests/topotests/bfd-profiles-topo1/r4/bgpd.conf +++ b/tests/topotests/bfd-profiles-topo1/r4/bgpd.conf @@ -4,6 +4,7 @@ router bgp 200 bgp router-id 10.254.254.4 no bgp ebgp-requires-policy neighbor 2001:db8:1::2 remote-as 100 + neighbor 2001:db8:1::2 timers 3 10 neighbor 2001:db8:1::2 ebgp-multihop 2 neighbor 2001:db8:1::2 bfd profile DOES_NOT_EXIST address-family ipv4 unicast diff --git a/tests/topotests/bfd-profiles-topo1/test_bfd_profiles_topo1.py b/tests/topotests/bfd-profiles-topo1/test_bfd_profiles_topo1.py index 02385b32e5..514933b891 100755..100644 --- a/tests/topotests/bfd-profiles-topo1/test_bfd_profiles_topo1.py +++ b/tests/topotests/bfd-profiles-topo1/test_bfd_profiles_topo1.py @@ -84,7 +84,7 @@ def setup_module(mod): tgen.start_topology() router_list = tgen.routers() - for rname, router in router_list.iteritems(): + for rname, router in router_list.items(): daemon_file = "{}/{}/bfdd.conf".format(CWD, rname) if os.path.isfile(daemon_file): router.load_config(TopoRouter.RD_BFD, daemon_file) diff --git a/tests/topotests/bfd-topo1/r1/bgpd.conf b/tests/topotests/bfd-topo1/r1/bgpd.conf index 25e16a6e0c..57bde1f234 100644 --- a/tests/topotests/bfd-topo1/r1/bgpd.conf +++ b/tests/topotests/bfd-topo1/r1/bgpd.conf @@ -2,6 +2,7 @@ router bgp 101 no bgp ebgp-requires-policy no bgp network import-check neighbor 192.168.0.2 remote-as 102 + neighbor 192.168.0.2 timers 3 10 neighbor 192.168.0.2 bfd address-family ipv4 unicast network 10.254.254.1/32 diff --git a/tests/topotests/bfd-topo1/r2/bgpd.conf b/tests/topotests/bfd-topo1/r2/bgpd.conf index 693fb93411..50d75ab67f 100644 --- a/tests/topotests/bfd-topo1/r2/bgpd.conf +++ b/tests/topotests/bfd-topo1/r2/bgpd.conf @@ -2,10 +2,13 @@ router bgp 102 no bgp ebgp-requires-policy no bgp network import-check neighbor 192.168.0.1 remote-as 101 + neighbor 192.168.0.1 timers 3 10 neighbor 192.168.0.1 bfd neighbor 192.168.1.1 remote-as 103 + neighbor 192.168.1.1 timers 3 10 neighbor 192.168.1.1 bfd neighbor 192.168.2.1 remote-as 104 + neighbor 192.168.2.1 timers 3 10 neighbor 192.168.2.1 bfd address-family ipv4 unicast network 10.254.254.2/32 diff --git a/tests/topotests/bfd-topo1/r3/bgpd.conf b/tests/topotests/bfd-topo1/r3/bgpd.conf index 7584f98803..ce6055d518 100644 --- a/tests/topotests/bfd-topo1/r3/bgpd.conf +++ b/tests/topotests/bfd-topo1/r3/bgpd.conf @@ -2,6 +2,7 @@ router bgp 103 no bgp ebgp-requires-policy no bgp network import-check neighbor 192.168.1.2 remote-as 102 + neighbor 192.168.1.2 timers 3 10 neighbor 192.168.1.2 bfd address-family ipv4 unicast network 10.254.254.3/32 diff --git a/tests/topotests/bfd-topo1/r4/bgpd.conf b/tests/topotests/bfd-topo1/r4/bgpd.conf index 3c68e7eec9..0d032b4cdd 100644 --- a/tests/topotests/bfd-topo1/r4/bgpd.conf +++ b/tests/topotests/bfd-topo1/r4/bgpd.conf @@ -2,6 +2,7 @@ router bgp 104 no bgp ebgp-requires-policy no bgp network import-check neighbor 192.168.2.2 remote-as 102 + neighbor 192.168.2.2 timers 3 10 neighbor 192.168.2.2 bfd address-family ipv4 unicast network 10.254.254.4/32 diff --git a/tests/topotests/bfd-topo1/test_bfd_topo1.py b/tests/topotests/bfd-topo1/test_bfd_topo1.py index e1865dc5a8..5306fdf353 100644 --- a/tests/topotests/bfd-topo1/test_bfd_topo1.py +++ b/tests/topotests/bfd-topo1/test_bfd_topo1.py @@ -76,7 +76,7 @@ def setup_module(mod): tgen.start_topology() router_list = tgen.routers() - for rname, router in router_list.iteritems(): + for rname, router in router_list.items(): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ) diff --git a/tests/topotests/bfd-topo2/r1/bgpd.conf b/tests/topotests/bfd-topo2/r1/bgpd.conf index 4d96bec2cb..0918796e95 100644 --- a/tests/topotests/bfd-topo2/r1/bgpd.conf +++ b/tests/topotests/bfd-topo2/r1/bgpd.conf @@ -5,6 +5,7 @@ router bgp 101 neighbor r2g remote-as external neighbor r2g bfd neighbor r1-eth0 interface peer-group r2g + neighbor r1-eth0 timers 3 10 address-family ipv4 unicast redistribute connected exit-address-family diff --git a/tests/topotests/bfd-topo2/r2/bgpd.conf b/tests/topotests/bfd-topo2/r2/bgpd.conf index 4d02fc4f29..55d48560e7 100644 --- a/tests/topotests/bfd-topo2/r2/bgpd.conf +++ b/tests/topotests/bfd-topo2/r2/bgpd.conf @@ -5,6 +5,7 @@ router bgp 102 neighbor r2g remote-as external neighbor r2g bfd neighbor r2-eth0 interface peer-group r2g + neighbor r2-eth0 timers 3 10 ! address-family ipv4 unicast redistribute connected diff --git a/tests/topotests/bfd-topo2/test_bfd_topo2.py b/tests/topotests/bfd-topo2/test_bfd_topo2.py index 3e87e8485a..2c5ce3e4c3 100644 --- a/tests/topotests/bfd-topo2/test_bfd_topo2.py +++ b/tests/topotests/bfd-topo2/test_bfd_topo2.py @@ -77,7 +77,7 @@ def setup_module(mod): tgen.start_topology() router_list = tgen.routers() - for rname, router in router_list.iteritems(): + for rname, router in router_list.items(): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ) diff --git a/tests/topotests/bfd-topo3/r1/bgpd.conf b/tests/topotests/bfd-topo3/r1/bgpd.conf index a0281d50c6..4c75d669c5 100644 --- a/tests/topotests/bfd-topo3/r1/bgpd.conf +++ b/tests/topotests/bfd-topo3/r1/bgpd.conf @@ -1,11 +1,14 @@ router bgp 100 no bgp ebgp-requires-policy neighbor 2001:db8:1::2 remote-as internal + neighbor 2001:db8:1::2 timers 3 10 neighbor 2001:db8:1::2 bfd profile fast-tx neighbor 192.168.2.1 remote-as external + neighbor 192.168.2.1 timers 3 10 neighbor 192.168.2.1 ebgp-multihop 2 neighbor 192.168.2.1 bfd profile slow-tx neighbor 2001:db8:3::1 remote-as external + neighbor 2001:db8:3::1 timers 3 10 neighbor 2001:db8:3::1 ebgp-multihop 3 neighbor 2001:db8:3::1 bfd profile slow-tx address-family ipv4 unicast diff --git a/tests/topotests/bfd-topo3/r2/bgpd.conf b/tests/topotests/bfd-topo3/r2/bgpd.conf index 0e96033023..75225765e1 100644 --- a/tests/topotests/bfd-topo3/r2/bgpd.conf +++ b/tests/topotests/bfd-topo3/r2/bgpd.conf @@ -1,8 +1,10 @@ router bgp 100 no bgp ebgp-requires-policy neighbor 2001:db8:1::1 remote-as internal + neighbor 2001:db8:1::1 timers 3 10 neighbor 2001:db8:1::1 bfd profile fast-tx neighbor 2001:db8:2::1 remote-as external + neighbor 2001:db8:2::1 timers 3 10 neighbor 2001:db8:2::1 bfd profile slow-tx address-family ipv4 unicast redistribute connected diff --git a/tests/topotests/bfd-topo3/r3/bgpd.conf b/tests/topotests/bfd-topo3/r3/bgpd.conf index e14d2011a0..82adf8be9e 100644 --- a/tests/topotests/bfd-topo3/r3/bgpd.conf +++ b/tests/topotests/bfd-topo3/r3/bgpd.conf @@ -1,11 +1,14 @@ router bgp 300 no bgp ebgp-requires-policy neighbor 192.168.1.1 remote-as external + neighbor 192.168.1.1 timers 3 10 neighbor 192.168.1.1 ebgp-multihop 2 neighbor 192.168.1.1 bfd profile slow-tx neighbor 2001:db8:2::2 remote-as external + neighbor 2001:db8:2::2 timers 3 10 neighbor 2001:db8:2::2 bfd profile slow-tx neighbor 2001:db8:3::1 remote-as external + neighbor 2001:db8:3::1 timers 3 10 neighbor 2001:db8:3::1 bfd profile slow-tx address-family ipv4 unicast redistribute connected diff --git a/tests/topotests/bfd-topo3/r4/bgpd.conf b/tests/topotests/bfd-topo3/r4/bgpd.conf index 3e81008d5d..0aab6e3017 100644 --- a/tests/topotests/bfd-topo3/r4/bgpd.conf +++ b/tests/topotests/bfd-topo3/r4/bgpd.conf @@ -1,8 +1,10 @@ router bgp 400 no bgp ebgp-requires-policy neighbor 2001:db8:3::2 remote-as external + neighbor 2001:db8:3::2 timers 3 10 neighbor 2001:db8:3::2 bfd profile slow-tx neighbor 2001:db8:1::1 remote-as external + neighbor 2001:db8:1::1 timers 3 10 neighbor 2001:db8:1::1 ebgp-multihop 3 neighbor 2001:db8:1::1 bfd profile slow-tx-mh address-family ipv4 unicast diff --git a/tests/topotests/bfd-topo3/test_bfd_topo3.py b/tests/topotests/bfd-topo3/test_bfd_topo3.py index bcee338a92..fa68ace59d 100644 --- a/tests/topotests/bfd-topo3/test_bfd_topo3.py +++ b/tests/topotests/bfd-topo3/test_bfd_topo3.py @@ -76,7 +76,7 @@ def setup_module(mod): tgen.start_topology() router_list = tgen.routers() - for rname, router in router_list.iteritems(): + for rname, router in router_list.items(): daemon_file = "{}/{}/bfdd.conf".format(CWD, rname) if os.path.isfile(daemon_file): router.load_config(TopoRouter.RD_BFD, daemon_file) diff --git a/tests/topotests/bfd-vrf-topo1/r1/bgpd.conf b/tests/topotests/bfd-vrf-topo1/r1/bgpd.conf index 439c58fb2a..5bb45b9863 100644 --- a/tests/topotests/bfd-vrf-topo1/r1/bgpd.conf +++ b/tests/topotests/bfd-vrf-topo1/r1/bgpd.conf @@ -2,6 +2,7 @@ router bgp 101 vrf r1-cust1 no bgp ebgp-requires-policy no bgp network import-check neighbor 192.168.0.2 remote-as 102 + neighbor 192.168.0.2 timers 3 10 ! neighbor 192.168.0.2 ebgp-multihop 10 neighbor 192.168.0.2 bfd address-family ipv4 unicast diff --git a/tests/topotests/bfd-vrf-topo1/r2/bgpd.conf b/tests/topotests/bfd-vrf-topo1/r2/bgpd.conf index 4fac25d7bb..b2aac74685 100644 --- a/tests/topotests/bfd-vrf-topo1/r2/bgpd.conf +++ b/tests/topotests/bfd-vrf-topo1/r2/bgpd.conf @@ -2,10 +2,13 @@ router bgp 102 vrf r2-cust1 no bgp ebgp-requires-policy no bgp network import-check neighbor 192.168.0.1 remote-as 101 + neighbor 192.168.0.1 timers 3 10 neighbor 192.168.0.1 bfd neighbor 192.168.1.1 remote-as 103 + neighbor 192.168.1.1 timers 3 10 neighbor 192.168.1.1 bfd neighbor 192.168.2.1 remote-as 104 + neighbor 192.168.2.1 timers 3 10 neighbor 192.168.2.1 bfd address-family ipv4 unicast network 10.254.254.2/32 diff --git a/tests/topotests/bfd-vrf-topo1/r3/bgpd.conf b/tests/topotests/bfd-vrf-topo1/r3/bgpd.conf index 052707ae1b..1d7c730395 100644 --- a/tests/topotests/bfd-vrf-topo1/r3/bgpd.conf +++ b/tests/topotests/bfd-vrf-topo1/r3/bgpd.conf @@ -2,6 +2,7 @@ router bgp 103 vrf r3-cust1 no bgp ebgp-requires-policy no bgp network import-check neighbor 192.168.1.2 remote-as 102 + neighbor 192.168.1.2 timers 3 10 neighbor 192.168.1.2 bfd address-family ipv4 unicast network 10.254.254.3/32 diff --git a/tests/topotests/bfd-vrf-topo1/r4/bgpd.conf b/tests/topotests/bfd-vrf-topo1/r4/bgpd.conf index bcb0a17c01..f34035d460 100644 --- a/tests/topotests/bfd-vrf-topo1/r4/bgpd.conf +++ b/tests/topotests/bfd-vrf-topo1/r4/bgpd.conf @@ -2,6 +2,7 @@ router bgp 104 vrf r4-cust1 no bgp ebgp-requires-policy no bgp network import-check neighbor 192.168.2.2 remote-as 102 + neighbor 192.168.2.2 timers 3 10 neighbor 192.168.2.2 bfd address-family ipv4 unicast network 10.254.254.4/32 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 b1f755ad06..95595ecba4 100755..100644 --- a/tests/topotests/bfd-vrf-topo1/test_bfd_vrf_topo1.py +++ b/tests/topotests/bfd-vrf-topo1/test_bfd_vrf_topo1.py @@ -79,7 +79,7 @@ def setup_module(mod): router_list = tgen.routers() # check for zebra capability - for rname, router in router_list.iteritems(): + for rname, router in router_list.items(): if router.check_capability(TopoRouter.RD_ZEBRA, "--vrfwnetns") == False: return pytest.skip( "Skipping BFD Topo1 VRF NETNS feature. VRF NETNS backend not available on FRR" @@ -105,7 +105,7 @@ def setup_module(mod): "ip netns exec {0}-cust1 ifconfig {0}-eth2 up", ] - for rname, router in router_list.iteritems(): + for rname, router in router_list.items(): # create VRF rx-cust1 and link rx-eth0 to rx-cust1 for cmd in cmds: output = tgen.net[rname].cmd(cmd.format(rname)) @@ -113,7 +113,7 @@ def setup_module(mod): for cmd in cmds2: output = tgen.net[rname].cmd(cmd.format(rname)) - for rname, router in router_list.iteritems(): + for rname, router in router_list.items(): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)), @@ -145,7 +145,7 @@ def teardown_module(_mod): ] router_list = tgen.routers() - for rname, router in router_list.iteritems(): + for rname, router in router_list.items(): if rname == "r2": for cmd in cmds2: tgen.net[rname].cmd(cmd.format(rname)) diff --git a/tests/topotests/bgp-auth/test_bgp_auth.py b/tests/topotests/bgp-auth/test_bgp_auth.py index 6198997b86..286af3bf65 100755..100644 --- a/tests/topotests/bgp-auth/test_bgp_auth.py +++ b/tests/topotests/bgp-auth/test_bgp_auth.py @@ -217,7 +217,7 @@ def setup_module(mod): router_list = tgen.routers() # For all registred routers, load the zebra configuration file - for rname, router in router_list.iteritems(): + for rname, router in router_list.items(): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ) @@ -273,7 +273,7 @@ def print_diag(vrf): tgen = get_topogen() router_list = tgen.routers() - for rname, router in router_list.iteritems(): + for rname, router in router_list.items(): print(rname + ":") print(router.vtysh_cmd("show run")) print(router.vtysh_cmd("show ip route {}".format(vrf_str(vrf)))) @@ -285,7 +285,7 @@ def configure(conf_file): tgen = get_topogen() router_list = tgen.routers() - for rname, router in router_list.iteritems(): + for rname, router in router_list.items(): with open( os.path.join(CWD, "{}/{}").format(router.name, conf_file), "r+" ) as cfg: @@ -321,7 +321,7 @@ def clear_ospf(vrf=""): tgen = get_topogen() router_list = tgen.routers() - for rname, router in router_list.iteritems(): + for rname, router in router_list.items(): if vrf == "": router.vtysh_cmd("conf t\nno router ospf") else: 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 69e050caed..41fa7c0a09 100755 --- a/tests/topotests/bgp-basic-functionality-topo1/test_bgp_basic_functionality.py +++ b/tests/topotests/bgp-basic-functionality-topo1/test_bgp_basic_functionality.py @@ -76,6 +76,7 @@ from lib.common_config import ( create_prefix_lists, create_route_maps, verify_bgp_community, + required_linux_kernel_version ) from lib.topolog import logger from lib.bgp import ( @@ -137,6 +138,11 @@ def setup_module(mod): * `mod`: module name """ + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version('4.15') + if result is not True: + pytest.skip("Kernel requirements are not met") + testsuite_run_time = time.asctime(time.localtime(time.time())) logger.info("Testsuite start time: {}".format(testsuite_run_time)) logger.info("=" * 40) diff --git a/tests/topotests/bgp-ecmp-topo1/r1/bgpd.conf b/tests/topotests/bgp-ecmp-topo1/r1/bgpd.conf index d3beb2d320..49981ac589 100644 --- a/tests/topotests/bgp-ecmp-topo1/r1/bgpd.conf +++ b/tests/topotests/bgp-ecmp-topo1/r1/bgpd.conf @@ -7,25 +7,45 @@ router bgp 100 bgp bestpath as-path multipath-relax no bgp ebgp-requires-policy neighbor 10.0.1.101 remote-as 99 + neighbor 10.0.1.101 timers 3 10 neighbor 10.0.1.102 remote-as 99 + neighbor 10.0.1.102 timers 3 10 neighbor 10.0.1.103 remote-as 99 + neighbor 10.0.1.103 timers 3 10 neighbor 10.0.1.104 remote-as 99 + neighbor 10.0.1.104 timers 3 10 neighbor 10.0.1.105 remote-as 99 + neighbor 10.0.1.105 timers 3 10 neighbor 10.0.2.106 remote-as 99 + neighbor 10.0.2.106 timers 3 10 neighbor 10.0.2.107 remote-as 99 + neighbor 10.0.2.107 timers 3 10 neighbor 10.0.2.108 remote-as 99 + neighbor 10.0.2.108 timers 3 10 neighbor 10.0.2.109 remote-as 99 + neighbor 10.0.2.109 timers 3 10 neighbor 10.0.2.110 remote-as 99 + neighbor 10.0.2.110 timers 3 10 neighbor 10.0.3.111 remote-as 111 + neighbor 10.0.3.111 timers 3 10 neighbor 10.0.3.112 remote-as 112 + neighbor 10.0.3.112 timers 3 10 neighbor 10.0.3.113 remote-as 113 + neighbor 10.0.3.113 timers 3 10 neighbor 10.0.3.114 remote-as 114 + neighbor 10.0.3.114 timers 3 10 neighbor 10.0.3.115 remote-as 115 + neighbor 10.0.3.115 timers 3 10 neighbor 10.0.4.116 remote-as 116 + neighbor 10.0.4.116 timers 3 10 neighbor 10.0.4.117 remote-as 117 + neighbor 10.0.4.117 timers 3 10 neighbor 10.0.4.118 remote-as 118 + neighbor 10.0.4.118 timers 3 10 neighbor 10.0.4.119 remote-as 119 + neighbor 10.0.4.119 timers 3 10 neighbor 10.0.4.120 remote-as 120 + neighbor 10.0.4.120 timers 3 10 ! ! diff --git a/tests/topotests/bgp-ecmp-topo1/test_bgp_ecmp_topo1.py b/tests/topotests/bgp-ecmp-topo1/test_bgp_ecmp_topo1.py index c37f818b0f..400e7e9bf5 100755..100644 --- a/tests/topotests/bgp-ecmp-topo1/test_bgp_ecmp_topo1.py +++ b/tests/topotests/bgp-ecmp-topo1/test_bgp_ecmp_topo1.py @@ -95,7 +95,7 @@ def setup_module(module): # Starting Routers router_list = tgen.routers() - for rname, router in router_list.iteritems(): + for rname, router in router_list.items(): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ) @@ -107,7 +107,7 @@ def setup_module(module): # Starting Hosts and init ExaBGP on each of them topotest.sleep(10, "starting BGP on all {} peers".format(total_ebgp_peers)) peer_list = tgen.exabgp_peers() - for pname, peer in peer_list.iteritems(): + for pname, peer in peer_list.items(): peer_dir = os.path.join(CWD, pname) env_file = os.path.join(CWD, "exabgp.env") peer.start(peer_dir, env_file) diff --git a/tests/topotests/bgp-ecmp-topo2/test_ebgp_ecmp_topo2.py b/tests/topotests/bgp-ecmp-topo2/test_ebgp_ecmp_topo2.py index 948f641afb..eed118ebdc 100755..100644 --- a/tests/topotests/bgp-ecmp-topo2/test_ebgp_ecmp_topo2.py +++ b/tests/topotests/bgp-ecmp-topo2/test_ebgp_ecmp_topo2.py @@ -61,6 +61,7 @@ from lib.common_config import ( check_address_types, interface_status, reset_config_on_routers, + required_linux_kernel_version ) from lib.topolog import logger from lib.bgp import verify_bgp_convergence, create_router_bgp, clear_bgp @@ -108,6 +109,11 @@ def setup_module(mod): global NEXT_HOPS, INTF_LIST_R3, INTF_LIST_R2, TEST_STATIC global ADDR_TYPES + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version('4.15') + if result is not True: + pytest.skip("Kernel requirements are not met") + testsuite_run_time = time.asctime(time.localtime(time.time())) logger.info("Testsuite start time: {}".format(testsuite_run_time)) logger.info("=" * 40) @@ -139,7 +145,7 @@ def setup_module(mod): link_data = [ val - for links, val in topo["routers"]["r2"]["links"].iteritems() + for links, val in topo["routers"]["r2"]["links"].items() if "r3" in links ] for adt in ADDR_TYPES: @@ -156,7 +162,7 @@ def setup_module(mod): link_data = [ val - for links, val in topo["routers"]["r3"]["links"].iteritems() + for links, val in topo["routers"]["r3"]["links"].items() if "r2" in links ] INTF_LIST_R3 = [val["interface"].split("/")[0] for val in link_data] diff --git a/tests/topotests/bgp-ecmp-topo2/test_ibgp_ecmp_topo2.py b/tests/topotests/bgp-ecmp-topo2/test_ibgp_ecmp_topo2.py index 5b997fdd16..7357c33824 100755..100644 --- a/tests/topotests/bgp-ecmp-topo2/test_ibgp_ecmp_topo2.py +++ b/tests/topotests/bgp-ecmp-topo2/test_ibgp_ecmp_topo2.py @@ -61,6 +61,7 @@ from lib.common_config import ( check_address_types, interface_status, reset_config_on_routers, + required_linux_kernel_version ) from lib.topolog import logger from lib.bgp import verify_bgp_convergence, create_router_bgp, clear_bgp @@ -108,6 +109,11 @@ def setup_module(mod): global NEXT_HOPS, INTF_LIST_R3, INTF_LIST_R2, TEST_STATIC global ADDR_TYPES + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version('4.15') + if result is not True: + pytest.skip("Kernel requirements are not met") + testsuite_run_time = time.asctime(time.localtime(time.time())) logger.info("Testsuite start time: {}".format(testsuite_run_time)) logger.info("=" * 40) @@ -140,7 +146,7 @@ def setup_module(mod): link_data = [ val - for links, val in topo["routers"]["r2"]["links"].iteritems() + for links, val in topo["routers"]["r2"]["links"].items() if "r3" in links ] for adt in ADDR_TYPES: @@ -157,7 +163,7 @@ def setup_module(mod): link_data = [ val - for links, val in topo["routers"]["r3"]["links"].iteritems() + for links, val in topo["routers"]["r3"]["links"].items() if "r2" in links ] INTF_LIST_R3 = [val["interface"].split("/")[0] for val in link_data] diff --git a/tests/topotests/bgp-evpn-mh/test_evpn_mh.py b/tests/topotests/bgp-evpn-mh/test_evpn_mh.py index fe28f79bd4..ee50a422a7 100755..100644 --- a/tests/topotests/bgp-evpn-mh/test_evpn_mh.py +++ b/tests/topotests/bgp-evpn-mh/test_evpn_mh.py @@ -384,7 +384,7 @@ def setup_module(module): # tgen.mininet_cli() # This is a sample of configuration loading. router_list = tgen.routers() - for rname, router in router_list.iteritems(): + for rname, router in router_list.items(): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ) @@ -416,7 +416,7 @@ def check_local_es(esi, vtep_ips, dut_name, down_vteps): else: tor_ips_rack = tor_ips_rack_2 - for tor_name, tor_ip in tor_ips_rack.iteritems(): + for tor_name, tor_ip in tor_ips_rack.items(): if dut_name not in tor_name: peer_ips.append(tor_ip) @@ -442,7 +442,7 @@ def check_remote_es(esi, vtep_ips, dut_name, down_vteps): else: tor_ips_rack = tor_ips_rack_1 - for tor_name, tor_ip in tor_ips_rack.iteritems(): + for tor_name, tor_ip in tor_ips_rack.items(): remote_ips.append(tor_ip) # remove down VTEPs from the remote check list @@ -464,7 +464,7 @@ def check_es(dut): result = None - expected_es_set = set([v for k, v in host_es_map.iteritems()]) + expected_es_set = set([v for k, v in host_es_map.items()]) curr_es_set = [] # check is ES content is correct @@ -588,7 +588,7 @@ def check_mac(dut, vni, mac, m_type, esi, intf): out = dut.vtysh_cmd("show evpn mac vni %d mac %s json" % (vni, mac)) mac_js = json.loads(out) - for mac, info in mac_js.iteritems(): + for mac, info in mac_js.items(): tmp_esi = info.get("esi", "") tmp_m_type = info.get("type", "") tmp_intf = info.get("intf", "") if tmp_m_type == "local" else "" diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/test_bgp_evpn_vxlan.py b/tests/topotests/bgp-evpn-vxlan_topo1/test_bgp_evpn_vxlan.py index 90144f5c66..2a14105383 100755..100644 --- a/tests/topotests/bgp-evpn-vxlan_topo1/test_bgp_evpn_vxlan.py +++ b/tests/topotests/bgp-evpn-vxlan_topo1/test_bgp_evpn_vxlan.py @@ -122,7 +122,7 @@ def setup_module(mod): router_list = tgen.routers() # For all registred routers, load the zebra configuration file - for rname, router in router_list.iteritems(): + for rname, router in router_list.items(): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ) diff --git a/tests/topotests/bgp-vrf-route-leak-basic/test_bgp-vrf-route-leak-basic.py b/tests/topotests/bgp-vrf-route-leak-basic/test_bgp-vrf-route-leak-basic.py index 6178bfc63a..5aa1bdf329 100755..100644 --- a/tests/topotests/bgp-vrf-route-leak-basic/test_bgp-vrf-route-leak-basic.py +++ b/tests/topotests/bgp-vrf-route-leak-basic/test_bgp-vrf-route-leak-basic.py @@ -57,7 +57,7 @@ def setup_module(mod): tgen.start_topology() # For all registered routers, load the zebra configuration file - for rname, router in tgen.routers().iteritems(): + for rname, router in tgen.routers().items(): router.run("/bin/bash {}/setup_vrfs".format(CWD)) router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) diff --git a/tests/topotests/bgp_aggregate-address_origin/r1/bgpd.conf b/tests/topotests/bgp_aggregate-address_origin/r1/bgpd.conf index 9d519fae88..3486c64c55 100644 --- a/tests/topotests/bgp_aggregate-address_origin/r1/bgpd.conf +++ b/tests/topotests/bgp_aggregate-address_origin/r1/bgpd.conf @@ -1,6 +1,7 @@ router bgp 65000 no bgp ebgp-requires-policy neighbor 192.168.255.2 remote-as 65001 + neighbor 192.168.255.2 timers 3 10 address-family ipv4 unicast redistribute connected aggregate-address 172.16.255.0/24 origin igp diff --git a/tests/topotests/bgp_aggregate-address_origin/r2/bgpd.conf b/tests/topotests/bgp_aggregate-address_origin/r2/bgpd.conf index 38cf5aaca7..b2d945583c 100644 --- a/tests/topotests/bgp_aggregate-address_origin/r2/bgpd.conf +++ b/tests/topotests/bgp_aggregate-address_origin/r2/bgpd.conf @@ -1,5 +1,6 @@ router bgp 65001 no bgp ebgp-requires-policy neighbor 192.168.255.1 remote-as 65000 + neighbor 192.168.255.1 timers 3 10 exit-address-family ! diff --git a/tests/topotests/bgp_aggregate-address_origin/test_bgp_aggregate-address_origin.py b/tests/topotests/bgp_aggregate-address_origin/test_bgp_aggregate-address_origin.py index fa799f8256..86fd4b601f 100644 --- a/tests/topotests/bgp_aggregate-address_origin/test_bgp_aggregate-address_origin.py +++ b/tests/topotests/bgp_aggregate-address_origin/test_bgp_aggregate-address_origin.py @@ -66,7 +66,7 @@ def setup_module(mod): router_list = tgen.routers() - for i, (rname, router) in enumerate(router_list.iteritems(), 1): + for i, (rname, router) in enumerate(router_list.items(), 1): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ) diff --git a/tests/topotests/bgp_aggregate-address_route-map/r1/bgpd.conf b/tests/topotests/bgp_aggregate-address_route-map/r1/bgpd.conf index 292f0e967f..7fb55cf001 100644 --- a/tests/topotests/bgp_aggregate-address_route-map/r1/bgpd.conf +++ b/tests/topotests/bgp_aggregate-address_route-map/r1/bgpd.conf @@ -1,6 +1,7 @@ router bgp 65000 no bgp ebgp-requires-policy neighbor 192.168.255.2 remote-as 65001 + neighbor 192.168.255.2 timers 3 10 address-family ipv4 unicast redistribute connected aggregate-address 172.16.255.0/24 route-map aggr-rmap diff --git a/tests/topotests/bgp_aggregate-address_route-map/r2/bgpd.conf b/tests/topotests/bgp_aggregate-address_route-map/r2/bgpd.conf index 38cf5aaca7..b2d945583c 100644 --- a/tests/topotests/bgp_aggregate-address_route-map/r2/bgpd.conf +++ b/tests/topotests/bgp_aggregate-address_route-map/r2/bgpd.conf @@ -1,5 +1,6 @@ router bgp 65001 no bgp ebgp-requires-policy neighbor 192.168.255.1 remote-as 65000 + neighbor 192.168.255.1 timers 3 10 exit-address-family ! diff --git a/tests/topotests/bgp_aggregate-address_route-map/test_bgp_aggregate-address_route-map.py b/tests/topotests/bgp_aggregate-address_route-map/test_bgp_aggregate-address_route-map.py index 9c06c9d382..c7d9f13f3f 100644 --- a/tests/topotests/bgp_aggregate-address_route-map/test_bgp_aggregate-address_route-map.py +++ b/tests/topotests/bgp_aggregate-address_route-map/test_bgp_aggregate-address_route-map.py @@ -69,7 +69,7 @@ def setup_module(mod): router_list = tgen.routers() - for i, (rname, router) in enumerate(router_list.iteritems(), 1): + for i, (rname, router) in enumerate(router_list.items(), 1): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ) diff --git a/tests/topotests/bgp_as_allow_in/test_bgp_as_allow_in.py b/tests/topotests/bgp_as_allow_in/test_bgp_as_allow_in.py index 89b15c46d3..f9d22a3a36 100755 --- a/tests/topotests/bgp_as_allow_in/test_bgp_as_allow_in.py +++ b/tests/topotests/bgp_as_allow_in/test_bgp_as_allow_in.py @@ -65,6 +65,7 @@ from lib.common_config import ( create_route_maps, check_address_types, step, + required_linux_kernel_version ) from lib.topolog import logger from lib.bgp import ( @@ -112,6 +113,11 @@ def setup_module(mod): * `mod`: module name """ + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version('4.15') + if result is not True: + pytest.skip("Kernel requirements are not met") + testsuite_run_time = time.asctime(time.localtime(time.time())) logger.info("Testsuite start time: {}".format(testsuite_run_time)) logger.info("=" * 40) diff --git a/tests/topotests/bgp_as_wide_bgp_identifier/r1/bgpd.conf b/tests/topotests/bgp_as_wide_bgp_identifier/r1/bgpd.conf index 75741a3c3e..cb04749b92 100644 --- a/tests/topotests/bgp_as_wide_bgp_identifier/r1/bgpd.conf +++ b/tests/topotests/bgp_as_wide_bgp_identifier/r1/bgpd.conf @@ -3,4 +3,5 @@ router bgp 65001 bgp router-id 10.10.10.10 no bgp ebgp-requires-policy neighbor 192.168.255.1 remote-as 65002 + neighbor 192.168.255.1 timers 3 10 ! diff --git a/tests/topotests/bgp_as_wide_bgp_identifier/r2/bgpd.conf b/tests/topotests/bgp_as_wide_bgp_identifier/r2/bgpd.conf index 18a6c66f69..bed68858fd 100644 --- a/tests/topotests/bgp_as_wide_bgp_identifier/r2/bgpd.conf +++ b/tests/topotests/bgp_as_wide_bgp_identifier/r2/bgpd.conf @@ -3,5 +3,7 @@ router bgp 65002 bgp router-id 10.10.10.10 no bgp ebgp-requires-policy neighbor 192.168.255.2 remote-as 65001 + neighbor 192.168.255.2 timers 3 10 neighbor 192.168.255.3 remote-as 65002 + neighbor 192.168.255.3 timers 3 10 ! diff --git a/tests/topotests/bgp_as_wide_bgp_identifier/r3/bgpd.conf b/tests/topotests/bgp_as_wide_bgp_identifier/r3/bgpd.conf index 27bf126000..384e617d02 100644 --- a/tests/topotests/bgp_as_wide_bgp_identifier/r3/bgpd.conf +++ b/tests/topotests/bgp_as_wide_bgp_identifier/r3/bgpd.conf @@ -3,4 +3,5 @@ router bgp 65002 bgp router-id 10.10.10.10 no bgp ebgp-requires-policy neighbor 192.168.255.1 remote-as 65002 + neighbor 192.168.255.1 timers 3 10 ! diff --git a/tests/topotests/bgp_as_wide_bgp_identifier/test_bgp_as_wide_bgp_identifier.py b/tests/topotests/bgp_as_wide_bgp_identifier/test_bgp_as_wide_bgp_identifier.py index 459af486ff..02edb62ca0 100644 --- a/tests/topotests/bgp_as_wide_bgp_identifier/test_bgp_as_wide_bgp_identifier.py +++ b/tests/topotests/bgp_as_wide_bgp_identifier/test_bgp_as_wide_bgp_identifier.py @@ -65,7 +65,7 @@ def setup_module(mod): router_list = tgen.routers() - for i, (rname, router) in enumerate(router_list.iteritems(), 1): + for i, (rname, router) in enumerate(router_list.items(), 1): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ) diff --git a/tests/topotests/bgp_comm-list_delete/r1/bgpd.conf b/tests/topotests/bgp_comm-list_delete/r1/bgpd.conf index 9518894351..12161d2fa2 100644 --- a/tests/topotests/bgp_comm-list_delete/r1/bgpd.conf +++ b/tests/topotests/bgp_comm-list_delete/r1/bgpd.conf @@ -1,6 +1,7 @@ router bgp 65000 no bgp ebgp-requires-policy neighbor 192.168.255.2 remote-as 65001 + neighbor 192.168.255.2 timers 3 10 address-family ipv4 unicast redistribute connected route-map r2-out exit-address-family diff --git a/tests/topotests/bgp_comm-list_delete/r2/bgpd.conf b/tests/topotests/bgp_comm-list_delete/r2/bgpd.conf index e4c1167745..33231b5274 100644 --- a/tests/topotests/bgp_comm-list_delete/r2/bgpd.conf +++ b/tests/topotests/bgp_comm-list_delete/r2/bgpd.conf @@ -1,6 +1,7 @@ router bgp 65001 no bgp ebgp-requires-policy neighbor 192.168.255.1 remote-as 65000 + neighbor 192.168.255.1 timers 3 10 address-family ipv4 neighbor 192.168.255.1 route-map r1-in in exit-address-family diff --git a/tests/topotests/bgp_comm-list_delete/test_bgp_comm-list_delete.py b/tests/topotests/bgp_comm-list_delete/test_bgp_comm-list_delete.py index 314ad12a6d..fe7052b80f 100644 --- a/tests/topotests/bgp_comm-list_delete/test_bgp_comm-list_delete.py +++ b/tests/topotests/bgp_comm-list_delete/test_bgp_comm-list_delete.py @@ -64,7 +64,7 @@ def setup_module(mod): router_list = tgen.routers() - for i, (rname, router) in enumerate(router_list.iteritems(), 1): + for i, (rname, router) in enumerate(router_list.items(), 1): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ) diff --git a/tests/topotests/bgp_communities_topo1/test_bgp_communities.py b/tests/topotests/bgp_communities_topo1/test_bgp_communities.py index 7d960d6916..57e8e0d34a 100644 --- a/tests/topotests/bgp_communities_topo1/test_bgp_communities.py +++ b/tests/topotests/bgp_communities_topo1/test_bgp_communities.py @@ -54,6 +54,7 @@ from lib.common_config import ( create_route_maps, create_prefix_lists, create_route_maps, + required_linux_kernel_version ) from lib.topolog import logger from lib.bgp import ( @@ -102,6 +103,11 @@ def setup_module(mod): * `mod`: module name """ + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version('4.15') + if result is not True: + pytest.skip("Kernel requirements are not met") + testsuite_run_time = time.asctime(time.localtime(time.time())) logger.info("Testsuite start time: {}".format(testsuite_run_time)) logger.info("=" * 40) diff --git a/tests/topotests/bgp_default-route_route-map/r1/bgpd.conf b/tests/topotests/bgp_default-route_route-map/r1/bgpd.conf index 12e56e27c4..cb07ea9fdf 100644 --- a/tests/topotests/bgp_default-route_route-map/r1/bgpd.conf +++ b/tests/topotests/bgp_default-route_route-map/r1/bgpd.conf @@ -1,6 +1,7 @@ router bgp 65000 no bgp ebgp-requires-policy neighbor 192.168.255.2 remote-as 65001 + neighbor 192.168.255.2 timers 3 10 address-family ipv4 unicast neighbor 192.168.255.2 default-originate route-map default exit-address-family diff --git a/tests/topotests/bgp_default-route_route-map/r2/bgpd.conf b/tests/topotests/bgp_default-route_route-map/r2/bgpd.conf index b6b560aa4d..00c96cc58b 100644 --- a/tests/topotests/bgp_default-route_route-map/r2/bgpd.conf +++ b/tests/topotests/bgp_default-route_route-map/r2/bgpd.conf @@ -1,6 +1,7 @@ router bgp 65001 no bgp ebgp-requires-policy neighbor 192.168.255.1 remote-as 65000 + neighbor 192.168.255.1 timers 3 10 address-family ipv4 unicast redistribute connected exit-address-family diff --git a/tests/topotests/bgp_default-route_route-map/test_bgp_default-originate_route-map.py b/tests/topotests/bgp_default-route_route-map/test_bgp_default-originate_route-map.py index ba9a6dffb5..a72c3a4cbf 100644 --- a/tests/topotests/bgp_default-route_route-map/test_bgp_default-originate_route-map.py +++ b/tests/topotests/bgp_default-route_route-map/test_bgp_default-originate_route-map.py @@ -69,7 +69,7 @@ def setup_module(mod): router_list = tgen.routers() - for i, (rname, router) in enumerate(router_list.iteritems(), 1): + for i, (rname, router) in enumerate(router_list.items(), 1): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ) diff --git a/tests/topotests/bgp_distance_change/r1/bgpd.conf b/tests/topotests/bgp_distance_change/r1/bgpd.conf index cd2ef675fc..07cfe2e2f1 100644 --- a/tests/topotests/bgp_distance_change/r1/bgpd.conf +++ b/tests/topotests/bgp_distance_change/r1/bgpd.conf @@ -1,5 +1,6 @@ router bgp 65000 no bgp ebgp-requires-policy neighbor 192.168.255.2 remote-as 65001 + neighbor 192.168.255.2 timers 3 10 exit-address-family ! diff --git a/tests/topotests/bgp_distance_change/r2/bgpd.conf b/tests/topotests/bgp_distance_change/r2/bgpd.conf index 0faec85032..9cd86dc284 100644 --- a/tests/topotests/bgp_distance_change/r2/bgpd.conf +++ b/tests/topotests/bgp_distance_change/r2/bgpd.conf @@ -1,6 +1,7 @@ router bgp 65001 no bgp ebgp-requires-policy neighbor 192.168.255.1 remote-as 65000 + neighbor 192.168.255.1 timers 3 10 address-family ipv4 redistribute connected exit-address-family diff --git a/tests/topotests/bgp_distance_change/test_bgp_distance_change.py b/tests/topotests/bgp_distance_change/test_bgp_distance_change.py index 6d09cd2e8c..f338d52e70 100644 --- a/tests/topotests/bgp_distance_change/test_bgp_distance_change.py +++ b/tests/topotests/bgp_distance_change/test_bgp_distance_change.py @@ -68,7 +68,7 @@ def setup_module(mod): router_list = tgen.routers() - for i, (rname, router) in enumerate(router_list.iteritems(), 1): + for i, (rname, router) in enumerate(router_list.items(), 1): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ) diff --git a/tests/topotests/bgp_ebgp_requires_policy/r1/bgpd.conf b/tests/topotests/bgp_ebgp_requires_policy/r1/bgpd.conf index aaa01ebcf9..add37ee58d 100644 --- a/tests/topotests/bgp_ebgp_requires_policy/r1/bgpd.conf +++ b/tests/topotests/bgp_ebgp_requires_policy/r1/bgpd.conf @@ -1,5 +1,6 @@ router bgp 65000 neighbor 192.168.255.2 remote-as 1000 + neighbor 192.168.255.2 timers 3 10 neighbor 192.168.255.2 local-as 500 address-family ipv4 unicast redistribute connected diff --git a/tests/topotests/bgp_ebgp_requires_policy/r2/bgpd.conf b/tests/topotests/bgp_ebgp_requires_policy/r2/bgpd.conf index 27427a9aaa..802c32714b 100644 --- a/tests/topotests/bgp_ebgp_requires_policy/r2/bgpd.conf +++ b/tests/topotests/bgp_ebgp_requires_policy/r2/bgpd.conf @@ -1,3 +1,5 @@ router bgp 1000 no bgp ebgp-requires-policy neighbor 192.168.255.1 remote-as 500 + neighbor 192.168.255.1 timers 3 10 +! diff --git a/tests/topotests/bgp_ebgp_requires_policy/r3/bgpd.conf b/tests/topotests/bgp_ebgp_requires_policy/r3/bgpd.conf index 2deb4b663d..1280b427ad 100644 --- a/tests/topotests/bgp_ebgp_requires_policy/r3/bgpd.conf +++ b/tests/topotests/bgp_ebgp_requires_policy/r3/bgpd.conf @@ -1,5 +1,6 @@ router bgp 65000 neighbor 192.168.255.2 remote-as 1000 + neighbor 192.168.255.2 timers 3 10 neighbor 192.168.255.2 local-as 500 address-family ipv4 unicast redistribute connected diff --git a/tests/topotests/bgp_ebgp_requires_policy/r4/bgpd.conf b/tests/topotests/bgp_ebgp_requires_policy/r4/bgpd.conf index 27427a9aaa..802c32714b 100644 --- a/tests/topotests/bgp_ebgp_requires_policy/r4/bgpd.conf +++ b/tests/topotests/bgp_ebgp_requires_policy/r4/bgpd.conf @@ -1,3 +1,5 @@ router bgp 1000 no bgp ebgp-requires-policy neighbor 192.168.255.1 remote-as 500 + neighbor 192.168.255.1 timers 3 10 +! diff --git a/tests/topotests/bgp_ebgp_requires_policy/r5/bgpd.conf b/tests/topotests/bgp_ebgp_requires_policy/r5/bgpd.conf index 92a2797921..60b29f6f1b 100644 --- a/tests/topotests/bgp_ebgp_requires_policy/r5/bgpd.conf +++ b/tests/topotests/bgp_ebgp_requires_policy/r5/bgpd.conf @@ -1,6 +1,7 @@ router bgp 65000 no bgp ebgp-requires-policy neighbor 192.168.255.2 remote-as 65000 + neighbor 192.168.255.2 timers 3 10 address-family ipv4 unicast redistribute connected ! diff --git a/tests/topotests/bgp_ebgp_requires_policy/r6/bgpd.conf b/tests/topotests/bgp_ebgp_requires_policy/r6/bgpd.conf index 342f53d4c7..7ad5d927a7 100644 --- a/tests/topotests/bgp_ebgp_requires_policy/r6/bgpd.conf +++ b/tests/topotests/bgp_ebgp_requires_policy/r6/bgpd.conf @@ -1,2 +1,4 @@ router bgp 65000 neighbor 192.168.255.1 remote-as 65000 + neighbor 192.168.255.1 timers 3 10 +! diff --git a/tests/topotests/bgp_ebgp_requires_policy/test_bgp_ebgp_requires_policy.py b/tests/topotests/bgp_ebgp_requires_policy/test_bgp_ebgp_requires_policy.py index 5c2af2b30b..2520763bda 100644 --- a/tests/topotests/bgp_ebgp_requires_policy/test_bgp_ebgp_requires_policy.py +++ b/tests/topotests/bgp_ebgp_requires_policy/test_bgp_ebgp_requires_policy.py @@ -82,7 +82,7 @@ def setup_module(mod): router_list = tgen.routers() - for i, (rname, router) in enumerate(router_list.iteritems(), 1): + for i, (rname, router) in enumerate(router_list.items(), 1): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ) diff --git a/tests/topotests/bgp_evpn_rt5/__init__.py b/tests/topotests/bgp_evpn_rt5/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/bgp_evpn_rt5/__init__.py diff --git a/tests/topotests/bgp_evpn_rt5/r1/bgpd.conf b/tests/topotests/bgp_evpn_rt5/r1/bgpd.conf new file mode 100644 index 0000000000..9237682067 --- /dev/null +++ b/tests/topotests/bgp_evpn_rt5/r1/bgpd.conf @@ -0,0 +1,26 @@ +debug bgp neighbor-events +debug bgp updates +debug bgp zebra +router bgp 65000 + bgp router-id 192.168.100.21 + bgp log-neighbor-changes + no bgp default ipv4-unicast + neighbor 192.168.100.41 remote-as 65000 + neighbor 192.168.100.41 capability extended-nexthop + ! + address-family l2vpn evpn + neighbor 192.168.100.41 activate + advertise-all-vni + exit-address-family +! +router bgp 65000 vrf r1-vrf-101 + bgp router-id 192.168.102.21 + bgp log-neighbor-changes + no bgp network import-check + address-family ipv4 unicast + network 192.168.102.21/32 + exit-address-family + address-family l2vpn evpn + advertise ipv4 unicast + exit-address-family + ! diff --git a/tests/topotests/bgp_evpn_rt5/r1/zebra.conf b/tests/topotests/bgp_evpn_rt5/r1/zebra.conf new file mode 100644 index 0000000000..f5eaab1953 --- /dev/null +++ b/tests/topotests/bgp_evpn_rt5/r1/zebra.conf @@ -0,0 +1,22 @@ +log stdout + +hostname r1 +password zebra + +debug zebra vxlan +debug zebra kernel +debug zebra dplane +debug zebra rib +log stdout +vrf r1-vrf-101 + vni 101 + exit-vrf +! +interface r1-eth0 + ip address 192.168.100.21/24 +! +interface loop101 vrf r1-vrf-101 + ip address 192.168.102.21/32 +! + + diff --git a/tests/topotests/bgp_evpn_rt5/r2/bgpd.conf b/tests/topotests/bgp_evpn_rt5/r2/bgpd.conf new file mode 100644 index 0000000000..6dcacd288d --- /dev/null +++ b/tests/topotests/bgp_evpn_rt5/r2/bgpd.conf @@ -0,0 +1,27 @@ +debug bgp neighbor-events +debug bgp updates +debug bgp zebra +router bgp 65000 + bgp router-id 192.168.100.41 + bgp log-neighbor-changes + no bgp default ipv4-unicast + neighbor 192.168.100.21 peer-group + neighbor 192.168.100.21 remote-as 65000 + neighbor 192.168.100.21 capability extended-nexthop + ! + address-family l2vpn evpn + neighbor 192.168.100.21 activate + advertise-all-vni + exit-address-family +! +router bgp 65000 vrf r2-vrf-101 + bgp router-id 192.168.101.41 + bgp log-neighbor-changes + no bgp network import-check + address-family ipv4 unicast + network 192.168.101.41/32 + exit-address-family + address-family l2vpn evpn + advertise ipv4 unicast + exit-address-family + ! diff --git a/tests/topotests/bgp_evpn_rt5/r2/zebra.conf b/tests/topotests/bgp_evpn_rt5/r2/zebra.conf new file mode 100644 index 0000000000..e5f962d254 --- /dev/null +++ b/tests/topotests/bgp_evpn_rt5/r2/zebra.conf @@ -0,0 +1,18 @@ +log stdout + +hostname r2 +password zebra + +debug zebra vxlan + +vrf r2-vrf-101 + vni 101 + exit-vrf +! +interface loop101 vrf r2-vrf-101 + ip address 192.168.101.41/32 +! +interface r2-eth0 + ip address 192.168.100.41/24 +! + diff --git a/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py b/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py new file mode 100755 index 0000000000..69ef7e9fae --- /dev/null +++ b/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py @@ -0,0 +1,234 @@ +#!/usr/bin/env python + +# +# test_bgp_evpn.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2019 by 6WIND +# +# 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_bgp_evpn.py: Test the FRR/Quagga BGP daemon with BGP IPv6 interface + with route advertisements on a separate netns. +""" + +import os +import sys +import json +from functools import partial +import pytest +import platform + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, '../')) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + +l3mdev_accept = 0 +krel = '' + +class BGPEVPNTopo(Topo): + "Test topology builder" + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + tgen.add_router('r1') + tgen.add_router('r2') + + switch = tgen.add_switch('s1') + switch.add_link(tgen.gears['r1']) + switch.add_link(tgen.gears['r2']) + + switch = tgen.add_switch('s2') + switch.add_link(tgen.gears['r1']) + + switch = tgen.add_switch('s3') + switch.add_link(tgen.gears['r2']) + +def setup_module(mod): + "Sets up the pytest environment" + global l3mdev_accept + global krel + + tgen = Topogen(BGPEVPNTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + krel = platform.release() + if topotest.version_cmp(krel, '4.18') < 0: + logger.info('BGP EVPN RT5 NETNS tests will not run (have kernel "{}", but it requires 4.18)'.format(krel)) + return pytest.skip('Skipping BGP EVPN RT5 NETNS Test. Kernel not supported') + + l3mdev_accept = 1 + logger.info('setting net.ipv4.tcp_l3mdev_accept={}'.format(l3mdev_accept)) + + # create VRF vrf-101 on R1 and R2 + # create loop101 + cmds_vrflite = ['sysctl -w net.ipv4.tcp_l3mdev_accept={}'.format(l3mdev_accept), + 'ip link add {}-vrf-101 type vrf table 101', + 'ip ru add oif {}-vrf-101 table 101', + 'ip ru add iif {}-vrf-101 table 101', + 'ip link set dev {}-vrf-101 up', + 'sysctl -w net.ipv4.tcp_l3mdev_accept={}'.format(l3mdev_accept), + 'ip link add loop101 type dummy', + 'ip link set dev loop101 master {}-vrf-101', + 'ip link set dev loop101 up'] + cmds_netns = ['ip netns add {}-vrf-101', + 'ip link add loop101 type dummy', + 'ip link set dev loop101 netns {}-vrf-101', + 'ip netns exec {}-vrf-101 ip link set dev loop101 up'] + + cmds_r2 = [ # config routing 101 + 'ip link add name bridge-101 up type bridge stp_state 0', + 'ip link set bridge-101 master {}-vrf-101', + 'ip link set dev bridge-101 up', + 'ip link add name vxlan-101 type vxlan id 101 dstport 4789 dev r2-eth0 local 192.168.100.41', + 'ip link set dev vxlan-101 master bridge-101', + 'ip link set vxlan-101 up type bridge_slave learning off flood off mcast_flood off'] + + cmds_r1_netns_method3 = ['ip link add name vxlan-{1} type vxlan id {1} dstport 4789 dev {0}-eth0 local 192.168.100.21', + 'ip link set dev vxlan-{1} netns {0}-vrf-{1}', + 'ip netns exec {0}-vrf-{1} ip li set dev lo up', + 'ip netns exec {0}-vrf-{1} ip link add name bridge-{1} up type bridge stp_state 0', + 'ip netns exec {0}-vrf-{1} ip link set dev vxlan-{1} master bridge-{1}', + 'ip netns exec {0}-vrf-{1} ip link set bridge-{1} up', + 'ip netns exec {0}-vrf-{1} ip link set vxlan-{1} up'] + + router = tgen.gears['r1'] + for cmd in cmds_netns: + logger.info('cmd to r1: '+cmd); + output = router.run(cmd.format('r1')) + logger.info('result: '+output); + + router = tgen.gears['r2'] + for cmd in cmds_vrflite: + logger.info('cmd to r2: '+cmd.format('r2')); + output = router.run(cmd.format('r2')) + logger.info('result: '+output); + + for cmd in cmds_r2: + logger.info('cmd to r2: '+cmd.format('r2')); + output = router.run(cmd.format('r2')) + logger.info('result: '+output); + + router = tgen.gears['r1'] + bridge_id = '101' + for cmd in cmds_r1_netns_method3: + logger.info('cmd to r1: '+cmd.format('r1', bridge_id)); + output = router.run(cmd.format('r1', bridge_id)) + logger.info('result: '+output); + router = tgen.gears['r1'] + + for rname, router in router_list.iteritems(): + if rname == 'r1': + router.load_config( + TopoRouter.RD_ZEBRA, + os.path.join(CWD, '{}/zebra.conf'.format(rname)), + '--vrfwnetns -o vrf0' + ) + else: + 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)) + ) + + # Initialize all routers. + tgen.start_router() + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + cmds_rx_netns = ['ip netns del {}-vrf-101'] + + router = tgen.gears['r1'] + for cmd in cmds_rx_netns: + logger.info('cmd to r1: '+cmd.format('r1')); + output = router.run(cmd.format('r1')) + tgen.stop_topology() + + +def test_protocols_convergence(): + """ + Assert that all protocols have converged + statuses as they depend on it. + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + topotest.sleep(4, 'waiting 4 seconds for bgp convergence') + # Check IPv4/IPv6 routing tables. + output = tgen.gears['r1'].vtysh_cmd('show bgp l2vpn evpn', isjson=False) + logger.info('==== result from show bgp l2vpn evpn') + logger.info(output) + output = tgen.gears['r1'].vtysh_cmd('show bgp l2vpn evpn route detail', isjson=False) + logger.info('==== result from show bgp l2vpn evpn route detail') + logger.info(output) + output = tgen.gears['r1'].vtysh_cmd('show bgp vrf r1-vrf-101 ipv4', isjson=False) + logger.info('==== result from show bgp vrf r1-vrf-101 ipv4') + logger.info(output) + output = tgen.gears['r1'].vtysh_cmd('show bgp vrf r1-vrf-101', isjson=False) + logger.info('==== result from show bgp vrf r1-vrf-101 ') + logger.info(output) + output = tgen.gears['r1'].vtysh_cmd('show ip route vrf r1-vrf-101', isjson=False) + logger.info('==== result from show ip route vrf r1-vrf-101') + logger.info(output) + output = tgen.gears['r1'].vtysh_cmd('show evpn vni detail', isjson=False) + logger.info('==== result from show evpn vni detail') + logger.info(output) + output = tgen.gears['r1'].vtysh_cmd('show evpn next-hops vni all', isjson=False) + logger.info('==== result from show evpn next-hops vni all') + logger.info(output) + output = tgen.gears['r1'].vtysh_cmd('show evpn rmac vni all', isjson=False) + logger.info('==== result from show evpn next-hops vni all') + logger.info(output) + # Check IPv4 and IPv6 connectivity between r1 and r2 ( routing vxlan evpn) + pingrouter = tgen.gears['r1'] + logger.info('Check Ping IPv4 from R1(r1-vrf-101) to R2(r2-vrf-101 = 192.168.101.41)') + output = pingrouter.run('ip netns exec r1-vrf-101 ping 192.168.101.41 -f -c 1000') + logger.info(output) + if '1000 packets transmitted, 1000 received' not in output: + assertmsg = 'expected ping IPv4 from R1(r1-vrf-101) to R2(192.168.101.41) should be ok' + assert 0, assertmsg + else: + logger.info('Check Ping IPv4 from R1(r1-vrf-101) to R2(192.168.101.41) OK') + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip('Memory leak test/report is disabled') + + tgen.report_memory_leaks() + + +if __name__ == '__main__': + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_features/r1/bgpd.conf b/tests/topotests/bgp_features/r1/bgpd.conf index 7aea2dc161..74d1993af0 100644 --- a/tests/topotests/bgp_features/r1/bgpd.conf +++ b/tests/topotests/bgp_features/r1/bgpd.conf @@ -9,10 +9,12 @@ router bgp 65000 bgp log-neighbor-changes no bgp ebgp-requires-policy neighbor 192.168.0.2 remote-as 65000 + neighbor 192.168.0.2 timers 3 10 neighbor 192.168.0.2 description Router R2 (iBGP) neighbor 192.168.0.2 update-source lo neighbor 192.168.0.2 timers connect 5 neighbor 192.168.101.2 remote-as 65100 + neighbor 192.168.101.2 timers 3 10 neighbor 192.168.101.2 description Router R4 (eBGP AS 65100) neighbor 192.168.101.2 timers connect 5 ! diff --git a/tests/topotests/bgp_features/r2/bgpd.conf b/tests/topotests/bgp_features/r2/bgpd.conf index 3daf9842ce..99cec98811 100644 --- a/tests/topotests/bgp_features/r2/bgpd.conf +++ b/tests/topotests/bgp_features/r2/bgpd.conf @@ -9,10 +9,12 @@ router bgp 65000 bgp log-neighbor-changes no bgp ebgp-requires-policy neighbor 192.168.0.1 remote-as 65000 + neighbor 192.168.0.1 timers 3 10 neighbor 192.168.0.1 description Router R1 (iBGP) neighbor 192.168.0.1 update-source lo neighbor 192.168.0.1 timers connect 5 neighbor 192.168.201.2 remote-as 65200 + neighbor 192.168.201.2 timers 3 10 neighbor 192.168.201.2 description Router R5 (eBGP AS 65200) neighbor 192.168.201.2 timers connect 5 ! diff --git a/tests/topotests/bgp_features/r4/bgpd.conf b/tests/topotests/bgp_features/r4/bgpd.conf index fe1a4d4ffe..cdf8f4e8f6 100644 --- a/tests/topotests/bgp_features/r4/bgpd.conf +++ b/tests/topotests/bgp_features/r4/bgpd.conf @@ -9,6 +9,7 @@ router bgp 65100 bgp log-neighbor-changes no bgp ebgp-requires-policy neighbor 192.168.101.1 remote-as 65000 + neighbor 192.168.101.1 timers 3 10 neighbor 192.168.101.1 description Router R1 (eBGP AS 65000) neighbor 192.168.101.1 timers connect 5 ! diff --git a/tests/topotests/bgp_features/r5/bgpd.conf b/tests/topotests/bgp_features/r5/bgpd.conf index 8504213b41..6368fedd39 100644 --- a/tests/topotests/bgp_features/r5/bgpd.conf +++ b/tests/topotests/bgp_features/r5/bgpd.conf @@ -9,6 +9,7 @@ router bgp 65200 bgp log-neighbor-changes no bgp ebgp-requires-policy neighbor 192.168.201.1 remote-as 65000 + neighbor 192.168.201.1 timers 3 10 neighbor 192.168.201.1 description Router R2 (eBGP AS 65000) neighbor 192.168.201.1 timers connect 5 ! diff --git a/tests/topotests/bgp_features/test_bgp_features.py b/tests/topotests/bgp_features/test_bgp_features.py index 21cc8d227b..7c45ca8a29 100755..100644 --- a/tests/topotests/bgp_features/test_bgp_features.py +++ b/tests/topotests/bgp_features/test_bgp_features.py @@ -102,7 +102,7 @@ def setup_module(module): # Starting Routers router_list = tgen.routers() - for rname, router in router_list.iteritems(): + for rname, router in router_list.items(): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ) diff --git a/tests/topotests/bgp_flowspec/r1/bgpd.conf b/tests/topotests/bgp_flowspec/r1/bgpd.conf index 6dc91b24a9..4b7a20f958 100644 --- a/tests/topotests/bgp_flowspec/r1/bgpd.conf +++ b/tests/topotests/bgp_flowspec/r1/bgpd.conf @@ -5,6 +5,7 @@ log stdout debugging router bgp 100 bgp router-id 10.0.1.1 neighbor 10.0.1.101 remote-as 100 + neighbor 10.0.1.101 timers 3 10 neighbor 10.0.1.101 update-source 10.0.1.1 address-family ipv6 flowspec local-install r1-eth0 diff --git a/tests/topotests/bgp_flowspec/test_bgp_flowspec_topo.py b/tests/topotests/bgp_flowspec/test_bgp_flowspec_topo.py index a7e2c31cde..7e6bfc8b2b 100755..100644 --- a/tests/topotests/bgp_flowspec/test_bgp_flowspec_topo.py +++ b/tests/topotests/bgp_flowspec/test_bgp_flowspec_topo.py @@ -124,7 +124,7 @@ def setup_module(module): router.start() peer_list = tgen.exabgp_peers() - for pname, peer in peer_list.iteritems(): + for pname, peer in peer_list.items(): peer_dir = os.path.join(CWD, pname) env_file = os.path.join(CWD, "exabgp.env") peer.start(peer_dir, env_file) diff --git a/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1.py b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1.py index fdc1bed522..fdbd317093 100755 --- a/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1.py +++ b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1.py @@ -135,6 +135,7 @@ from lib.common_config import ( kill_mininet_routers_process, get_frr_ipv6_linklocal, create_route_maps, + required_linux_kernel_version ) # Reading the data from JSON File for topology and configuration creation @@ -186,6 +187,11 @@ def setup_module(mod): global ADDR_TYPES + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version('4.15') + if result is not True: + pytest.skip("Kernel requirements are not met") + testsuite_run_time = time.asctime(time.localtime(time.time())) logger.info("Testsuite start time: {}".format(testsuite_run_time)) logger.info("=" * 40) 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 83a04f491f..e1ec0ea81b 100755 --- 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 @@ -135,6 +135,7 @@ from lib.common_config import ( kill_mininet_routers_process, get_frr_ipv6_linklocal, create_route_maps, + required_linux_kernel_version ) # Reading the data from JSON File for topology and configuration creation @@ -183,6 +184,11 @@ def setup_module(mod): * `mod`: module name """ + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version('4.15') + if result is not True: + pytest.skip("Kernel requirements are not met") + global ADDR_TYPES testsuite_run_time = time.asctime(time.localtime(time.time())) diff --git a/tests/topotests/bgp_ipv6_rtadv/r1/bgpd.conf b/tests/topotests/bgp_ipv6_rtadv/r1/bgpd.conf index 4d96bec2cb..0918796e95 100644 --- a/tests/topotests/bgp_ipv6_rtadv/r1/bgpd.conf +++ b/tests/topotests/bgp_ipv6_rtadv/r1/bgpd.conf @@ -5,6 +5,7 @@ router bgp 101 neighbor r2g remote-as external neighbor r2g bfd neighbor r1-eth0 interface peer-group r2g + neighbor r1-eth0 timers 3 10 address-family ipv4 unicast redistribute connected exit-address-family diff --git a/tests/topotests/bgp_ipv6_rtadv/r2/bgpd.conf b/tests/topotests/bgp_ipv6_rtadv/r2/bgpd.conf index 4d02fc4f29..55d48560e7 100644 --- a/tests/topotests/bgp_ipv6_rtadv/r2/bgpd.conf +++ b/tests/topotests/bgp_ipv6_rtadv/r2/bgpd.conf @@ -5,6 +5,7 @@ router bgp 102 neighbor r2g remote-as external neighbor r2g bfd neighbor r2-eth0 interface peer-group r2g + neighbor r2-eth0 timers 3 10 ! address-family ipv4 unicast redistribute connected diff --git a/tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.py b/tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.py index 10b2f3595f..0acf8d2dbc 100644 --- a/tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.py +++ b/tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.py @@ -69,7 +69,7 @@ def setup_module(mod): router_list = tgen.routers() - for rname, router in router_list.iteritems(): + for rname, router in router_list.items(): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ) diff --git a/tests/topotests/bgp_large_community/test_bgp_large_community_topo_1.py b/tests/topotests/bgp_large_community/test_bgp_large_community_topo_1.py index 334aaebb4b..dc06b7131a 100755 --- a/tests/topotests/bgp_large_community/test_bgp_large_community_topo_1.py +++ b/tests/topotests/bgp_large_community/test_bgp_large_community_topo_1.py @@ -67,6 +67,7 @@ from lib.common_config import ( verify_bgp_community, step, check_address_types, + required_linux_kernel_version ) from lib.topolog import logger from lib.bgp import verify_bgp_convergence, create_router_bgp, clear_bgp_and_verify @@ -142,6 +143,11 @@ def setup_module(mod): * `mod`: module name """ + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version('4.15') + if result is not True: + pytest.skip("Kernel requirements are not met") + global ADDR_TYPES testsuite_run_time = time.asctime(time.localtime(time.time())) logger.info("Testsuite start time: {}".format(testsuite_run_time)) diff --git a/tests/topotests/bgp_large_community/test_bgp_large_community_topo_2.py b/tests/topotests/bgp_large_community/test_bgp_large_community_topo_2.py index 502a9a9ec4..bb88e47415 100755 --- a/tests/topotests/bgp_large_community/test_bgp_large_community_topo_2.py +++ b/tests/topotests/bgp_large_community/test_bgp_large_community_topo_2.py @@ -91,6 +91,7 @@ from lib.common_config import ( verify_route_maps, create_static_routes, check_address_types, + required_linux_kernel_version ) from lib.topolog import logger from lib.bgp import verify_bgp_convergence, create_router_bgp, clear_bgp_and_verify @@ -133,6 +134,11 @@ def setup_module(mod): * `mod`: module name """ + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version('4.15') + if result is not True: + pytest.skip("Kernel requirements are not met") + testsuite_run_time = time.asctime(time.localtime(time.time())) logger.info("Testsuite start time: {}".format(testsuite_run_time)) logger.info("=" * 40) diff --git a/tests/topotests/bgp_link_bw_ip/r1/bgpd.conf b/tests/topotests/bgp_link_bw_ip/r1/bgpd.conf index 40c19062a2..0e46687100 100644 --- a/tests/topotests/bgp_link_bw_ip/r1/bgpd.conf +++ b/tests/topotests/bgp_link_bw_ip/r1/bgpd.conf @@ -5,5 +5,7 @@ router bgp 65101 no bgp ebgp-requires-policy bgp bestpath as-path multipath-relax neighbor 11.1.1.2 remote-as external + neighbor 11.1.1.2 timers 3 10 neighbor 11.1.1.6 remote-as external + neighbor 11.1.1.6 timers 3 10 ! diff --git a/tests/topotests/bgp_link_bw_ip/r10/bgpd.conf b/tests/topotests/bgp_link_bw_ip/r10/bgpd.conf index 80588e7961..022270ff8a 100644 --- a/tests/topotests/bgp_link_bw_ip/r10/bgpd.conf +++ b/tests/topotests/bgp_link_bw_ip/r10/bgpd.conf @@ -9,6 +9,7 @@ router bgp 65354 bgp router-id 11.1.6.2 no bgp ebgp-requires-policy neighbor 11.1.6.1 remote-as external + neighbor 11.1.6.1 timers 3 10 ! address-family ipv4 unicast redistribute connected route-map redist diff --git a/tests/topotests/bgp_link_bw_ip/r2/bgpd.conf b/tests/topotests/bgp_link_bw_ip/r2/bgpd.conf index 6fec1913c8..0c0e859cab 100644 --- a/tests/topotests/bgp_link_bw_ip/r2/bgpd.conf +++ b/tests/topotests/bgp_link_bw_ip/r2/bgpd.conf @@ -5,6 +5,9 @@ router bgp 65201 bgp bestpath as-path multipath-relax no bgp ebgp-requires-policy neighbor 11.1.1.1 remote-as external + neighbor 11.1.1.1 timers 3 10 neighbor 11.1.2.2 remote-as external + neighbor 11.1.2.2 timers 3 10 neighbor 11.1.2.6 remote-as external + neighbor 11.1.2.6 timers 3 10 ! diff --git a/tests/topotests/bgp_link_bw_ip/r3/bgpd.conf b/tests/topotests/bgp_link_bw_ip/r3/bgpd.conf index 1c2ca88306..3f20eb10a3 100644 --- a/tests/topotests/bgp_link_bw_ip/r3/bgpd.conf +++ b/tests/topotests/bgp_link_bw_ip/r3/bgpd.conf @@ -5,5 +5,7 @@ router bgp 65202 bgp bestpath as-path multipath-relax no bgp ebgp-requires-policy neighbor 11.1.1.5 remote-as external + neighbor 11.1.1.5 timers 3 10 neighbor 11.1.3.2 remote-as external + neighbor 11.1.3.2 timers 3 10 ! diff --git a/tests/topotests/bgp_link_bw_ip/r4/bgpd.conf b/tests/topotests/bgp_link_bw_ip/r4/bgpd.conf index 022a230643..d34db982c9 100644 --- a/tests/topotests/bgp_link_bw_ip/r4/bgpd.conf +++ b/tests/topotests/bgp_link_bw_ip/r4/bgpd.conf @@ -20,8 +20,11 @@ router bgp 65301 bgp bestpath as-path multipath-relax no bgp ebgp-requires-policy neighbor 11.1.2.1 remote-as external + neighbor 11.1.2.1 timers 3 10 neighbor 11.1.4.2 remote-as external + neighbor 11.1.4.2 timers 3 10 neighbor 11.1.4.6 remote-as external + neighbor 11.1.4.6 timers 3 10 ! address-family ipv4 unicast neighbor 11.1.2.1 route-map anycast_ip out diff --git a/tests/topotests/bgp_link_bw_ip/r5/bgpd.conf b/tests/topotests/bgp_link_bw_ip/r5/bgpd.conf index fc4e3888d8..4014bfbc8b 100644 --- a/tests/topotests/bgp_link_bw_ip/r5/bgpd.conf +++ b/tests/topotests/bgp_link_bw_ip/r5/bgpd.conf @@ -13,7 +13,9 @@ router bgp 65302 bgp bestpath as-path multipath-relax no bgp ebgp-requires-policy neighbor 11.1.2.5 remote-as external + neighbor 11.1.2.5 timers 3 10 neighbor 11.1.5.2 remote-as external + neighbor 11.1.5.2 timers 3 10 ! address-family ipv4 unicast neighbor 11.1.2.5 route-map anycast_ip out diff --git a/tests/topotests/bgp_link_bw_ip/r6/bgpd.conf b/tests/topotests/bgp_link_bw_ip/r6/bgpd.conf index f08f6337f4..18e7eb9285 100644 --- a/tests/topotests/bgp_link_bw_ip/r6/bgpd.conf +++ b/tests/topotests/bgp_link_bw_ip/r6/bgpd.conf @@ -13,7 +13,9 @@ router bgp 65303 bgp bestpath as-path multipath-relax no bgp ebgp-requires-policy neighbor 11.1.3.1 remote-as external + neighbor 11.1.3.1 timers 3 10 neighbor 11.1.6.2 remote-as external + neighbor 11.1.6.2 timers 3 10 ! address-family ipv4 unicast neighbor 11.1.3.1 route-map anycast_ip out diff --git a/tests/topotests/bgp_link_bw_ip/r7/bgpd.conf b/tests/topotests/bgp_link_bw_ip/r7/bgpd.conf index 98dfe2471a..39c0c81950 100644 --- a/tests/topotests/bgp_link_bw_ip/r7/bgpd.conf +++ b/tests/topotests/bgp_link_bw_ip/r7/bgpd.conf @@ -9,6 +9,7 @@ router bgp 65351 bgp router-id 11.1.4.2 no bgp ebgp-requires-policy neighbor 11.1.4.1 remote-as external + neighbor 11.1.4.1 timers 3 10 ! address-family ipv4 unicast redistribute connected route-map redist diff --git a/tests/topotests/bgp_link_bw_ip/r8/bgpd.conf b/tests/topotests/bgp_link_bw_ip/r8/bgpd.conf index b4ba8e8a72..ea1d546017 100644 --- a/tests/topotests/bgp_link_bw_ip/r8/bgpd.conf +++ b/tests/topotests/bgp_link_bw_ip/r8/bgpd.conf @@ -9,6 +9,7 @@ router bgp 65352 bgp router-id 11.1.4.6 no bgp ebgp-requires-policy neighbor 11.1.4.5 remote-as external + neighbor 11.1.4.5 timers 3 10 ! address-family ipv4 unicast redistribute connected route-map redist diff --git a/tests/topotests/bgp_link_bw_ip/r9/bgpd.conf b/tests/topotests/bgp_link_bw_ip/r9/bgpd.conf index 31f971dd08..a6066ee766 100644 --- a/tests/topotests/bgp_link_bw_ip/r9/bgpd.conf +++ b/tests/topotests/bgp_link_bw_ip/r9/bgpd.conf @@ -9,6 +9,7 @@ router bgp 65353 bgp router-id 11.1.5.2 no bgp ebgp-requires-policy neighbor 11.1.5.1 remote-as external + neighbor 11.1.5.1 timers 3 10 ! address-family ipv4 unicast redistribute connected route-map redist diff --git a/tests/topotests/bgp_link_bw_ip/test_bgp_linkbw_ip.py b/tests/topotests/bgp_link_bw_ip/test_bgp_linkbw_ip.py index 86eb2969ce..dff69e3a27 100755..100644 --- a/tests/topotests/bgp_link_bw_ip/test_bgp_linkbw_ip.py +++ b/tests/topotests/bgp_link_bw_ip/test_bgp_linkbw_ip.py @@ -119,7 +119,7 @@ def setup_module(mod): tgen.start_topology() router_list = tgen.routers() - for rname, router in router_list.iteritems(): + for rname, router in router_list.items(): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, '{}/zebra.conf'.format(rname)) diff --git a/tests/topotests/bgp_local_as_private_remove/r1/bgpd.conf b/tests/topotests/bgp_local_as_private_remove/r1/bgpd.conf index 6f8fcd753d..16a9eeadd1 100644 --- a/tests/topotests/bgp_local_as_private_remove/r1/bgpd.conf +++ b/tests/topotests/bgp_local_as_private_remove/r1/bgpd.conf @@ -1,6 +1,7 @@ router bgp 65000 no bgp ebgp-requires-policy neighbor 192.168.255.2 remote-as 1000 + neighbor 192.168.255.2 timers 3 10 neighbor 192.168.255.2 local-as 500 address-family ipv4 unicast neighbor 192.168.255.2 remove-private-AS diff --git a/tests/topotests/bgp_local_as_private_remove/r2/bgpd.conf b/tests/topotests/bgp_local_as_private_remove/r2/bgpd.conf index 27427a9aaa..802c32714b 100644 --- a/tests/topotests/bgp_local_as_private_remove/r2/bgpd.conf +++ b/tests/topotests/bgp_local_as_private_remove/r2/bgpd.conf @@ -1,3 +1,5 @@ router bgp 1000 no bgp ebgp-requires-policy neighbor 192.168.255.1 remote-as 500 + neighbor 192.168.255.1 timers 3 10 +! diff --git a/tests/topotests/bgp_local_as_private_remove/r3/bgpd.conf b/tests/topotests/bgp_local_as_private_remove/r3/bgpd.conf index f2050ddfdb..9a831270b4 100644 --- a/tests/topotests/bgp_local_as_private_remove/r3/bgpd.conf +++ b/tests/topotests/bgp_local_as_private_remove/r3/bgpd.conf @@ -1,6 +1,7 @@ router bgp 3000 no bgp ebgp-requires-policy neighbor 192.168.255.2 remote-as 1000 + neighbor 192.168.255.2 timers 3 10 neighbor 192.168.255.2 local-as 500 address-family ipv4 unicast neighbor 192.168.255.2 remove-private-AS diff --git a/tests/topotests/bgp_local_as_private_remove/r4/bgpd.conf b/tests/topotests/bgp_local_as_private_remove/r4/bgpd.conf index 27427a9aaa..802c32714b 100644 --- a/tests/topotests/bgp_local_as_private_remove/r4/bgpd.conf +++ b/tests/topotests/bgp_local_as_private_remove/r4/bgpd.conf @@ -1,3 +1,5 @@ router bgp 1000 no bgp ebgp-requires-policy neighbor 192.168.255.1 remote-as 500 + neighbor 192.168.255.1 timers 3 10 +! diff --git a/tests/topotests/bgp_local_as_private_remove/test_bgp_local_as_private_remove.py b/tests/topotests/bgp_local_as_private_remove/test_bgp_local_as_private_remove.py index 56bb14411a..32e7a4df61 100644 --- a/tests/topotests/bgp_local_as_private_remove/test_bgp_local_as_private_remove.py +++ b/tests/topotests/bgp_local_as_private_remove/test_bgp_local_as_private_remove.py @@ -66,7 +66,7 @@ def setup_module(mod): router_list = tgen.routers() - for i, (rname, router) in enumerate(router_list.iteritems(), 1): + for i, (rname, router) in enumerate(router_list.items(), 1): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ) diff --git a/tests/topotests/bgp_maximum_prefix_invalid_update/r1/bgpd.conf b/tests/topotests/bgp_maximum_prefix_invalid_update/r1/bgpd.conf index f0df56e947..62110429cf 100644 --- a/tests/topotests/bgp_maximum_prefix_invalid_update/r1/bgpd.conf +++ b/tests/topotests/bgp_maximum_prefix_invalid_update/r1/bgpd.conf @@ -1,5 +1,6 @@ router bgp 65000 no bgp ebgp-requires-policy neighbor 192.168.255.2 remote-as 65001 + neighbor 192.168.255.2 timers 3 10 address-family ipv4 unicast redistribute connected diff --git a/tests/topotests/bgp_maximum_prefix_invalid_update/r2/bgpd.conf b/tests/topotests/bgp_maximum_prefix_invalid_update/r2/bgpd.conf index ef50dd0d7f..005425e850 100644 --- a/tests/topotests/bgp_maximum_prefix_invalid_update/r2/bgpd.conf +++ b/tests/topotests/bgp_maximum_prefix_invalid_update/r2/bgpd.conf @@ -1,5 +1,6 @@ router bgp 65001 no bgp ebgp-requires-policy neighbor 192.168.255.1 remote-as 65000 + neighbor 192.168.255.1 timers 3 10 address-family ipv4 neighbor 192.168.255.1 maximum-prefix 1 diff --git a/tests/topotests/bgp_maximum_prefix_invalid_update/test_bgp_maximum_prefix_invalid_update.py b/tests/topotests/bgp_maximum_prefix_invalid_update/test_bgp_maximum_prefix_invalid_update.py index 5e7c6d4b63..8494653dfe 100644 --- a/tests/topotests/bgp_maximum_prefix_invalid_update/test_bgp_maximum_prefix_invalid_update.py +++ b/tests/topotests/bgp_maximum_prefix_invalid_update/test_bgp_maximum_prefix_invalid_update.py @@ -66,7 +66,7 @@ def setup_module(mod): router_list = tgen.routers() - for i, (rname, router) in enumerate(router_list.iteritems(), 1): + for i, (rname, router) in enumerate(router_list.items(), 1): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ) diff --git a/tests/topotests/bgp_maximum_prefix_out/r1/bgpd.conf b/tests/topotests/bgp_maximum_prefix_out/r1/bgpd.conf index 03e3eb6e08..adb594b3dd 100644 --- a/tests/topotests/bgp_maximum_prefix_out/r1/bgpd.conf +++ b/tests/topotests/bgp_maximum_prefix_out/r1/bgpd.conf @@ -2,6 +2,7 @@ router bgp 65001 no bgp ebgp-requires-policy neighbor 192.168.255.1 remote-as 65002 + neighbor 192.168.255.1 timers 3 10 address-family ipv4 unicast redistribute connected neighbor 192.168.255.1 maximum-prefix-out 2 diff --git a/tests/topotests/bgp_maximum_prefix_out/r2/bgpd.conf b/tests/topotests/bgp_maximum_prefix_out/r2/bgpd.conf index 2d47b2f661..229de2e2f2 100644 --- a/tests/topotests/bgp_maximum_prefix_out/r2/bgpd.conf +++ b/tests/topotests/bgp_maximum_prefix_out/r2/bgpd.conf @@ -2,6 +2,7 @@ router bgp 65002 no bgp ebgp-requires-policy neighbor 192.168.255.2 remote-as 65001 + neighbor 192.168.255.2 timers 3 10 exit-address-family ! ! diff --git a/tests/topotests/bgp_maximum_prefix_out/test_bgp_maximum_prefix_out.py b/tests/topotests/bgp_maximum_prefix_out/test_bgp_maximum_prefix_out.py index 708684f696..b99664e700 100644 --- a/tests/topotests/bgp_maximum_prefix_out/test_bgp_maximum_prefix_out.py +++ b/tests/topotests/bgp_maximum_prefix_out/test_bgp_maximum_prefix_out.py @@ -62,7 +62,7 @@ def setup_module(mod): router_list = tgen.routers() - for i, (rname, router) in enumerate(router_list.iteritems(), 1): + for i, (rname, router) in enumerate(router_list.items(), 1): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ) 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 a37e3f36a3..c15b88d371 100755 --- 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 @@ -132,6 +132,7 @@ from lib.common_config import ( create_bgp_community_lists, check_router_status, apply_raw_config, + required_linux_kernel_version ) from lib.topolog import logger @@ -209,6 +210,10 @@ def setup_module(mod): * `mod`: module name """ + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version('4.15') + if result is not True: + pytest.skip("Kernel requirements are not met") testsuite_run_time = time.asctime(time.localtime(time.time())) logger.info("Testsuite start time: {}".format(testsuite_run_time)) 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 c36e66a60e..bb13d54019 100755 --- 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 @@ -78,6 +78,7 @@ from lib.common_config import ( get_frr_ipv6_linklocal, check_router_status, apply_raw_config, + required_linux_kernel_version ) from lib.topolog import logger @@ -141,6 +142,10 @@ def setup_module(mod): * `mod`: module name """ + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version('4.15') + if result is not True: + pytest.skip("Kernel requirements are not met") testsuite_run_time = time.asctime(time.localtime(time.time())) logger.info("Testsuite start time: {}".format(testsuite_run_time)) diff --git a/tests/topotests/bgp_multiview_topo1/r1/bgpd.conf b/tests/topotests/bgp_multiview_topo1/r1/bgpd.conf index 5f65a54d7f..d44c3e18e6 100644 --- a/tests/topotests/bgp_multiview_topo1/r1/bgpd.conf +++ b/tests/topotests/bgp_multiview_topo1/r1/bgpd.conf @@ -17,8 +17,11 @@ router bgp 100 view 1 network 172.20.0.0/28 route-map local1 timers bgp 60 180 neighbor 172.16.1.1 remote-as 65001 + neighbor 172.16.1.1 timers 3 10 neighbor 172.16.1.2 remote-as 65002 + neighbor 172.16.1.2 timers 3 10 neighbor 172.16.1.5 remote-as 65005 + neighbor 172.16.1.5 timers 3 10 ! router bgp 100 view 2 bgp router-id 172.30.1.1 @@ -26,7 +29,9 @@ router bgp 100 view 2 network 172.20.0.0/28 route-map local2 timers bgp 60 180 neighbor 172.16.1.3 remote-as 65003 + neighbor 172.16.1.3 timers 3 10 neighbor 172.16.1.4 remote-as 65004 + neighbor 172.16.1.4 timers 3 10 ! router bgp 100 view 3 bgp router-id 172.30.1.1 @@ -34,8 +39,11 @@ router bgp 100 view 3 network 172.20.0.0/28 timers bgp 60 180 neighbor 172.16.1.6 remote-as 65006 + neighbor 172.16.1.6 timers 3 10 neighbor 172.16.1.7 remote-as 65007 + neighbor 172.16.1.7 timers 3 10 neighbor 172.16.1.8 remote-as 65008 + neighbor 172.16.1.8 timers 3 10 ! route-map local1 permit 10 set community 100:9999 additive diff --git a/tests/topotests/bgp_prefix_sid/r1/bgpd.conf b/tests/topotests/bgp_prefix_sid/r1/bgpd.conf index 06bdc31f8c..e02226f2fd 100644 --- a/tests/topotests/bgp_prefix_sid/r1/bgpd.conf +++ b/tests/topotests/bgp_prefix_sid/r1/bgpd.conf @@ -6,7 +6,9 @@ router bgp 1 no bgp default ipv4-unicast no bgp ebgp-requires-policy neighbor 10.0.0.101 remote-as 2 + neighbor 10.0.0.101 timers 3 10 neighbor 10.0.0.102 remote-as 3 + neighbor 10.0.0.102 timers 3 10 ! address-family ipv4 labeled-unicast neighbor 10.0.0.101 activate diff --git a/tests/topotests/bgp_prefix_sid/test_bgp_prefix_sid.py b/tests/topotests/bgp_prefix_sid/test_bgp_prefix_sid.py index 3a6aefe7ee..6d7131e1e5 100755..100644 --- a/tests/topotests/bgp_prefix_sid/test_bgp_prefix_sid.py +++ b/tests/topotests/bgp_prefix_sid/test_bgp_prefix_sid.py @@ -75,7 +75,7 @@ def setup_module(module): logger.info("starting exaBGP on peer1") peer_list = tgen.exabgp_peers() - for pname, peer in peer_list.iteritems(): + for pname, peer in peer_list.items(): peer_dir = os.path.join(CWD, pname) env_file = os.path.join(CWD, "exabgp.env") logger.info("Running ExaBGP peer") diff --git a/tests/topotests/bgp_reject_as_sets/r1/bgpd.conf b/tests/topotests/bgp_reject_as_sets/r1/bgpd.conf index 94bfc5e561..a28b612e00 100644 --- a/tests/topotests/bgp_reject_as_sets/r1/bgpd.conf +++ b/tests/topotests/bgp_reject_as_sets/r1/bgpd.conf @@ -2,6 +2,7 @@ router bgp 65001 no bgp ebgp-requires-policy neighbor 192.168.255.1 remote-as 65002 + neighbor 192.168.255.1 timers 3 10 address-family ipv4 unicast redistribute connected exit-address-family diff --git a/tests/topotests/bgp_reject_as_sets/r2/bgpd.conf b/tests/topotests/bgp_reject_as_sets/r2/bgpd.conf index f217b7f794..453961762a 100644 --- a/tests/topotests/bgp_reject_as_sets/r2/bgpd.conf +++ b/tests/topotests/bgp_reject_as_sets/r2/bgpd.conf @@ -3,7 +3,9 @@ router bgp 65002 bgp reject-as-sets no bgp ebgp-requires-policy neighbor 192.168.255.2 remote-as 65001 + neighbor 192.168.255.2 timers 3 10 neighbor 192.168.254.2 remote-as 65003 + neighbor 192.168.254.2 timers 3 10 address-family ipv4 unicast aggregate-address 172.16.0.0/16 as-set summary-only exit-address-family diff --git a/tests/topotests/bgp_reject_as_sets/r3/bgpd.conf b/tests/topotests/bgp_reject_as_sets/r3/bgpd.conf index 8d085a0e4b..1dde4f0f69 100644 --- a/tests/topotests/bgp_reject_as_sets/r3/bgpd.conf +++ b/tests/topotests/bgp_reject_as_sets/r3/bgpd.conf @@ -2,6 +2,7 @@ router bgp 65003 no bgp ebgp-requires-policy neighbor 192.168.254.1 remote-as 65002 + neighbor 192.168.254.1 timers 3 10 address-family ipv4 unicast neighbor 192.168.254.1 allowas-in redistribute connected diff --git a/tests/topotests/bgp_reject_as_sets/test_bgp_reject_as_sets.py b/tests/topotests/bgp_reject_as_sets/test_bgp_reject_as_sets.py index b49a57b308..d514dccd4a 100644 --- a/tests/topotests/bgp_reject_as_sets/test_bgp_reject_as_sets.py +++ b/tests/topotests/bgp_reject_as_sets/test_bgp_reject_as_sets.py @@ -73,7 +73,7 @@ def setup_module(mod): router_list = tgen.routers() - for i, (rname, router) in enumerate(router_list.iteritems(), 1): + for i, (rname, router) in enumerate(router_list.items(), 1): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ) diff --git a/tests/topotests/bgp_rfapi_basic_sanity/r1/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity/r1/bgpd.conf index ada354bd62..f6e0baa43d 100644 --- a/tests/topotests/bgp_rfapi_basic_sanity/r1/bgpd.conf +++ b/tests/topotests/bgp_rfapi_basic_sanity/r1/bgpd.conf @@ -9,6 +9,7 @@ router bgp 5226 bgp cluster-id 1.1.1.1 no bgp ebgp-requires-policy neighbor 2.2.2.2 remote-as 5226 + neighbor 2.2.2.2 timers 3 10 neighbor 2.2.2.2 update-source 1.1.1.1 ! address-family ipv4 unicast diff --git a/tests/topotests/bgp_rfapi_basic_sanity/r2/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity/r2/bgpd.conf index 95890f25b9..19050e67f6 100644 --- a/tests/topotests/bgp_rfapi_basic_sanity/r2/bgpd.conf +++ b/tests/topotests/bgp_rfapi_basic_sanity/r2/bgpd.conf @@ -9,10 +9,13 @@ router bgp 5226 bgp cluster-id 2.2.2.2 no bgp ebgp-requires-policy neighbor 1.1.1.1 remote-as 5226 + neighbor 1.1.1.1 timers 3 10 neighbor 1.1.1.1 update-source 2.2.2.2 neighbor 3.3.3.3 remote-as 5226 + neighbor 3.3.3.3 timers 3 10 neighbor 3.3.3.3 update-source 2.2.2.2 neighbor 4.4.4.4 remote-as 5226 + neighbor 4.4.4.4 timers 3 10 neighbor 4.4.4.4 update-source 2.2.2.2 address-family ipv4 unicast no neighbor 1.1.1.1 activate diff --git a/tests/topotests/bgp_rfapi_basic_sanity/r3/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity/r3/bgpd.conf index 4932d63d4f..2210f24589 100644 --- a/tests/topotests/bgp_rfapi_basic_sanity/r3/bgpd.conf +++ b/tests/topotests/bgp_rfapi_basic_sanity/r3/bgpd.conf @@ -9,6 +9,7 @@ router bgp 5226 bgp cluster-id 3.3.3.3 no bgp ebgp-requires-policy neighbor 2.2.2.2 remote-as 5226 + neighbor 2.2.2.2 timers 3 10 neighbor 2.2.2.2 update-source 3.3.3.3 ! address-family ipv4 unicast diff --git a/tests/topotests/bgp_rfapi_basic_sanity/r4/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity/r4/bgpd.conf index 1a5e41aae6..28b5f9cfc5 100644 --- a/tests/topotests/bgp_rfapi_basic_sanity/r4/bgpd.conf +++ b/tests/topotests/bgp_rfapi_basic_sanity/r4/bgpd.conf @@ -9,6 +9,7 @@ router bgp 5226 bgp cluster-id 4.4.4.4 no bgp ebgp-requires-policy neighbor 2.2.2.2 remote-as 5226 + neighbor 2.2.2.2 timers 3 10 neighbor 2.2.2.2 update-source 4.4.4.4 ! address-family ipv4 unicast diff --git a/tests/topotests/bgp_rfapi_basic_sanity_config2/r1/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity_config2/r1/bgpd.conf index a38afd632f..3196a16626 100644 --- a/tests/topotests/bgp_rfapi_basic_sanity_config2/r1/bgpd.conf +++ b/tests/topotests/bgp_rfapi_basic_sanity_config2/r1/bgpd.conf @@ -9,6 +9,7 @@ router bgp 5226 bgp cluster-id 1.1.1.1 no bgp ebgp-requires-policy neighbor 2.2.2.2 remote-as 5226 + neighbor 2.2.2.2 timers 3 10 neighbor 2.2.2.2 update-source 1.1.1.1 ! address-family ipv4 unicast diff --git a/tests/topotests/bgp_rfapi_basic_sanity_config2/r2/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity_config2/r2/bgpd.conf index 95890f25b9..19050e67f6 100644 --- a/tests/topotests/bgp_rfapi_basic_sanity_config2/r2/bgpd.conf +++ b/tests/topotests/bgp_rfapi_basic_sanity_config2/r2/bgpd.conf @@ -9,10 +9,13 @@ router bgp 5226 bgp cluster-id 2.2.2.2 no bgp ebgp-requires-policy neighbor 1.1.1.1 remote-as 5226 + neighbor 1.1.1.1 timers 3 10 neighbor 1.1.1.1 update-source 2.2.2.2 neighbor 3.3.3.3 remote-as 5226 + neighbor 3.3.3.3 timers 3 10 neighbor 3.3.3.3 update-source 2.2.2.2 neighbor 4.4.4.4 remote-as 5226 + neighbor 4.4.4.4 timers 3 10 neighbor 4.4.4.4 update-source 2.2.2.2 address-family ipv4 unicast no neighbor 1.1.1.1 activate diff --git a/tests/topotests/bgp_rfapi_basic_sanity_config2/r3/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity_config2/r3/bgpd.conf index dbeb2c4665..e74fc0b3de 100644 --- a/tests/topotests/bgp_rfapi_basic_sanity_config2/r3/bgpd.conf +++ b/tests/topotests/bgp_rfapi_basic_sanity_config2/r3/bgpd.conf @@ -9,6 +9,7 @@ router bgp 5226 bgp cluster-id 3.3.3.3 no bgp ebgp-requires-policy neighbor 2.2.2.2 remote-as 5226 + neighbor 2.2.2.2 timers 3 10 neighbor 2.2.2.2 update-source 3.3.3.3 ! address-family ipv4 unicast diff --git a/tests/topotests/bgp_rfapi_basic_sanity_config2/r4/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity_config2/r4/bgpd.conf index ae1787718c..56474aad9c 100644 --- a/tests/topotests/bgp_rfapi_basic_sanity_config2/r4/bgpd.conf +++ b/tests/topotests/bgp_rfapi_basic_sanity_config2/r4/bgpd.conf @@ -9,6 +9,7 @@ router bgp 5226 bgp cluster-id 4.4.4.4 no bgp ebgp-requires-policy neighbor 2.2.2.2 remote-as 5226 + neighbor 2.2.2.2 timers 3 10 neighbor 2.2.2.2 update-source 4.4.4.4 ! address-family ipv4 unicast diff --git a/tests/topotests/bgp_route_aggregation/bgp_aggregation.json b/tests/topotests/bgp_route_aggregation/bgp_aggregation.json new file mode 100644 index 0000000000..5520f67d0d --- /dev/null +++ b/tests/topotests/bgp_route_aggregation/bgp_aggregation.json @@ -0,0 +1,249 @@ +{ + "address_types": [ + "ipv4", + "ipv6" + ], + "ipv4base": "192.168.0.0", + "ipv4mask": 24, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "192.168.0.0", + "v4mask": 24, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:db8:f::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {} + } + }, + "r3": { + "dest_link": { + "r1": { + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": { + } + } + }, + "r3": { + "dest_link": { + "r1": { + } + } + } + } + } + } + } + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r4": { + "dest_link": { + "r2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": { + } + } + }, + "r4": { + "dest_link": { + "r2": { + } + } + } + } + } + } + } + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {} + } + }, + "r4": { + "dest_link": { + "r3": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + } + } + }, + "r4": { + "dest_link": { + "r3": { + } + } + } + } + } + } + } + } + }, + "r4": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "400", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": {} + } + }, + "r3": { + "dest_link": { + "r4": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": { + } + } + }, + "r3": { + "dest_link": { + "r4": { + } + } + } + } + } + } + } + } + } + } +} diff --git a/tests/topotests/bgp_route_aggregation/test_bgp_aggregation.py b/tests/topotests/bgp_route_aggregation/test_bgp_aggregation.py new file mode 100755 index 0000000000..0fabd90341 --- /dev/null +++ b/tests/topotests/bgp_route_aggregation/test_bgp_aggregation.py @@ -0,0 +1,1167 @@ +#!/usr/bin/python +# +# Copyright (c) 2020 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. +# + +""" +Following tests are covered to test bgp aggregation functionality: + +1. Verify route summarisation with summary-only for redistributed as well as + locally generated routes. +2. Verify route summarisation with as-set for redistributed routes. + +""" + +import os +import sys +import time +import json +import pytest +from time import sleep +from copy import deepcopy + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from 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, + apply_raw_config, + write_test_footer, + reset_config_on_routers, + verify_rib, + create_static_routes, + check_address_types, + step, + create_route_maps, + create_prefix_lists, +) +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + create_router_bgp, + verify_bgp_rib, + verify_bgp_community, + verify_bgp_timers_and_functionality, +) +from lib.topojson import build_topo_from_json, build_config_from_json + +# Reading the data from JSON File for topology and configuration creation +jsonFile = "{}/bgp_aggregation.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + logger.info("Could not read file:", jsonFile) + +# Global variables +BGP_CONVERGENCE = False +ADDR_TYPES = check_address_types() + +NETWORK_1_1 = {"ipv4": "10.1.1.0/24", "ipv6": "10:1::1:0/120"} +NETWORK_1_2 = {"ipv4": "10.1.2.0/24", "ipv6": "10:1::2:0/120"} +NETWORK_1_3 = {"ipv4": "10.1.3.0/24", "ipv6": "10:1::3:0/120"} +NETWORK_1_4 = {"ipv4": "10.1.4.0/24", "ipv6": "10:1::4:0/120"} +NETWORK_1_5 = {"ipv4": "10.1.5.0/24", "ipv6": "10:1::5:0/120"} +NETWORK_2_1 = {"ipv4": "10.1.1.100/32", "ipv6": "10:1::1:0/124"} +NETWORK_2_2 = {"ipv4": "10.1.5.0/24", "ipv6": "10:1::5:0/120"} +NETWORK_2_3 = {"ipv4": "10.1.6.0/24", "ipv6": "10:1::6:0/120"} +NETWORK_2_4 = {"ipv4": "10.1.7.0/24", "ipv6": "10:1::7:0/120"} +NETWORK_3_1 = {"ipv4": "10.1.8.0/24", "ipv6": "10:1::8:0/120"} +NETWORK_4_1 = {"ipv4": "10.2.1.0/24", "ipv6": "10:2::1:0/120"} +NEXT_HOP = {"ipv4": "Null0", "ipv6": "Null0"} +AGGREGATE_NW = {"ipv4": "10.1.0.0/20", "ipv6": "10:1::/96"} + +COMMUNITY = [ + "0:1 0:10 0:100", + "0:2 0:20 0:200", + "0:3 0:30 0:300", + "0:4 0:40 0:400", + "0:5 0:50 0:500", + "0:1 0:2 0:3 0:4 0:5 0:10 0:20 0:30 0:40 0:50 0:100 0:200 0:300 0:400 0:500", + "0:3 0:4 0:5 0:30 0:40 0:50 0:300 0:400 0:500", + "0:6 0:60 0:600", + "0:7 0:70 0:700", + "0:3 0:4 0:5 0:6 0:30 0:40 0:50 0:60 0:300 0:400 0:500 0:600", +] + + +class CreateTopo(Topo): + """ + Test BasicTopo - topology 1 + + * `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 + """ + + 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. + + # 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) + + # Api call verify whether BGP is converged + ADDR_TYPES = check_address_types() + + for addr_type in ADDR_TYPES: + 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(mod): + """ + 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() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Tests starting +# +##################################################### + + +def test_route_summarisation_with_summary_only_p1(request): + """ + Verify route summarisation with summary-only for redistributed as well as + locally generated routes. + """ + + tgen = get_topogen() + tc_name = request.node.name + reset_config_on_routers(tgen) + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Configure static routes on router R1 and redistribute in " "BGP process.") + + for addr_type in ADDR_TYPES: + input_static = { + "r1": { + "static_routes": [ + { + "network": [ + NETWORK_1_1[addr_type], + NETWORK_1_2[addr_type], + NETWORK_1_3[addr_type], + ], + "next_hop": NEXT_HOP[addr_type], + } + ] + } + } + input_redistribute = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + + step("Configuring {} static routes on router R1 ".format(addr_type)) + + result = create_static_routes(tgen, input_static) + assert result is True, "Testcase : Failed \n Error: {}".format(tc_name, result) + + step( + "Configuring redistribute static for {} address-family on router R1 ".format( + addr_type + ) + ) + + result = create_router_bgp(tgen, topo, input_redistribute) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that Static routes are redistributed in BGP process") + + for addr_type in ADDR_TYPES: + input_static = { + "r1": { + "static_routes": [ + { + "network": [ + NETWORK_1_1[addr_type], + NETWORK_1_2[addr_type], + NETWORK_1_3[addr_type], + ] + } + ] + } + } + + result = verify_rib(tgen, addr_type, "r3", input_static) + assert result is True, "Testcase : Failed \n Error: {}".format(tc_name, result) + + step("Advertise some prefixes using network command") + step( + "Additionally advertise 10.1.4.0/24 & 10.1.5.0/24 and " + "10:1::4:0/120 & 10:1::5:0/120 from R4 to R1." + ) + + for addr_type in ADDR_TYPES: + input_advertise = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "advertise_networks": [ + { + "network": [ + NETWORK_2_1[addr_type], + NETWORK_2_2[addr_type], + NETWORK_2_3[addr_type], + NETWORK_2_4[addr_type], + ] + } + ] + } + } + } + } + }, + "r4": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "advertise_networks": [ + { + "network": [ + NETWORK_1_4[addr_type], + NETWORK_1_5[addr_type], + ] + } + ] + } + } + } + } + }, + } + + result = create_router_bgp(tgen, topo, input_advertise) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that advertised prefixes using network command are being " + "advertised in BGP process" + ) + + for addr_type in ADDR_TYPES: + input_advertise = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "advertise_networks": [ + { + "network": [ + NETWORK_2_1[addr_type], + NETWORK_2_2[addr_type], + NETWORK_2_3[addr_type], + NETWORK_2_4[addr_type], + ] + } + ] + } + } + } + } + } + } + + result = verify_rib(tgen, addr_type, "r3", input_advertise) + assert result is True, "Testcase : Failed \n Error: {}".format(tc_name, result) + + step("Configure aggregate-address to summarise all the advertised routes.") + + for addr_type in ADDR_TYPES: + route_aggregate = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "aggregate_address": [ + { + "network": AGGREGATE_NW[addr_type], + "summary": True, + } + ] + } + } + } + } + } + } + + result = create_router_bgp(tgen, topo, route_aggregate) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that we see 1 summarised route and remaining suppressed " + "routes on advertising router R1 and only 1 summarised route on " + "receiving router R3 for both AFIs." + ) + + for addr_type in ADDR_TYPES: + input_static_agg = { + "r1": {"static_routes": [{"network": AGGREGATE_NW[addr_type]}]} + } + + input_static = { + "r1": { + "static_routes": [ + { + "network": [ + NETWORK_1_1[addr_type], + NETWORK_1_2[addr_type], + NETWORK_1_3[addr_type], + ] + } + ] + } + } + + result = verify_rib(tgen, addr_type, "r3", input_static_agg, protocol="bgp") + assert result is True, "Testcase : Failed \n Error: {}".format(tc_name, result) + + result = verify_rib( + tgen, addr_type, "r3", input_static, protocol="bgp", expected=False + ) + assert result is not True, ( + "Testcase : Failed \n " + "Routes are still present \n Error: {}".format(tc_name, result) + ) + + result = verify_rib(tgen, addr_type, "r1", input_static_agg, protocol="bgp") + assert result is True, "Testcase : Failed \n Error: {}".format(tc_name, result) + + result = verify_rib(tgen, addr_type, "r1", input_static) + assert result is True, "Testcase : Failed \n Error: {}".format(tc_name, result) + + for action, value in zip(["removed", "add"], [True, False]): + + step( + "{} static routes as below: " + "(no) ip route 10.1.1.0/24 and (no) ip route 10.1.2.0/24" + "(no) ipv6 route 10:1::1:0/120 and (no) ip route 10:1::2:0/120".format( + action + ) + ) + + for addr_type in ADDR_TYPES: + input_static = { + "r1": { + "static_routes": [ + { + "network": [NETWORK_1_1[addr_type], NETWORK_1_2[addr_type]], + "next_hop": NEXT_HOP[addr_type], + "delete": value, + } + ] + } + } + + result = create_static_routes(tgen, input_static) + assert result is True, "Testcase : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that there is no impact on R3, as summarised route remains " + "intact. However suppressed routes on R1 disappear and re-appear " + "based on {} static routes.".format(action) + ) + + for addr_type in ADDR_TYPES: + input_static_1 = { + "r1": { + "static_routes": [ + {"network": [NETWORK_1_1[addr_type], NETWORK_1_2[addr_type]]} + ] + } + } + + input_static_2 = { + "r1": {"static_routes": [{"network": AGGREGATE_NW[addr_type]}]} + } + + if value: + result = verify_rib( + tgen, addr_type, "r1", input_static_1, expected=False + ) + assert result is not True, ( + "Testcase : Failed \n " + "Routes are still present \n Error: {}".format(tc_name, result) + ) + else: + result = verify_rib(tgen, addr_type, "r1", input_static_1) + assert result is True, "Testcase : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r3", input_static_2, protocol="bgp") + assert result is True, "Testcase : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "{} prefixes using network command as below:" + "(no) network 10.1.6.1/24 and (no) network 10.1.7.1/24" + "(no) network 10:1::6:0/120 and (no) network 10:1::7:0/120".format(action) + ) + + for addr_type in ADDR_TYPES: + input_advertise = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "advertise_networks": [ + { + "network": [ + NETWORK_2_3[addr_type], + NETWORK_2_4[addr_type], + ], + "delete": value, + } + ] + } + } + } + } + } + } + + result = create_router_bgp(tgen, topo, input_advertise) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that there is no impact on R3, as summarised route remains " + "intact. However suppressed routes on R1 disappear and re-appear " + "based on {} of network command.".format(action) + ) + + for addr_type in ADDR_TYPES: + input_advertise_1 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "advertise_networks": [ + { + "network": [ + NETWORK_2_3[addr_type], + NETWORK_2_4[addr_type], + ] + } + ] + } + } + } + } + } + } + + input_advertise_2 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "advertise_networks": [ + {"network": AGGREGATE_NW[addr_type]} + ] + } + } + } + } + } + } + + if value: + result = verify_bgp_rib( + tgen, addr_type, "r1", input_advertise_1, expected=False + ) + assert result is not True, ( + "Testcase : Failed \n " + "Routes are still present \n Error: {}".format(tc_name, result) + ) + else: + result = verify_bgp_rib(tgen, addr_type, "r1", input_advertise_1) + assert result is True, "Testcase : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r3", input_advertise_2) + assert result is True, "Testcase : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Add a new network each one from out of aggregation range and " + "other within aggregation range. " + ) + + for addr_type in ADDR_TYPES: + input_static = { + "r1": { + "static_routes": [ + {"network": NETWORK_3_1[addr_type], "next_hop": NEXT_HOP[addr_type]} + ] + } + } + + result = create_static_routes(tgen, input_static) + assert result is True, "Testcase : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + input_advertise = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "advertise_networks": [ + {"network": NETWORK_4_1[addr_type],} + ] + } + } + } + } + } + } + + result = create_router_bgp(tgen, topo, input_advertise) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that when a network within aggregation range is added, " + "there is no impact on receiving router. However if a network " + "outside aggregation range is added/removed, R3 receives and " + "withdraws it accordingly." + ) + + for addr_type in ADDR_TYPES: + input_static = {"r1": {"static_routes": [{"network": AGGREGATE_NW[addr_type]}]}} + + result = verify_rib(tgen, addr_type, "r3", input_static, protocol="bgp") + assert result is True, "Testcase : Failed \n Error: {}".format(tc_name, result) + + input_advertise_2 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "advertise_networks": [ + { + "network": [ + NETWORK_4_1[addr_type], + AGGREGATE_NW[addr_type], + ] + } + ] + } + } + } + } + } + } + + result = verify_rib(tgen, addr_type, "r3", input_advertise_2, protocol="bgp") + assert result is True, "Testcase : Failed \n Error: {}".format(tc_name, result) + + for action, value in zip(["Delete", "Re-add"], [True, False]): + step("{} aggregation command from R1.".format(action)) + + for addr_type in ADDR_TYPES: + route_aggregate = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "aggregate_address": [ + { + "network": AGGREGATE_NW[addr_type], + "summary": True, + "delete": value, + } + ] + } + } + } + } + } + } + + result = create_router_bgp(tgen, topo, route_aggregate) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify on both routers that summarised route is withdrawn from R1 " + "and R3 when aggregate-address command is removed and appears again " + "when aggregate-address command is re-added. Check for both AFIs." + ) + + for addr_type in ADDR_TYPES: + input_static_agg = { + "r1": {"static_routes": [{"network": AGGREGATE_NW[addr_type]}]} + } + + if value: + result = verify_rib( + tgen, addr_type, "r1", input_static_agg, expected=False + ) + assert result is not True, ( + "Testcase : Failed \n " + "Aggregated route is still present \n Error: {}".format( + tc_name, result + ) + ) + + result = verify_rib( + tgen, addr_type, "r3", input_static_agg, expected=False + ) + assert result is not True, ( + "Testcase : Failed \n " + "Aggregated route is still present \n Error: {}".format( + tc_name, result + ) + ) + else: + result = verify_rib(tgen, addr_type, "r1", input_static_agg) + assert result is True, "Testcase : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r3", input_static_agg) + assert result is True, "Testcase : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_route_summarisation_with_as_set_p1(request): + """ + Verify route summarisation with as-set for redistributed routes. + """ + + tgen = get_topogen() + tc_name = request.node.name + reset_config_on_routers(tgen) + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Configure static routes on router R1 and redistribute in " "BGP process.") + + for addr_type in ADDR_TYPES: + input_static = { + "r1": { + "static_routes": [ + { + "network": [ + NETWORK_1_1[addr_type], + NETWORK_1_2[addr_type], + NETWORK_1_3[addr_type], + NETWORK_1_4[addr_type], + NETWORK_1_5[addr_type], + ], + "next_hop": NEXT_HOP[addr_type], + } + ] + } + } + input_redistribute = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + + step("Configuring {} static routes on router R1 ".format(addr_type)) + + result = create_static_routes(tgen, input_static) + assert result is True, "Testcase : Failed \n Error: {}".format(tc_name, result) + + step( + "Configuring redistribute static for {} address-family on router R1 ".format( + addr_type + ) + ) + + result = create_router_bgp(tgen, topo, input_redistribute) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that Static routes are redistributed in BGP process") + + for addr_type in ADDR_TYPES: + input_static = { + "r1": { + "static_routes": [ + { + "network": [ + NETWORK_1_1[addr_type], + NETWORK_1_2[addr_type], + NETWORK_1_3[addr_type], + NETWORK_1_4[addr_type], + NETWORK_1_5[addr_type], + ] + } + ] + } + } + + result = verify_rib(tgen, addr_type, "r3", input_static) + assert result is True, "Testcase : Failed \n Error: {}".format(tc_name, result) + + step( + "Configure a route-map to attach a unique community attribute value " + "to each of these prefixes, while re-distributing static." + ) + + for addr_type in ADDR_TYPES: + for pfx, seq_id, network, in zip( + [1, 2, 3, 4, 5], + [10, 20, 30, 40, 50], + [NETWORK_1_1, NETWORK_1_2, NETWORK_1_3, NETWORK_1_4, NETWORK_1_5], + ): + prefix_list = { + "r1": { + "prefix_lists": { + addr_type: { + "pf_list_{}_{}".format(addr_type, pfx): [ + { + "seqid": seq_id, + "network": network[addr_type], + "action": "permit", + } + ] + } + } + } + } + result = create_prefix_lists(tgen, prefix_list) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Create route-map for applying prefix-list on r1") + + for addr_type in ADDR_TYPES: + for pfx, comm_id in zip([1, 2, 3, 4, 5], [0, 1, 2, 3, 4]): + route_map = { + "r1": { + "route_maps": { + "rmap_{}".format(addr_type): [ + { + "action": "permit", + "match": { + addr_type: { + "prefix_lists": "pf_list_{}_{}".format( + addr_type, pfx + ) + } + }, + "set": {"community": {"num": COMMUNITY[comm_id]}}, + } + ] + } + } + } + + result = create_route_maps(tgen, route_map) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Re-configure redistribute static with route-map") + + for addr_type in ADDR_TYPES: + input_redistribute = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "redistribute": [ + { + "redist_type": "static", + "attribute": { + "route-map": "rmap_{}".format(addr_type) + }, + } + ] + } + } + } + } + } + } + + result = create_router_bgp(tgen, topo, input_redistribute) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure aggregate-address to summarise all the advertised routes.") + + for addr_type in ADDR_TYPES: + route_aggregate = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "aggregate_address": [ + {"network": AGGREGATE_NW[addr_type], "as_set": True} + ] + } + } + } + } + } + } + + result = create_router_bgp(tgen, topo, route_aggregate) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that we see summarised route on router R3 with all the " + "community attribute values combined with that aggregate route." + ) + + for addr_type in ADDR_TYPES: + input_dict = {"community": COMMUNITY[5]} + result = verify_bgp_community( + tgen, addr_type, "r3", [AGGREGATE_NW[addr_type]], input_dict + ) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Remove static routes as below: " + "(no) ip route 10.1.1.0/24 blackhole " + "(no) ip route 10.1.2.0/24 blackhole " + "(no) ipv6 route 10:1::1:0/120 blackhole " + "(no) ipv6 route 10:1::2:0/120 blackhole " + ) + + for addr_type in ADDR_TYPES: + input_static = { + "r1": { + "static_routes": [ + { + "network": [NETWORK_1_1[addr_type], NETWORK_1_2[addr_type]], + "next_hop": NEXT_HOP[addr_type], + "delete": True, + } + ] + } + } + + result = create_static_routes(tgen, input_static) + assert result is True, "Testcase : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify on R3 that whenever we remove the static routes, we still" + " see aggregated route however the corresponding community attribute" + "values are withdrawn." + ) + + for addr_type in ADDR_TYPES: + input_dict = {"community": COMMUNITY[6]} + result = verify_bgp_community( + tgen, addr_type, "r3", [AGGREGATE_NW[addr_type]], input_dict + ) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Add/remove a new network with community value, each one from out of " + "aggregation range and other within aggregation range. " + ) + + step( + "Add a new network each one from out of aggregation range and " + "other within aggregation range. " + ) + + for addr_type in ADDR_TYPES: + input_static = { + "r1": { + "static_routes": [ + { + "network": [NETWORK_3_1[addr_type], NETWORK_4_1[addr_type]], + "next_hop": NEXT_HOP[addr_type], + } + ] + } + } + + result = create_static_routes(tgen, input_static) + assert result is True, "Testcase : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + for pfx, seq_id, network, in zip([6, 7], [60, 70], [NETWORK_3_1, NETWORK_4_1]): + prefix_list = { + "r1": { + "prefix_lists": { + addr_type: { + "pf_list_{}_{}".format(addr_type, pfx): [ + { + "seqid": seq_id, + "network": network[addr_type], + "action": "permit", + } + ] + } + } + } + } + result = create_prefix_lists(tgen, prefix_list) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Create route-map for applying prefix-list on r1") + + for addr_type in ADDR_TYPES: + for pfx, comm_id in zip([6, 7], [7, 8]): + route_map = { + "r1": { + "route_maps": { + "rmap_{}".format(addr_type): [ + { + "action": "permit", + "match": { + addr_type: { + "prefix_lists": "pf_list_{}_{}".format( + addr_type, pfx + ) + } + }, + "set": {"community": {"num": COMMUNITY[comm_id]}}, + } + ] + } + } + } + + result = create_route_maps(tgen, route_map) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify on R3 when route is added within the summary range, aggregated" + " route also has associated community value added. However if the route" + " is beyond the summary range the aggregated route would have no impact" + ) + + for addr_type in ADDR_TYPES: + input_dict = {"community": COMMUNITY[9]} + result = verify_bgp_community( + tgen, addr_type, "r3", [AGGREGATE_NW[addr_type]], input_dict + ) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + for action, value in zip(["Delete", "Re-add"], [True, False]): + step("{} aggregation command from R1.".format(action)) + + for addr_type in ADDR_TYPES: + route_aggregate = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "aggregate_address": [ + { + "network": AGGREGATE_NW[addr_type], + "as_set": True, + "delete": value, + } + ] + } + } + } + } + } + } + + result = create_router_bgp(tgen, topo, route_aggregate) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that when as-set command is removed, we do not see community " + "attribute added to summarised route on R3. However when as-set option " + "is re-added, all the community attribute values must appear with " + "summarised route." + ) + + for addr_type in ADDR_TYPES: + input_static_agg = { + "r1": {"static_routes": [{"network": AGGREGATE_NW[addr_type]}]} + } + + if value: + result = verify_rib( + tgen, addr_type, "r1", input_static_agg, expected=False + ) + assert result is not True, ( + "Testcase : Failed \n " + "Aggregated route is still present \n Error: {}".format( + tc_name, result + ) + ) + + result = verify_rib( + tgen, addr_type, "r3", input_static_agg, expected=False + ) + assert result is not True, ( + "Testcase : Failed \n " + "Aggregated route is still present \n Error: {}".format( + tc_name, result + ) + ) + else: + result = verify_rib(tgen, addr_type, "r1", input_static_agg) + assert result is True, "Testcase : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r3", input_static_agg) + assert result is True, "Testcase : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict = {"community": COMMUNITY[9]} + result = verify_bgp_community( + tgen, addr_type, "r3", [AGGREGATE_NW[addr_type]], input_dict + ) + assert result is True, "Test case {} : 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_rr_ibgp/spine1/bgpd.conf b/tests/topotests/bgp_rr_ibgp/spine1/bgpd.conf index fa77cce073..cece3fc600 100644 --- a/tests/topotests/bgp_rr_ibgp/spine1/bgpd.conf +++ b/tests/topotests/bgp_rr_ibgp/spine1/bgpd.conf @@ -2,7 +2,9 @@ hostname spine1 router bgp 99 no bgp ebgp-requires-policy neighbor 192.168.2.1 remote-as internal + neighbor 192.168.2.1 timers 3 10 neighbor 192.168.4.2 remote-as internal + neighbor 192.168.4.2 timers 3 10 address-family ipv4 uni redistribute connected neighbor 192.168.2.1 route-reflector-client diff --git a/tests/topotests/bgp_rr_ibgp/test_bgp_rr_ibgp_topo1.py b/tests/topotests/bgp_rr_ibgp/test_bgp_rr_ibgp_topo1.py index da45e73ab4..6a604765ca 100755..100644 --- a/tests/topotests/bgp_rr_ibgp/test_bgp_rr_ibgp_topo1.py +++ b/tests/topotests/bgp_rr_ibgp/test_bgp_rr_ibgp_topo1.py @@ -104,7 +104,7 @@ def setup_module(module): # This is a sample of configuration loading. router_list = tgen.routers() - for rname, router in router_list.iteritems(): + for rname, router in router_list.items(): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ) diff --git a/tests/topotests/bgp_rr_ibgp/tor1/bgpd.conf b/tests/topotests/bgp_rr_ibgp/tor1/bgpd.conf index b028ab4e8b..1b9f15010a 100644 --- a/tests/topotests/bgp_rr_ibgp/tor1/bgpd.conf +++ b/tests/topotests/bgp_rr_ibgp/tor1/bgpd.conf @@ -2,4 +2,5 @@ hostname tor1 router bgp 99 no bgp ebgp-requires-policy neighbor 192.168.2.3 remote-as internal + neighbor 192.168.2.3 timers 3 10 redistribute connected diff --git a/tests/topotests/bgp_rr_ibgp/tor2/bgpd.conf b/tests/topotests/bgp_rr_ibgp/tor2/bgpd.conf index 99c34158b9..3bdb35976c 100644 --- a/tests/topotests/bgp_rr_ibgp/tor2/bgpd.conf +++ b/tests/topotests/bgp_rr_ibgp/tor2/bgpd.conf @@ -2,4 +2,5 @@ hostname tor2 router bgp 99 no bgp ebgp-requires-policy neighbor 192.168.4.3 remote-as internal + neighbor 192.168.4.3 timers 3 10 redistribute connected diff --git a/tests/topotests/bgp_sender-as-path-loop-detection/r1/bgpd.conf b/tests/topotests/bgp_sender-as-path-loop-detection/r1/bgpd.conf index b16e94d7c1..719d76392d 100644 --- a/tests/topotests/bgp_sender-as-path-loop-detection/r1/bgpd.conf +++ b/tests/topotests/bgp_sender-as-path-loop-detection/r1/bgpd.conf @@ -2,6 +2,7 @@ router bgp 65001 no bgp ebgp-requires-policy neighbor 192.168.255.1 remote-as 65002 + neighbor 192.168.255.1 timers 3 10 address-family ipv4 unicast neighbor 192.168.255.1 route-map prepend out redistribute connected diff --git a/tests/topotests/bgp_sender-as-path-loop-detection/r2/bgpd.conf b/tests/topotests/bgp_sender-as-path-loop-detection/r2/bgpd.conf index 674877edd3..a4a654d7b5 100644 --- a/tests/topotests/bgp_sender-as-path-loop-detection/r2/bgpd.conf +++ b/tests/topotests/bgp_sender-as-path-loop-detection/r2/bgpd.conf @@ -2,8 +2,10 @@ router bgp 65002 no bgp ebgp-requires-policy neighbor 192.168.255.2 remote-as 65001 + neighbor 192.168.255.2 timers 3 10 neighbor 192.168.255.2 solo neighbor 192.168.254.2 remote-as 65003 + neighbor 192.168.254.2 timers 3 10 neighbor 192.168.254.2 solo neighbor 192.168.254.2 sender-as-path-loop-detection ! diff --git a/tests/topotests/bgp_sender-as-path-loop-detection/r3/bgpd.conf b/tests/topotests/bgp_sender-as-path-loop-detection/r3/bgpd.conf index 4ee7a39ab2..2e24de0b2d 100644 --- a/tests/topotests/bgp_sender-as-path-loop-detection/r3/bgpd.conf +++ b/tests/topotests/bgp_sender-as-path-loop-detection/r3/bgpd.conf @@ -2,4 +2,5 @@ router bgp 65003 no bgp ebgp-requires-policy neighbor 192.168.254.1 remote-as 65002 + neighbor 192.168.254.1 timers 3 10 ! diff --git a/tests/topotests/bgp_sender-as-path-loop-detection/test_bgp_sender-as-path-loop-detection.py b/tests/topotests/bgp_sender-as-path-loop-detection/test_bgp_sender-as-path-loop-detection.py index 56a98c1ef8..88935ae4d1 100644 --- a/tests/topotests/bgp_sender-as-path-loop-detection/test_bgp_sender-as-path-loop-detection.py +++ b/tests/topotests/bgp_sender-as-path-loop-detection/test_bgp_sender-as-path-loop-detection.py @@ -66,7 +66,7 @@ def setup_module(mod): router_list = tgen.routers() - for i, (rname, router) in enumerate(router_list.iteritems(), 1): + for i, (rname, router) in enumerate(router_list.items(), 1): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ) diff --git a/tests/topotests/bgp_set_local-preference_add_subtract/r1/bgpd.conf b/tests/topotests/bgp_set_local-preference_add_subtract/r1/bgpd.conf index 7dab52fef0..57e2f5818a 100644 --- a/tests/topotests/bgp_set_local-preference_add_subtract/r1/bgpd.conf +++ b/tests/topotests/bgp_set_local-preference_add_subtract/r1/bgpd.conf @@ -1,6 +1,8 @@ router bgp 65000 no bgp ebgp-requires-policy neighbor 192.168.255.2 remote-as 65000 + neighbor 192.168.255.2 timers 3 10 neighbor 192.168.255.3 remote-as 65000 + neighbor 192.168.255.3 timers 3 10 exit-address-family ! diff --git a/tests/topotests/bgp_set_local-preference_add_subtract/r2/bgpd.conf b/tests/topotests/bgp_set_local-preference_add_subtract/r2/bgpd.conf index a8a0384632..82a01d4570 100644 --- a/tests/topotests/bgp_set_local-preference_add_subtract/r2/bgpd.conf +++ b/tests/topotests/bgp_set_local-preference_add_subtract/r2/bgpd.conf @@ -1,6 +1,7 @@ router bgp 65000 no bgp ebgp-requires-policy neighbor 192.168.255.1 remote-as 65000 + neighbor 192.168.255.1 timers 3 10 address-family ipv4 redistribute connected neighbor 192.168.255.1 route-map r1-out out diff --git a/tests/topotests/bgp_set_local-preference_add_subtract/r3/bgpd.conf b/tests/topotests/bgp_set_local-preference_add_subtract/r3/bgpd.conf index 2f5dceede2..65e092b0f2 100644 --- a/tests/topotests/bgp_set_local-preference_add_subtract/r3/bgpd.conf +++ b/tests/topotests/bgp_set_local-preference_add_subtract/r3/bgpd.conf @@ -1,6 +1,7 @@ router bgp 65000 no bgp ebgp-requires-policy neighbor 192.168.255.1 remote-as 65000 + neighbor 192.168.255.1 timers 3 10 address-family ipv4 redistribute connected neighbor 192.168.255.1 route-map r1-out out diff --git a/tests/topotests/bgp_set_local-preference_add_subtract/test_bgp_set_local-preference_add_subtract.py b/tests/topotests/bgp_set_local-preference_add_subtract/test_bgp_set_local-preference_add_subtract.py index ce3165db25..af64648951 100644 --- a/tests/topotests/bgp_set_local-preference_add_subtract/test_bgp_set_local-preference_add_subtract.py +++ b/tests/topotests/bgp_set_local-preference_add_subtract/test_bgp_set_local-preference_add_subtract.py @@ -64,7 +64,7 @@ def setup_module(mod): router_list = tgen.routers() - for i, (rname, router) in enumerate(router_list.iteritems(), 1): + for i, (rname, router) in enumerate(router_list.items(), 1): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ) diff --git a/tests/topotests/bgp_update_delay/__init__.py b/tests/topotests/bgp_update_delay/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/bgp_update_delay/__init__.py diff --git a/tests/topotests/bgp_update_delay/r1/bgpd.conf b/tests/topotests/bgp_update_delay/r1/bgpd.conf new file mode 100644 index 0000000000..8ebb509d6d --- /dev/null +++ b/tests/topotests/bgp_update_delay/r1/bgpd.conf @@ -0,0 +1,10 @@ +! exit1 +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 65002 + neighbor 192.168.255.1 timers connect 10 + address-family ipv4 unicast + redistribute connected + exit-address-family + ! +! diff --git a/tests/topotests/bgp_update_delay/r1/zebra.conf b/tests/topotests/bgp_update_delay/r1/zebra.conf new file mode 100644 index 0000000000..9904bb4e16 --- /dev/null +++ b/tests/topotests/bgp_update_delay/r1/zebra.conf @@ -0,0 +1,9 @@ +! exit1 +interface lo + ip address 172.16.255.254/32 +! +interface r1-eth0 + ip address 192.168.255.2/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_update_delay/r2/bgpd.conf b/tests/topotests/bgp_update_delay/r2/bgpd.conf new file mode 100644 index 0000000000..438f9950a5 --- /dev/null +++ b/tests/topotests/bgp_update_delay/r2/bgpd.conf @@ -0,0 +1,18 @@ +! spine +router bgp 65002 + no bgp ebgp-requires-policy + timers bgp 3 9 + neighbor 192.168.255.2 remote-as 65001 + neighbor 192.168.254.2 remote-as 65003 + neighbor 192.168.253.2 remote-as 65004 + neighbor 192.168.255.2 timers connect 10 + neighbor 192.168.254.2 timers connect 10 + neighbor 192.168.253.2 timers connect 10 +! + router bgp 65002 vrf vrf1 + no bgp ebgp-requires-policy + timers bgp 3 9 + neighbor 192.168.252.2 remote-as 65005 + neighbor 192.168.252.2 timers connect 10 + ! +! diff --git a/tests/topotests/bgp_update_delay/r2/zebra.conf b/tests/topotests/bgp_update_delay/r2/zebra.conf new file mode 100644 index 0000000000..420f00d974 --- /dev/null +++ b/tests/topotests/bgp_update_delay/r2/zebra.conf @@ -0,0 +1,20 @@ +! spine +interface r2-eth0 + ip address 192.168.255.1/30 +! +interface r2-eth1 + ip address 192.168.254.1/30 +! +interface r2-eth2 + ip address 192.168.253.1/30 +! +interface r2-eth3 + ip address 192.168.252.1/30 + vrf vrf1 +! +auto vrf1 +iface vrf1 + vrf-table auto +! +ip forwarding +! diff --git a/tests/topotests/bgp_update_delay/r3/bgpd.conf b/tests/topotests/bgp_update_delay/r3/bgpd.conf new file mode 100644 index 0000000000..53e51788f7 --- /dev/null +++ b/tests/topotests/bgp_update_delay/r3/bgpd.conf @@ -0,0 +1,10 @@ +! exit2 +router bgp 65003 + no bgp ebgp-requires-policy + timers bgp 3 9 + neighbor 192.168.254.1 remote-as 65002 + neighbor 192.168.254.1 timers connect 10 + address-family ipv4 unicast + redistribute connected + ! +! diff --git a/tests/topotests/bgp_update_delay/r3/zebra.conf b/tests/topotests/bgp_update_delay/r3/zebra.conf new file mode 100644 index 0000000000..f490d97afe --- /dev/null +++ b/tests/topotests/bgp_update_delay/r3/zebra.conf @@ -0,0 +1,9 @@ +! exit2 +interface lo + ip address 172.16.254.254/32 +! +interface r3-eth0 + ip address 192.168.254.2/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_update_delay/r4/bgpd.conf b/tests/topotests/bgp_update_delay/r4/bgpd.conf new file mode 100644 index 0000000000..34cb429c0a --- /dev/null +++ b/tests/topotests/bgp_update_delay/r4/bgpd.conf @@ -0,0 +1,11 @@ +! exit2 +router bgp 65004 + no bgp ebgp-requires-policy + timers bgp 3 9 + neighbor 192.168.253.1 remote-as 65002 + neighbor 192.168.253.1 timers connect 10 + address-family ipv4 unicast + redistribute connected + exit-address-family + ! +! diff --git a/tests/topotests/bgp_update_delay/r4/zebra.conf b/tests/topotests/bgp_update_delay/r4/zebra.conf new file mode 100644 index 0000000000..baba04c160 --- /dev/null +++ b/tests/topotests/bgp_update_delay/r4/zebra.conf @@ -0,0 +1,9 @@ +! exit2 +interface lo + ip address 172.16.253.254/32 +! +interface r4-eth0 + ip address 192.168.253.2/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_update_delay/r5/bgpd.conf b/tests/topotests/bgp_update_delay/r5/bgpd.conf new file mode 100644 index 0000000000..66ecc703cd --- /dev/null +++ b/tests/topotests/bgp_update_delay/r5/bgpd.conf @@ -0,0 +1,11 @@ +! exit1 +router bgp 65005 + no bgp ebgp-requires-policy + timers bgp 3 9 + neighbor 192.168.252.1 remote-as 65002 + neighbor 192.168.252.1 timers connect 10 + address-family ipv4 unicast + redistribute connected + exit-address-family + ! +! diff --git a/tests/topotests/bgp_update_delay/r5/zebra.conf b/tests/topotests/bgp_update_delay/r5/zebra.conf new file mode 100644 index 0000000000..8adf6f89e0 --- /dev/null +++ b/tests/topotests/bgp_update_delay/r5/zebra.conf @@ -0,0 +1,9 @@ +! exit1 +interface lo + ip address 172.16.252.254/32 +! +interface r1-eth0 + ip address 192.168.252.2/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_update_delay/test_bgp_update_delay.py b/tests/topotests/bgp_update_delay/test_bgp_update_delay.py new file mode 100644 index 0000000000..4de7184c8e --- /dev/null +++ b/tests/topotests/bgp_update_delay/test_bgp_update_delay.py @@ -0,0 +1,295 @@ +#!/usr/bin/env python + +# +# test_bgp_update_delay.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2019 by +# Don Slice <dslice@nvidia.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 the ability to define update-delay to delay bestpath, rib install +and advertisement to peers when frr is started, restarted or "clear ip +bgp *" is performed. Test both the vrf-specific and global configuration +and operation. + +r1 +| +r2----r3 +| \ +| \ +r5 r4 + + +r2 is UUT and peers with r1, r3, and r4 in default bgp instance. +r2 peers with r5 in vrf vrf1. + +Check r2 initial convergence in default table +Define update-delay with max-delay in the default bgp instance on r2 +Shutdown peering on r1 toward r2 so that delay timers can be exercised +Clear bgp neighbors on r2 and then check for the 'in progress' indicator +Check that r2 only installs route learned from r4 after the max-delay timer expires +Define update-delay with max-delay and estabish-wait and check json output showing set +Clear neighbors on r2 and check that r3 installs route from r4 after establish-wait time +Remove update-delay timer on r2 to verify that it goes back to normal behavior +Clear neighbors on r2 and check that route install time on r2 does not delay +Define global bgp update-delay with max-delay and establish-wait on r2 +Check that r2 default instance and vrf1 have the max-delay and establish set +Clear neighbors on r2 and check route-install time is after the establish-wait timer + +Note that the keepalive/hold times were changed to 3/9 and the connect retry timer +to 10 to improve the odds the convergence timing in this test case is useful in the +event of packet loss. +""" + +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 + + +class TemplateTopo(Topo): + def build(self, *_args, **_opts): + tgen = get_topogen(self) + + for routern in range(1, 6): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r4"]) + + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r5"]) + + +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_update_delay(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + + # initial convergence without update-delay defined + def _bgp_converge(router): + output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.2 json")) + expected = { + "192.168.255.2": { + "bgpState": "Established", + "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 2}}, + } + } + return topotest.json_cmp(output, expected) + + def _bgp_check_update_delay(router): + output = json.loads(router.vtysh_cmd("show ip bgp sum json")) + expected = {"ipv4Unicast": {"updateDelayLimit": 20}} + + return topotest.json_cmp(output, expected) + + def _bgp_check_update_delay_in_progress(router): + output = json.loads(router.vtysh_cmd("show ip bgp sum json")) + expected = {"ipv4Unicast": {"updateDelayInProgress":True}} + + return topotest.json_cmp(output, expected) + + def _bgp_check_route_install(router): + output = json.loads(router.vtysh_cmd("show ip route 172.16.253.254/32 json")) + expected = {"172.16.253.254/32": [ {"protocol": "bgp"}]} + + return topotest.json_cmp(output, expected) + + def _bgp_check_update_delay_and_wait(router): + output = json.loads(router.vtysh_cmd("show ip bgp sum json")) + expected = { + "ipv4Unicast": { + "updateDelayLimit": 20, + "updateDelayEstablishWait": 10}} + + return topotest.json_cmp(output, expected) + + def _bgp_check_update_delay(router): + output = json.loads(router.vtysh_cmd("show ip bgp sum json")) + expected = {"ipv4Unicast": {"updateDelayLimit": 20}} + + return topotest.json_cmp(output, expected) + + def _bgp_check_vrf_update_delay_and_wait(router): + output = json.loads(router.vtysh_cmd("show ip bgp vrf vrf1 sum json")) + expected = { + "ipv4Unicast": { + "updateDelayLimit": 20, + "updateDelayEstablishWait": 10}} + + + return topotest.json_cmp(output, expected) + + + # Check r2 initial convergence in default table + test_func = functools.partial(_bgp_converge, router2) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + + assert result is None, 'Failed bgp convergence in "{}"'.format(router2) + + # Define update-delay with max-delay in the default bgp instance on r2 + router2.vtysh_cmd( + """ + configure terminal + router bgp 65002 + update-delay 20 + """ + ) + + # Shutdown peering on r1 toward r2 so that delay timers can be exercised + router1.vtysh_cmd( + """ + configure terminal + router bgp 65001 + neighbor 192.168.255.1 shut + """ + ) + + # Clear bgp neighbors on r2 and then check for the 'in progress' indicator + router2.vtysh_cmd("""clear ip bgp *""") + + test_func = functools.partial(_bgp_check_update_delay_in_progress, router2) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + + assert result is None, 'Failed to set update-delay max-delay timer "{}"'.format(router2) + + # Check that r2 only installs route learned from r4 after the max-delay timer expires + test_func = functools.partial(_bgp_check_route_install, router2) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + + assert result is None, 'Failed to install route after update-delay "{}"'.format(router2) + + # Define update-delay with max-delay and estabish-wait and check json output showing set + router2.vtysh_cmd( + """ + configure terminal + router bgp 65002 + update-delay 20 10 + """ + ) + + test_func = functools.partial(_bgp_check_update_delay_and_wait, router2) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + + assert result is None, 'Failed to set max-delay and establish-weight timers in "{}"'.format(router2) + + # Define update-delay with max-delay and estabish-wait and check json output showing set + router2.vtysh_cmd("""clear ip bgp *""") + + test_func = functools.partial(_bgp_check_route_install, router3) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + + assert result is None, 'Failed to installed advertised route after establish-wait timer espired "{}"'.format(router2) + + # Remove update-delay timer on r2 to verify that it goes back to normal behavior + router2.vtysh_cmd( + """ + configure terminal + router bgp 65002 + no update-delay + """ + ) + + # Clear neighbors on r2 and check that route install time on r2 does not delay + router2.vtysh_cmd("""clear ip bgp *""") + + test_func = functools.partial(_bgp_check_route_install, router2) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + + assert result is None, 'Failed to remove update-delay delay timing "{}"'.format(router2) + + # Define global bgp update-delay with max-delay and establish-wait on r2 + router2.vtysh_cmd( + """ + configure terminal + bgp update-delay 20 10 + """ + ) + + # Check that r2 default instance and vrf1 have the max-delay and establish set + test_func = functools.partial(_bgp_check_update_delay_and_wait, router2) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + + assert result is None, 'Failed to set update-delay in default instance "{}"'.format(router2) + + test_func = functools.partial(_bgp_check_vrf_update_delay_and_wait, router2) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + + assert result is None, 'Failed to set update-delay in vrf1 "{}"'.format(router2) + + # Clear neighbors on r2 and check route-install time is after the establish-wait timer + router2.vtysh_cmd("""clear ip bgp *""") + + test_func = functools.partial(_bgp_check_route_install, router3) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + + assert result is None, 'Failed to installed advertised route after establish-wait timer espired "{}"'.format(router2) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/bgpd.conf b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/bgpd.conf index 002cecd1fa..0033bc2a7a 100644 --- a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/bgpd.conf +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/bgpd.conf @@ -5,6 +5,7 @@ router bgp 101 vrf r1-cust1 neighbor r2g remote-as external neighbor r2g bfd neighbor r1-eth0 interface peer-group r2g + neighbor r1-eth0 timers 3 10 address-family ipv4 unicast redistribute connected exit-address-family diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/bgpd.conf b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/bgpd.conf index 0878b9b995..183157eb46 100644 --- a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/bgpd.conf +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/bgpd.conf @@ -5,6 +5,7 @@ router bgp 102 vrf r2-cust1 neighbor r2g remote-as external neighbor r2g bfd neighbor r2-eth0 interface peer-group r2g + neighbor r2-eth0 timers 3 10 ! address-family ipv4 unicast redistribute connected diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.py b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.py index 5d8c80c6a2..50b9b092d6 100644 --- a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.py +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.py @@ -95,7 +95,7 @@ def setup_module(mod): "ip link set {0}-eth0 master {0}-cust1", ] - for rname, router in router_list.iteritems(): + for rname, router in router_list.items(): for cmd in cmds: output = tgen.net[rname].cmd(cmd.format(rname)) @@ -109,7 +109,7 @@ def setup_module(mod): "sysctl -w net.ipv4.tcp_l3mdev_accept={}".format(l3mdev_accept) ) - for rname, router in router_list.iteritems(): + for rname, router in router_list.items(): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ) diff --git a/tests/topotests/bgp_vrf_netns/r1/bgpd.conf b/tests/topotests/bgp_vrf_netns/r1/bgpd.conf index dabf9521ac..cfe3f2e2b5 100644 --- a/tests/topotests/bgp_vrf_netns/r1/bgpd.conf +++ b/tests/topotests/bgp_vrf_netns/r1/bgpd.conf @@ -4,6 +4,7 @@ router bgp 100 vrf r1-cust1 bgp bestpath as-path multipath-relax no bgp ebgp-requires-policy neighbor 10.0.1.101 remote-as 99 + neighbor 10.0.1.101 timers 3 10 ! ! 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 ae48f01a0e..30bb9595b7 100755..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 @@ -140,7 +140,7 @@ def setup_module(module): # Starting Hosts and init ExaBGP on each of them logger.info("starting exaBGP on peer1") peer_list = tgen.exabgp_peers() - for pname, peer in peer_list.iteritems(): + for pname, peer in peer_list.items(): peer_dir = os.path.join(CWD, pname) env_file = os.path.join(CWD, "exabgp.env") logger.info("Running ExaBGP peer") diff --git a/tests/topotests/eigrp-topo1/test_eigrp_topo1.py b/tests/topotests/eigrp-topo1/test_eigrp_topo1.py index c1dd88823b..70666a3d61 100755..100644 --- a/tests/topotests/eigrp-topo1/test_eigrp_topo1.py +++ b/tests/topotests/eigrp-topo1/test_eigrp_topo1.py @@ -99,7 +99,7 @@ def setup_module(module): # This is a sample of configuration loading. router_list = tgen.routers() - for rname, router in router_list.iteritems(): + for rname, router in router_list.items(): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ) diff --git a/tests/topotests/evpn-pim-1/leaf1/bgpd.conf b/tests/topotests/evpn-pim-1/leaf1/bgpd.conf index 4dedfecd61..97fd8662f4 100644 --- a/tests/topotests/evpn-pim-1/leaf1/bgpd.conf +++ b/tests/topotests/evpn-pim-1/leaf1/bgpd.conf @@ -2,6 +2,7 @@ router bgp 65002 no bgp ebgp-requires-policy neighbor 192.168.1.1 remote-as external + neighbor 192.168.1.1 timers 3 10 redistribute connected address-family l2vpn evpn neighbor 192.168.1.1 activate diff --git a/tests/topotests/evpn-pim-1/leaf2/bgpd.conf b/tests/topotests/evpn-pim-1/leaf2/bgpd.conf index 5bc708240d..91d9bd8c8b 100644 --- a/tests/topotests/evpn-pim-1/leaf2/bgpd.conf +++ b/tests/topotests/evpn-pim-1/leaf2/bgpd.conf @@ -2,6 +2,7 @@ router bgp 65003 no bgp ebgp-requires-policy neighbor 192.168.2.1 remote-as external + neighbor 192.168.2.1 timers 3 10 redistribute connected address-family l2vpn evpn neighbor 192.168.2.1 activate diff --git a/tests/topotests/evpn-pim-1/spine/bgpd.conf b/tests/topotests/evpn-pim-1/spine/bgpd.conf index 16c17b29cc..81ab802f35 100644 --- a/tests/topotests/evpn-pim-1/spine/bgpd.conf +++ b/tests/topotests/evpn-pim-1/spine/bgpd.conf @@ -2,7 +2,9 @@ router bgp 65001 no bgp ebgp-requires-policy neighbor 192.168.1.2 remote-as external + neighbor 192.168.1.2 timers 3 10 neighbor 192.168.2.3 remote-as external + neighbor 192.168.2.3 timers 3 10 redistribute connected address-family l2vpn evpn neighbor 192.168.1.2 activate diff --git a/tests/topotests/evpn-pim-1/test_evpn_pim_topo1.py b/tests/topotests/evpn-pim-1/test_evpn_pim_topo1.py index 94bb91d49f..265124132f 100755..100644 --- a/tests/topotests/evpn-pim-1/test_evpn_pim_topo1.py +++ b/tests/topotests/evpn-pim-1/test_evpn_pim_topo1.py @@ -123,7 +123,7 @@ def setup_module(module): # tgen.mininet_cli() # This is a sample of configuration loading. router_list = tgen.routers() - for rname, router in router_list.iteritems(): + for rname, router in router_list.items(): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ) diff --git a/tests/topotests/example-test/test_template.py b/tests/topotests/example-test/test_template.py index afe974876a..4305e0199f 100755..100644 --- a/tests/topotests/example-test/test_template.py +++ b/tests/topotests/example-test/test_template.py @@ -82,7 +82,7 @@ def setup_module(mod): router_list = tgen.routers() # For all registred routers, load the zebra configuration file - for rname, router in router_list.iteritems(): + for rname, router in router_list.items(): router.load_config( TopoRouter.RD_ZEBRA, # Uncomment next line to load configuration from ./router/zebra.conf diff --git a/tests/topotests/isis-sr-topo1/test_isis_sr_topo1.py b/tests/topotests/isis-sr-topo1/test_isis_sr_topo1.py index 72bc96e4d0..d4ebe52bf6 100755..100644 --- a/tests/topotests/isis-sr-topo1/test_isis_sr_topo1.py +++ b/tests/topotests/isis-sr-topo1/test_isis_sr_topo1.py @@ -140,7 +140,7 @@ def setup_module(mod): router_list = tgen.routers() # For all registered routers, load the zebra configuration file - for rname, router in router_list.iteritems(): + for rname, router in router_list.items(): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, '{}/zebra.conf'.format(rname)) diff --git a/tests/topotests/isis-topo1-vrf/test_isis_topo1_vrf.py b/tests/topotests/isis-topo1-vrf/test_isis_topo1_vrf.py index a0e34b71b0..65515f22cc 100755..100644 --- a/tests/topotests/isis-topo1-vrf/test_isis_topo1_vrf.py +++ b/tests/topotests/isis-topo1-vrf/test_isis_topo1_vrf.py @@ -113,7 +113,7 @@ def setup_module(mod): ] # For all registered routers, load the zebra configuration file - for rname, router in tgen.routers().iteritems(): + for rname, router in tgen.routers().items(): # create VRF rx-cust1 and link rx-eth0 to rx-cust1 for cmd in cmds: output = tgen.net[rname].cmd(cmd.format(rname)) @@ -127,7 +127,7 @@ def setup_module(mod): "sysctl -w net.ipv4.tcp_l3mdev_accept={}".format(l3mdev_accept) ) - for rname, router in tgen.routers().iteritems(): + for rname, router in tgen.routers().items(): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) @@ -164,7 +164,7 @@ def test_isis_convergence(): logger.info("waiting for ISIS protocol to converge") - for rname, router in tgen.routers().iteritems(): + for rname, router in tgen.routers().items(): filename = "{0}/{1}/{1}_topology.json".format(CWD, rname) expected = json.loads(open(filename).read()) def compare_isis_topology(router, expected): @@ -186,13 +186,13 @@ def test_isis_route_installation(): logger.info("Checking routers for installed ISIS vrf routes") # Check for routes in 'show ip route vrf {}-cust1 json' - for rname, router in tgen.routers().iteritems(): + for rname, router in tgen.routers().items(): filename = "{0}/{1}/{1}_route.json".format(CWD, rname) expected = json.loads(open(filename, "r").read()) actual = router.vtysh_cmd("show ip route vrf {0}-cust1 json".format(rname) , isjson=True) # Older FRR versions don't list interfaces in some ISIS routes if router.has_version("<", "3.1"): - for network, routes in expected.iteritems(): + for network, routes in expected.items(): for route in routes: if route["protocol"] != "isis": continue @@ -220,14 +220,14 @@ def test_isis_linux_route_installation(): logger.info("Checking routers for installed ISIS vrf routes in OS") # Check for routes in `ip route show vrf {}-cust1` - for rname, router in tgen.routers().iteritems(): + for rname, router in tgen.routers().items(): filename = "{0}/{1}/{1}_route_linux.json".format(CWD, rname) expected = json.loads(open(filename, "r").read()) actual = topotest.ip4_vrf_route(router) # Older FRR versions install routes using different proto if router.has_version("<", "3.1"): - for network, netoptions in expected.iteritems(): + for network, netoptions in expected.items(): if "proto" in netoptions and netoptions["proto"] == "187": netoptions["proto"] = "zebra" @@ -243,14 +243,14 @@ def test_isis_route6_installation(): logger.info("Checking routers for installed ISIS vrf IPv6 routes") # Check for routes in 'show ipv6 route vrf {}-cust1 json' - for rname, router in tgen.routers().iteritems(): + for rname, router in tgen.routers().items(): filename = "{0}/{1}/{1}_route6.json".format(CWD, rname) expected = json.loads(open(filename, "r").read()) actual = router.vtysh_cmd("show ipv6 route vrf {}-cust1 json".format(rname) , isjson=True) # Older FRR versions don't list interfaces in some ISIS routes if router.has_version("<", "3.1"): - for network, routes in expected.iteritems(): + for network, routes in expected.items(): for route in routes: if route["protocol"] != "isis": continue @@ -277,14 +277,14 @@ def test_isis_linux_route6_installation(): logger.info("Checking routers for installed ISIS vrf IPv6 routes in OS") # Check for routes in `ip -6 route show vrf {}-cust1` - for rname, router in tgen.routers().iteritems(): + for rname, router in tgen.routers().items(): filename = "{0}/{1}/{1}_route6_linux.json".format(CWD, rname) expected = json.loads(open(filename, "r").read()) actual = topotest.ip6_vrf_route(router) # Older FRR versions install routes using different proto if router.has_version("<", "3.1"): - for network, netoptions in expected.iteritems(): + for network, netoptions in expected.items(): if "proto" in netoptions and netoptions["proto"] == "187": netoptions["proto"] = "zebra" @@ -323,7 +323,7 @@ def dict_merge(dct, merge_dct): Source: https://gist.github.com/angstwad/bf22d1822c38a92ec0a9 """ - for k, v in merge_dct.iteritems(): + for k, v in merge_dct.items(): if ( k in dct and isinstance(dct[k], dict) diff --git a/tests/topotests/isis-topo1/test_isis_topo1.py b/tests/topotests/isis-topo1/test_isis_topo1.py index 6b1d9a8964..71005a0362 100644 --- a/tests/topotests/isis-topo1/test_isis_topo1.py +++ b/tests/topotests/isis-topo1/test_isis_topo1.py @@ -91,7 +91,7 @@ def setup_module(mod): tgen.start_topology() # For all registered routers, load the zebra configuration file - for rname, router in tgen.routers().iteritems(): + for rname, router in tgen.routers().items(): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ) @@ -129,12 +129,12 @@ def test_isis_convergence(): logger.info("waiting for ISIS protocol to converge") # Code to generate the json files. - # for rname, router in tgen.routers().iteritems(): + # for rname, router in tgen.routers().items(): # open('/tmp/{}_topology.json'.format(rname), 'w').write( # json.dumps(show_isis_topology(router), indent=2, sort_keys=True) # ) - for rname, router in tgen.routers().iteritems(): + for rname, router in tgen.routers().items(): filename = "{0}/{1}/{1}_topology.json".format(CWD, rname) expected = json.loads(open(filename).read()) @@ -158,14 +158,14 @@ def test_isis_route_installation(): logger.info("Checking routers for installed ISIS routes") # Check for routes in 'show ip route json' - for rname, router in tgen.routers().iteritems(): + for rname, router in tgen.routers().items(): filename = "{0}/{1}/{1}_route.json".format(CWD, rname) expected = json.loads(open(filename, "r").read()) actual = router.vtysh_cmd("show ip route json", isjson=True) # Older FRR versions don't list interfaces in some ISIS routes if router.has_version("<", "3.1"): - for network, routes in expected.iteritems(): + for network, routes in expected.items(): for route in routes: if route["protocol"] != "isis": continue @@ -188,14 +188,14 @@ def test_isis_linux_route_installation(): logger.info("Checking routers for installed ISIS routes in OS") # Check for routes in `ip route` - for rname, router in tgen.routers().iteritems(): + for rname, router in tgen.routers().items(): filename = "{0}/{1}/{1}_route_linux.json".format(CWD, rname) expected = json.loads(open(filename, "r").read()) actual = topotest.ip4_route(router) # Older FRR versions install routes using different proto if router.has_version("<", "3.1"): - for network, netoptions in expected.iteritems(): + for network, netoptions in expected.items(): if "proto" in netoptions and netoptions["proto"] == "187": netoptions["proto"] = "zebra" @@ -213,14 +213,14 @@ def test_isis_route6_installation(): logger.info("Checking routers for installed ISIS IPv6 routes") # Check for routes in 'show ip route json' - for rname, router in tgen.routers().iteritems(): + for rname, router in tgen.routers().items(): filename = "{0}/{1}/{1}_route6.json".format(CWD, rname) expected = json.loads(open(filename, "r").read()) actual = router.vtysh_cmd("show ipv6 route json", isjson=True) # Older FRR versions don't list interfaces in some ISIS routes if router.has_version("<", "3.1"): - for network, routes in expected.iteritems(): + for network, routes in expected.items(): for route in routes: # Older versions display different metrics for IPv6 routes route.pop("metric", None) @@ -246,14 +246,14 @@ def test_isis_linux_route6_installation(): logger.info("Checking routers for installed ISIS IPv6 routes in OS") # Check for routes in `ip route` - for rname, router in tgen.routers().iteritems(): + for rname, router in tgen.routers().items(): filename = "{0}/{1}/{1}_route6_linux.json".format(CWD, rname) expected = json.loads(open(filename, "r").read()) actual = topotest.ip6_route(router) # Older FRR versions install routes using different proto if router.has_version("<", "3.1"): - for network, netoptions in expected.iteritems(): + for network, netoptions in expected.items(): if "proto" in netoptions and netoptions["proto"] == "187": netoptions["proto"] = "zebra" @@ -293,7 +293,7 @@ def dict_merge(dct, merge_dct): Source: https://gist.github.com/angstwad/bf22d1822c38a92ec0a9 """ - for k, v in merge_dct.iteritems(): + for k, v in merge_dct.items(): if ( k in dct and isinstance(dct[k], dict) diff --git a/tests/topotests/ldp-oc-acl-topo1/test_ldp_oc_acl_topo1.py b/tests/topotests/ldp-oc-acl-topo1/test_ldp_oc_acl_topo1.py index 450d35e16c..dadb2065e6 100755..100644 --- a/tests/topotests/ldp-oc-acl-topo1/test_ldp_oc_acl_topo1.py +++ b/tests/topotests/ldp-oc-acl-topo1/test_ldp_oc_acl_topo1.py @@ -117,7 +117,7 @@ def setup_module(mod): router_list = tgen.routers() # For all registered routers, load the zebra configuration file - for rname, router in router_list.iteritems(): + for rname, router in router_list.items(): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ) diff --git a/tests/topotests/ldp-oc-topo1/test_ldp_oc_topo1.py b/tests/topotests/ldp-oc-topo1/test_ldp_oc_topo1.py index ac99eb1a26..ea449e4aba 100755..100644 --- a/tests/topotests/ldp-oc-topo1/test_ldp_oc_topo1.py +++ b/tests/topotests/ldp-oc-topo1/test_ldp_oc_topo1.py @@ -117,7 +117,7 @@ def setup_module(mod): router_list = tgen.routers() # For all registered routers, load the zebra configuration file - for rname, router in router_list.iteritems(): + for rname, router in router_list.items(): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ) diff --git a/tests/topotests/ldp-sync-isis-topo1/ce1/zebra.conf b/tests/topotests/ldp-sync-isis-topo1/ce1/zebra.conf new file mode 100644 index 0000000000..6f165e2724 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/ce1/zebra.conf @@ -0,0 +1,12 @@ +log file zebra.log +! +hostname ce1 +! +interface ce1-eth0 + ip address 172.16.1.1/24 + no link-detect +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ldp-sync-isis-topo1/ce2/zebra.conf b/tests/topotests/ldp-sync-isis-topo1/ce2/zebra.conf new file mode 100644 index 0000000000..ac02d0f9a4 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/ce2/zebra.conf @@ -0,0 +1,12 @@ +log file zebra.log +! +hostname ce2 +! +interface ce2-eth0 + ip address 172.16.1.2/24 + no link-detect +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ldp-sync-isis-topo1/ce3/zebra.conf b/tests/topotests/ldp-sync-isis-topo1/ce3/zebra.conf new file mode 100644 index 0000000000..c6a5824d15 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/ce3/zebra.conf @@ -0,0 +1,12 @@ +log file zebra.log +! +hostname ce3 +! +interface ce3-eth0 + ip address 172.16.1.3/24 + no link-detect +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ldp-sync-isis-topo1/r1/isisd.conf b/tests/topotests/ldp-sync-isis-topo1/r1/isisd.conf new file mode 100644 index 0000000000..af8d117bc1 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r1/isisd.conf @@ -0,0 +1,26 @@ +hostname r1 +log file isisd.log +debug isis adj-packets +debug isis events +debug isis update-packets +debug isis ldp-sync +! +router isis 1 + net 10.0000.0000.0000.0000.0000.0000.0000.0000.0001.00 + metric-style wide + redistribute ipv4 connected level-1 + redistribute ipv6 connected level-1 + mpls ldp-sync +! +interface r1-eth1 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis circuit-type level-1 +! +interface r1-eth2 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis circuit-type level-1 +! diff --git a/tests/topotests/ldp-sync-isis-topo1/r1/ldpd.conf b/tests/topotests/ldp-sync-isis-topo1/r1/ldpd.conf new file mode 100644 index 0000000000..b9c32d3000 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r1/ldpd.conf @@ -0,0 +1,33 @@ +hostname r1 +log file ldpd.log +! +debug mpls ldp zebra +debug mpls ldp event +debug mpls ldp errors +debug mpls ldp sync +! +mpls ldp + router-id 1.1.1.1 + ! + address-family ipv4 + discovery transport-address 1.1.1.1 + label local allocate host-routes + ! + ttl-security disable + ! + interface r1-eth1 + ! + interface r1-eth2 + ! + ! +! +l2vpn CUST_A type vpls + member interface r1-eth0 + ! + member pseudowire r1-mpw0 + neighbor lsr-id 2.2.2.2 + pw-id 100 + ! +! +line vty +! diff --git a/tests/topotests/ldp-sync-isis-topo1/r1/show_ip_route.ref b/tests/topotests/ldp-sync-isis-topo1/r1/show_ip_route.ref new file mode 100644 index 0000000000..dc8f19dad0 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r1/show_ip_route.ref @@ -0,0 +1,143 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceIndex":1, + "interfaceName":"lo", + "active":true + } + ] + } + ], + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "distance":115, + "metric":10, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceIndex":3, + "interfaceName":"r1-eth1", + "active":true + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "distance":115, + "metric":10, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.3", + "afi":"ipv4", + "interfaceIndex":4, + "interfaceName":"r1-eth2", + "active":true + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "distance":115, + "metric":10, + "nexthops":[ + { + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceIndex":3, + "interfaceName":"r1-eth1" + } + ] + }, + { + "prefix":"10.0.1.0\/24", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceIndex":3, + "interfaceName":"r1-eth1", + "active":true + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "distance":115, + "metric":10, + "nexthops":[ + { + "ip":"10.0.2.3", + "afi":"ipv4", + "interfaceIndex":4, + "interfaceName":"r1-eth2" + } + ] + }, + { + "prefix":"10.0.2.0\/24", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceIndex":4, + "interfaceName":"r1-eth2", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "selected":true, + "distance":115, + "metric":10, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceIndex":3, + "interfaceName":"r1-eth1", + "active":true + }, + { + "fib":true, + "ip":"10.0.2.3", + "afi":"ipv4", + "interfaceIndex":4, + "interfaceName":"r1-eth2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r1/show_isis_interface_detail.ref b/tests/topotests/ldp-sync-isis-topo1/r1/show_isis_interface_detail.ref new file mode 100644 index 0000000000..d8fb27af8c --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r1/show_isis_interface_detail.ref @@ -0,0 +1,16 @@ +{ + "1": { + "r1-eth1": [ + { + "level": "Level-1", + "metric": "10" + } + ], + "r1-eth2": [ + { + "level": "Level-1", + "metric": "10" + } + ] + } +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r1/show_isis_interface_detail_r1_eth1_shutdown.ref b/tests/topotests/ldp-sync-isis-topo1/r1/show_isis_interface_detail_r1_eth1_shutdown.ref new file mode 100644 index 0000000000..f77d65ebc1 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r1/show_isis_interface_detail_r1_eth1_shutdown.ref @@ -0,0 +1,16 @@ +{ + "1": { + "r1-eth1": [ + { + "level": "Level-1", + "metric": "16777214" + } + ], + "r1-eth2": [ + { + "level": "Level-1", + "metric": "10" + } + ] + } +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r1/show_isis_interface_detail_r2_eth1_shutdown.ref b/tests/topotests/ldp-sync-isis-topo1/r1/show_isis_interface_detail_r2_eth1_shutdown.ref new file mode 100644 index 0000000000..f77d65ebc1 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r1/show_isis_interface_detail_r2_eth1_shutdown.ref @@ -0,0 +1,16 @@ +{ + "1": { + "r1-eth1": [ + { + "level": "Level-1", + "metric": "16777214" + } + ], + "r1-eth2": [ + { + "level": "Level-1", + "metric": "10" + } + ] + } +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r1/show_isis_ldp_sync.ref b/tests/topotests/ldp-sync-isis-topo1/r1/show_isis_ldp_sync.ref new file mode 100644 index 0000000000..b699e8c145 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r1/show_isis_ldp_sync.ref @@ -0,0 +1,13 @@ +{ + "r1-eth1":{ + "ldpIgpSyncEnabled":true, + "holdDownTimeInSec":0, + "ldpIgpSyncState":"Sync achieved" + }, + "r1-eth2":{ + "ldpIgpSyncEnabled":true, + "holdDownTimeInSec":0, + "ldpIgpSyncState":"Sync achieved" + } + +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r1/show_isis_ldp_sync_r1_eth1_shutdown.ref b/tests/topotests/ldp-sync-isis-topo1/r1/show_isis_ldp_sync_r1_eth1_shutdown.ref new file mode 100644 index 0000000000..c63bbea77f --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r1/show_isis_ldp_sync_r1_eth1_shutdown.ref @@ -0,0 +1,13 @@ +{ + "r1-eth1":{ + "ldpIgpSyncEnabled":true, + "holdDownTimeInSec":0, + "ldpIgpSyncState":"Sync not achieved" + }, + "r1-eth2":{ + "ldpIgpSyncEnabled":true, + "holdDownTimeInSec":0, + "ldpIgpSyncState":"Sync achieved" + } + +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r1/show_isis_ldp_sync_r2_eth1_shutdown.ref b/tests/topotests/ldp-sync-isis-topo1/r1/show_isis_ldp_sync_r2_eth1_shutdown.ref new file mode 100644 index 0000000000..c63bbea77f --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r1/show_isis_ldp_sync_r2_eth1_shutdown.ref @@ -0,0 +1,13 @@ +{ + "r1-eth1":{ + "ldpIgpSyncEnabled":true, + "holdDownTimeInSec":0, + "ldpIgpSyncState":"Sync not achieved" + }, + "r1-eth2":{ + "ldpIgpSyncEnabled":true, + "holdDownTimeInSec":0, + "ldpIgpSyncState":"Sync achieved" + } + +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r1/show_l2vpn_binding.ref b/tests/topotests/ldp-sync-isis-topo1/r1/show_l2vpn_binding.ref new file mode 100644 index 0000000000..b3de7e2c66 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r1/show_l2vpn_binding.ref @@ -0,0 +1,16 @@ +{ + "2.2.2.2: 100":{ + "destination":"2.2.2.2", + "vcId":100, + "localLabel":16, + "localControlWord":1, + "localVcType":"Ethernet", + "localGroupID":0, + "localIfMtu":1500, + "remoteLabel":16, + "remoteControlWord":1, + "remoteVcType":"Ethernet", + "remoteGroupID":0, + "remoteIfMtu":1500 + } +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r1/show_l2vpn_vc.ref b/tests/topotests/ldp-sync-isis-topo1/r1/show_l2vpn_vc.ref new file mode 100644 index 0000000000..29e9df1089 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r1/show_l2vpn_vc.ref @@ -0,0 +1,8 @@ +{ + "r1-mpw0":{ + "peerId":"2.2.2.2", + "vcId":100, + "VpnName":"CUST_A", + "status":"up" + } +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r1/show_ldp_binding.ref b/tests/topotests/ldp-sync-isis-topo1/r1/show_ldp_binding.ref new file mode 100644 index 0000000000..b3a12ec53f --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r1/show_ldp_binding.ref @@ -0,0 +1,44 @@ +{ + "bindings":[ + { + "addressFamily":"ipv4", + "prefix":"1.1.1.1/32", + "neighborId":"2.2.2.2", + "localLabel":"imp-null", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"1.1.1.1/32", + "neighborId":"3.3.3.3", + "localLabel":"imp-null", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"2.2.2.2/32", + "neighborId":"2.2.2.2", + "remoteLabel":"imp-null", + "inUse":1 + }, + { + "addressFamily":"ipv4", + "prefix":"2.2.2.2/32", + "neighborId":"3.3.3.3", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"3.3.3.3/32", + "neighborId":"2.2.2.2", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"3.3.3.3/32", + "neighborId":"3.3.3.3", + "remoteLabel":"imp-null", + "inUse":1 + } + ] +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r1/show_ldp_discovery.ref b/tests/topotests/ldp-sync-isis-topo1/r1/show_ldp_discovery.ref new file mode 100644 index 0000000000..9301e60c67 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r1/show_ldp_discovery.ref @@ -0,0 +1,25 @@ +{ + "adjacencies":[ + { + "addressFamily":"ipv4", + "neighborId":"2.2.2.2", + "type":"link", + "interface":"r1-eth1", + "helloHoldtime":15 + }, + { + "addressFamily":"ipv4", + "neighborId":"2.2.2.2", + "type":"targeted", + "peer":"2.2.2.2", + "helloHoldtime":45 + }, + { + "addressFamily":"ipv4", + "neighborId":"3.3.3.3", + "type":"link", + "interface":"r1-eth2", + "helloHoldtime":15 + } + ] +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r1/show_ldp_igp_sync.ref b/tests/topotests/ldp-sync-isis-topo1/r1/show_ldp_igp_sync.ref new file mode 100644 index 0000000000..54d015fef9 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r1/show_ldp_igp_sync.ref @@ -0,0 +1,16 @@ +{ + "r1-eth1":{ + "state":"labelExchangeComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"2.2.2.2" + }, + "r1-eth2":{ + "state":"labelExchangeComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"3.3.3.3" + } +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r1/show_ldp_igp_sync_r1_eth1_shutdown.ref b/tests/topotests/ldp-sync-isis-topo1/r1/show_ldp_igp_sync_r1_eth1_shutdown.ref new file mode 100644 index 0000000000..2232069f68 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r1/show_ldp_igp_sync_r1_eth1_shutdown.ref @@ -0,0 +1,16 @@ +{ + "r1-eth1":{ + "state":"labelExchangeNotComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"" + }, + "r1-eth2":{ + "state":"labelExchangeComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"3.3.3.3" + } +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r1/show_ldp_neighbor.ref b/tests/topotests/ldp-sync-isis-topo1/r1/show_ldp_neighbor.ref new file mode 100644 index 0000000000..40d8ebeb90 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r1/show_ldp_neighbor.ref @@ -0,0 +1,16 @@ +{ + "neighbors":[ + { + "addressFamily":"ipv4", + "neighborId":"2.2.2.2", + "state":"OPERATIONAL", + "transportAddress":"2.2.2.2" + }, + { + "addressFamily":"ipv4", + "neighborId":"3.3.3.3", + "state":"OPERATIONAL", + "transportAddress":"3.3.3.3" + } + ] +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/ldp-sync-isis-topo1/r1/show_yang_interface_isis_adjacencies.ref new file mode 100644 index 0000000000..6138d03672 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r1/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1,42 @@ +{ + "frr-interface:lib": { + "interface": [ + { + "name": "r1-eth1", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0002", + "neighbor-extended-circuit-id": 0, + "state": "up" + } + ] + } + } + } + }, + { + "name": "r1-eth2", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0003", + "neighbor-extended-circuit-id": 0, + "state": "up" + } + ] + } + } + } + } + ] + } +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r1/zebra.conf b/tests/topotests/ldp-sync-isis-topo1/r1/zebra.conf new file mode 100644 index 0000000000..ea047355ad --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r1/zebra.conf @@ -0,0 +1,29 @@ +log file zebra.log +! +hostname r1 +! +debug zebra kernel +debug zebra rib detailed +debug zebra dplane detailed +debug zebra nht +debug zebra pseudowires +debug zebra mpls +! +interface lo + ip address 1.1.1.1/32 +! +interface r1-eth0 + description to s1 +! +interface r1-eth1 + description to s4 + ip address 10.0.1.1/24 +! +interface r1-eth2 + description to s5 + ip address 10.0.2.1/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ldp-sync-isis-topo1/r2/isisd.conf b/tests/topotests/ldp-sync-isis-topo1/r2/isisd.conf new file mode 100644 index 0000000000..e477bce827 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r2/isisd.conf @@ -0,0 +1,27 @@ +hostname r2 +log file isisd.log +debug isis adj-packets +debug isis events +debug isis update-packets +debug isis ldp-sync +! +router isis 1 + net 10.0000.0000.0000.0000.0000.0000.0000.0000.0002.00 + metric-style wide + redistribute ipv4 connected level-1 + redistribute ipv6 connected level-1 + mpls ldp-sync +! +interface r2-eth1 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis circuit-type level-1 +! +interface r2-eth2 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis circuit-type level-1 + no isis mpls ldp-sync +! diff --git a/tests/topotests/ldp-sync-isis-topo1/r2/ldpd.conf b/tests/topotests/ldp-sync-isis-topo1/r2/ldpd.conf new file mode 100644 index 0000000000..52398b1b72 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r2/ldpd.conf @@ -0,0 +1,33 @@ +hostname r2 +log file ldpd.log +! +debug mpls ldp zebra +debug mpls ldp event +debug mpls ldp errors +debug mpls ldp sync +! +mpls ldp + router-id 2.2.2.2 + ! + address-family ipv4 + discovery transport-address 2.2.2.2 + label local allocate host-routes + ! + ttl-security disable + ! + interface r2-eth1 + ! + interface r2-eth2 + ! + ! +! +l2vpn CUST_A type vpls + member interface r2-eth0 + ! + member pseudowire r2-mpw0 + neighbor lsr-id 1.1.1.1 + pw-id 100 + ! +! +line vty +! diff --git a/tests/topotests/ldp-sync-isis-topo1/r2/ospfd.conf b/tests/topotests/ldp-sync-isis-topo1/r2/ospfd.conf new file mode 100644 index 0000000000..f93f6aed56 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r2/ospfd.conf @@ -0,0 +1,19 @@ +hostname r2 +log file ospfd.log +debug ospf zebra interface +debug ospf ldp-sync +! +router ospf + router-id 2.2.2.2 + network 0.0.0.0/0 area 0 + mpls ldp-sync + mpls ldp-sync holddown 50 +! +interface r2-eth1 + ip ospf network point-to-point + ip ospf mpls ldp-sync holddown 300 +! +interface r2-eth2 + ip ospf network point-to-point + no ip ospf mpls ldp-sync +! diff --git a/tests/topotests/ldp-sync-isis-topo1/r2/show_ip_route.ref b/tests/topotests/ldp-sync-isis-topo1/r2/show_ip_route.ref new file mode 100644 index 0000000000..2bcee96064 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r2/show_ip_route.ref @@ -0,0 +1,143 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "distance":115, + "metric":10, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceIndex":3, + "interfaceName":"r2-eth1", + "active":true + } + ] + } + ], + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceIndex":1, + "interfaceName":"lo", + "active":true + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "distance":115, + "metric":10, + "nexthops":[ + { + "fib":true, + "ip":"10.0.3.3", + "afi":"ipv4", + "interfaceIndex":4, + "interfaceName":"r2-eth2", + "active":true + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "distance":115, + "metric":10, + "nexthops":[ + { + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceIndex":3, + "interfaceName":"r2-eth1" + } + ] + }, + { + "prefix":"10.0.1.0\/24", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceIndex":3, + "interfaceName":"r2-eth1", + "active":true + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "selected":true, + "distance":115, + "metric":10, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceIndex":3, + "interfaceName":"r2-eth1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.3", + "afi":"ipv4", + "interfaceIndex":4, + "interfaceName":"r2-eth2", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "distance":115, + "metric":10, + "nexthops":[ + { + "ip":"10.0.3.3", + "afi":"ipv4", + "interfaceIndex":4, + "interfaceName":"r2-eth2" + } + ] + }, + { + "prefix":"10.0.3.0\/24", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceIndex":4, + "interfaceName":"r2-eth2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r2/show_isis_interface_detail.ref b/tests/topotests/ldp-sync-isis-topo1/r2/show_isis_interface_detail.ref new file mode 100644 index 0000000000..844aa9402a --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r2/show_isis_interface_detail.ref @@ -0,0 +1,16 @@ +{ + "1": { + "r2-eth1": [ + { + "level": "Level-1", + "metric": "10" + } + ], + "r2-eth2": [ + { + "level": "Level-1", + "metric": "10" + } + ] + } +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r2/show_isis_interface_detail_r1_eth1_shutdown.ref b/tests/topotests/ldp-sync-isis-topo1/r2/show_isis_interface_detail_r1_eth1_shutdown.ref new file mode 100644 index 0000000000..821ec70ba5 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r2/show_isis_interface_detail_r1_eth1_shutdown.ref @@ -0,0 +1,16 @@ +{ + "1": { + "r2-eth1": [ + { + "level": "Level-1", + "metric": "16777214" + } + ], + "r2-eth2": [ + { + "level": "Level-1", + "metric": "10" + } + ] + } +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r2/show_isis_interface_detail_r2_eth1_shutdown.ref b/tests/topotests/ldp-sync-isis-topo1/r2/show_isis_interface_detail_r2_eth1_shutdown.ref new file mode 100644 index 0000000000..821ec70ba5 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r2/show_isis_interface_detail_r2_eth1_shutdown.ref @@ -0,0 +1,16 @@ +{ + "1": { + "r2-eth1": [ + { + "level": "Level-1", + "metric": "16777214" + } + ], + "r2-eth2": [ + { + "level": "Level-1", + "metric": "10" + } + ] + } +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r2/show_isis_ldp_sync.ref b/tests/topotests/ldp-sync-isis-topo1/r2/show_isis_ldp_sync.ref new file mode 100644 index 0000000000..433d89bd16 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r2/show_isis_ldp_sync.ref @@ -0,0 +1,13 @@ +{ + "r2-eth1":{ + "ldpIgpSyncEnabled":true, + "holdDownTimeInSec":0, + "ldpIgpSyncState":"Sync achieved" + }, + "r2-eth2":{ + "ldpIgpSyncEnabled":false, + "holdDownTimeInSec":0, + "ldpIgpSyncState":"Sync not required" + } + +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r2/show_isis_ldp_sync_r1_eth1_shutdown.ref b/tests/topotests/ldp-sync-isis-topo1/r2/show_isis_ldp_sync_r1_eth1_shutdown.ref new file mode 100644 index 0000000000..2f3eae47c8 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r2/show_isis_ldp_sync_r1_eth1_shutdown.ref @@ -0,0 +1,13 @@ +{ + "r2-eth1":{ + "ldpIgpSyncEnabled":true, + "holdDownTimeInSec":0, + "ldpIgpSyncState":"Sync not achieved" + }, + "r2-eth2":{ + "ldpIgpSyncEnabled":false, + "holdDownTimeInSec":0, + "ldpIgpSyncState":"Sync not required" + } + +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r2/show_isis_ldp_sync_r2_eth1_shutdown.ref b/tests/topotests/ldp-sync-isis-topo1/r2/show_isis_ldp_sync_r2_eth1_shutdown.ref new file mode 100644 index 0000000000..2f3eae47c8 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r2/show_isis_ldp_sync_r2_eth1_shutdown.ref @@ -0,0 +1,13 @@ +{ + "r2-eth1":{ + "ldpIgpSyncEnabled":true, + "holdDownTimeInSec":0, + "ldpIgpSyncState":"Sync not achieved" + }, + "r2-eth2":{ + "ldpIgpSyncEnabled":false, + "holdDownTimeInSec":0, + "ldpIgpSyncState":"Sync not required" + } + +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r2/show_l2vpn_binding.ref b/tests/topotests/ldp-sync-isis-topo1/r2/show_l2vpn_binding.ref new file mode 100644 index 0000000000..42c5a1cbd9 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r2/show_l2vpn_binding.ref @@ -0,0 +1,16 @@ +{ + "1.1.1.1: 100":{ + "destination":"1.1.1.1", + "vcId":100, + "localLabel":16, + "localControlWord":1, + "localVcType":"Ethernet", + "localGroupID":0, + "localIfMtu":1500, + "remoteLabel":16, + "remoteControlWord":1, + "remoteVcType":"Ethernet", + "remoteGroupID":0, + "remoteIfMtu":1500 + } +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r2/show_l2vpn_vc.ref b/tests/topotests/ldp-sync-isis-topo1/r2/show_l2vpn_vc.ref new file mode 100644 index 0000000000..942ed23a1e --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r2/show_l2vpn_vc.ref @@ -0,0 +1,8 @@ +{ + "r2-mpw0":{ + "peerId":"1.1.1.1", + "vcId":100, + "VpnName":"CUST_A", + "status":"up" + } +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r2/show_ldp_binding.ref b/tests/topotests/ldp-sync-isis-topo1/r2/show_ldp_binding.ref new file mode 100644 index 0000000000..c641fb47e6 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r2/show_ldp_binding.ref @@ -0,0 +1,44 @@ +{ + "bindings":[ + { + "addressFamily":"ipv4", + "prefix":"1.1.1.1/32", + "neighborId":"1.1.1.1", + "remoteLabel":"imp-null", + "inUse":1 + }, + { + "addressFamily":"ipv4", + "prefix":"1.1.1.1/32", + "neighborId":"3.3.3.3", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"2.2.2.2/32", + "neighborId":"1.1.1.1", + "localLabel":"imp-null", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"2.2.2.2/32", + "neighborId":"3.3.3.3", + "localLabel":"imp-null", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"3.3.3.3/32", + "neighborId":"1.1.1.1", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"3.3.3.3/32", + "neighborId":"3.3.3.3", + "remoteLabel":"imp-null", + "inUse":1 + } + ] +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r2/show_ldp_discovery.ref b/tests/topotests/ldp-sync-isis-topo1/r2/show_ldp_discovery.ref new file mode 100644 index 0000000000..26801acade --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r2/show_ldp_discovery.ref @@ -0,0 +1,25 @@ +{ + "adjacencies":[ + { + "addressFamily":"ipv4", + "neighborId":"1.1.1.1", + "type":"link", + "interface":"r2-eth1", + "helloHoldtime":15 + }, + { + "addressFamily":"ipv4", + "neighborId":"1.1.1.1", + "type":"targeted", + "peer":"1.1.1.1", + "helloHoldtime":45 + }, + { + "addressFamily":"ipv4", + "neighborId":"3.3.3.3", + "type":"link", + "interface":"r2-eth2", + "helloHoldtime":15 + } + ] +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r2/show_ldp_igp_sync.ref b/tests/topotests/ldp-sync-isis-topo1/r2/show_ldp_igp_sync.ref new file mode 100644 index 0000000000..f2b24d7d62 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r2/show_ldp_igp_sync.ref @@ -0,0 +1,16 @@ +{ + "r2-eth1":{ + "state":"labelExchangeComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"1.1.1.1" + }, + "r2-eth2":{ + "state":"labelExchangeComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"3.3.3.3" + } +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r2/show_ldp_igp_sync_r1_eth1_shutdown.ref b/tests/topotests/ldp-sync-isis-topo1/r2/show_ldp_igp_sync_r1_eth1_shutdown.ref new file mode 100644 index 0000000000..b5508dd35c --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r2/show_ldp_igp_sync_r1_eth1_shutdown.ref @@ -0,0 +1,16 @@ +{ + "r2-eth1":{ + "state":"labelExchangeNotComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"" + }, + "r2-eth2":{ + "state":"labelExchangeComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"3.3.3.3" + } +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r2/show_ldp_igp_sync_r2_eth1_shutdown.ref b/tests/topotests/ldp-sync-isis-topo1/r2/show_ldp_igp_sync_r2_eth1_shutdown.ref new file mode 100644 index 0000000000..f2b24d7d62 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r2/show_ldp_igp_sync_r2_eth1_shutdown.ref @@ -0,0 +1,16 @@ +{ + "r2-eth1":{ + "state":"labelExchangeComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"1.1.1.1" + }, + "r2-eth2":{ + "state":"labelExchangeComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"3.3.3.3" + } +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r2/show_ldp_neighbor.ref b/tests/topotests/ldp-sync-isis-topo1/r2/show_ldp_neighbor.ref new file mode 100644 index 0000000000..eed35289ea --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r2/show_ldp_neighbor.ref @@ -0,0 +1,16 @@ +{ + "neighbors":[ + { + "addressFamily":"ipv4", + "neighborId":"1.1.1.1", + "state":"OPERATIONAL", + "transportAddress":"1.1.1.1" + }, + { + "addressFamily":"ipv4", + "neighborId":"3.3.3.3", + "state":"OPERATIONAL", + "transportAddress":"3.3.3.3" + } + ] +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r2/show_yang_interface_isis_adjacencies.ref b/tests/topotests/ldp-sync-isis-topo1/r2/show_yang_interface_isis_adjacencies.ref new file mode 100644 index 0000000000..4dd6ddd76b --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r2/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1,42 @@ +{ + "frr-interface:lib": { + "interface": [ + { + "name": "r2-eth1", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0001", + "neighbor-extended-circuit-id": 0, + "state": "up" + } + ] + } + } + } + }, + { + "name": "r2-eth2", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0003", + "neighbor-extended-circuit-id": 0, + "state": "up" + } + ] + } + } + } + } + ] + } +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r2/zebra.conf b/tests/topotests/ldp-sync-isis-topo1/r2/zebra.conf new file mode 100644 index 0000000000..c244442876 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r2/zebra.conf @@ -0,0 +1,28 @@ +log file zebra.log +! +hostname r2 +! +debug zebra rib detailed +debug zebra dplane detailed +debug zebra kernel +debug zebra nht +debug zebra pseudowires +! +interface lo + ip address 2.2.2.2/32 +! +interface r2-eth0 + description to s2 +! +interface r2-eth1 + description to s4 + ip address 10.0.1.2/24 +! +interface r2-eth2 + description to s6 + ip address 10.0.3.2/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ldp-sync-isis-topo1/r3/isisd.conf b/tests/topotests/ldp-sync-isis-topo1/r3/isisd.conf new file mode 100644 index 0000000000..e50fb077ba --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r3/isisd.conf @@ -0,0 +1,28 @@ +hostname r3 +log file isisd.log +debug isis adj-packets +debug isis events +debug isis update-packets +debug isis ldp-sync +! +router isis 1 + net 10.0000.0000.0000.0000.0000.0000.0000.0000.0003.00 + metric-style wide + redistribute ipv4 connected level-1 + redistribute ipv6 connected level-1 + mpls ldp-sync + mpls ldp-sync holddown 50 +! +interface r3-eth1 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis circuit-type level-1 + no isis mpls ldp-sync +! +interface r3-eth2 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis circuit-type level-1 +! diff --git a/tests/topotests/ldp-sync-isis-topo1/r3/ldpd.conf b/tests/topotests/ldp-sync-isis-topo1/r3/ldpd.conf new file mode 100644 index 0000000000..2935caf13b --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r3/ldpd.conf @@ -0,0 +1,25 @@ +hostname r3 +log file ldpd.log +! +debug mpls ldp zebra +debug mpls ldp event +debug mpls ldp errors +debug mpls ldp sync +! +mpls ldp + router-id 3.3.3.3 + ! + address-family ipv4 + discovery transport-address 3.3.3.3 + label local allocate host-routes + ! + ttl-security disable + ! + interface r3-eth1 + ! + interface r3-eth2 + ! + ! +! +line vty +! diff --git a/tests/topotests/ldp-sync-isis-topo1/r3/show_ip_route.ref b/tests/topotests/ldp-sync-isis-topo1/r3/show_ip_route.ref new file mode 100644 index 0000000000..da46f1dfe2 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r3/show_ip_route.ref @@ -0,0 +1,143 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "distance":115, + "metric":10, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.1", + "afi":"ipv4", + "interfaceIndex":3, + "interfaceName":"r3-eth1", + "active":true + } + ] + } + ], + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "distance":115, + "metric":10, + "nexthops":[ + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceIndex":4, + "interfaceName":"r3-eth2", + "active":true + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceIndex":1, + "interfaceName":"lo", + "active":true + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "selected":true, + "distance":115, + "metric":10, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.1", + "afi":"ipv4", + "interfaceIndex":3, + "interfaceName":"r3-eth1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceIndex":4, + "interfaceName":"r3-eth2", + "active":true + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "distance":115, + "metric":10, + "nexthops":[ + { + "ip":"10.0.2.1", + "afi":"ipv4", + "interfaceIndex":3, + "interfaceName":"r3-eth1" + } + ] + }, + { + "prefix":"10.0.2.0\/24", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceIndex":3, + "interfaceName":"r3-eth1", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "distance":115, + "metric":10, + "nexthops":[ + { + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceIndex":4, + "interfaceName":"r3-eth2" + } + ] + }, + { + "prefix":"10.0.3.0\/24", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceIndex":4, + "interfaceName":"r3-eth2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r3/show_isis_interface_detail.ref b/tests/topotests/ldp-sync-isis-topo1/r3/show_isis_interface_detail.ref new file mode 100644 index 0000000000..e323f61f25 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r3/show_isis_interface_detail.ref @@ -0,0 +1,16 @@ +{ + "1": { + "r3-eth1": [ + { + "level": "Level-1", + "metric": "10" + } + ], + "r3-eth2": [ + { + "level": "Level-1", + "metric": "10" + } + ] + } +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r3/show_isis_interface_detail_r1_eth1_shutdown.ref b/tests/topotests/ldp-sync-isis-topo1/r3/show_isis_interface_detail_r1_eth1_shutdown.ref new file mode 100644 index 0000000000..e323f61f25 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r3/show_isis_interface_detail_r1_eth1_shutdown.ref @@ -0,0 +1,16 @@ +{ + "1": { + "r3-eth1": [ + { + "level": "Level-1", + "metric": "10" + } + ], + "r3-eth2": [ + { + "level": "Level-1", + "metric": "10" + } + ] + } +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r3/show_isis_interface_detail_r2_eth1_shutdown.ref b/tests/topotests/ldp-sync-isis-topo1/r3/show_isis_interface_detail_r2_eth1_shutdown.ref new file mode 100644 index 0000000000..e323f61f25 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r3/show_isis_interface_detail_r2_eth1_shutdown.ref @@ -0,0 +1,16 @@ +{ + "1": { + "r3-eth1": [ + { + "level": "Level-1", + "metric": "10" + } + ], + "r3-eth2": [ + { + "level": "Level-1", + "metric": "10" + } + ] + } +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r3/show_isis_ldp_sync.ref b/tests/topotests/ldp-sync-isis-topo1/r3/show_isis_ldp_sync.ref new file mode 100644 index 0000000000..9cb70a4758 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r3/show_isis_ldp_sync.ref @@ -0,0 +1,13 @@ +{ + "r3-eth1":{ + "ldpIgpSyncEnabled":false, + "holdDownTimeInSec":50, + "ldpIgpSyncState":"Sync not required" + }, + "r3-eth2":{ + "ldpIgpSyncEnabled":true, + "holdDownTimeInSec":50, + "ldpIgpSyncState":"Sync achieved" + } + +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r3/show_isis_ldp_sync_r1_eth1_shutdown.ref b/tests/topotests/ldp-sync-isis-topo1/r3/show_isis_ldp_sync_r1_eth1_shutdown.ref new file mode 100644 index 0000000000..9cb70a4758 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r3/show_isis_ldp_sync_r1_eth1_shutdown.ref @@ -0,0 +1,13 @@ +{ + "r3-eth1":{ + "ldpIgpSyncEnabled":false, + "holdDownTimeInSec":50, + "ldpIgpSyncState":"Sync not required" + }, + "r3-eth2":{ + "ldpIgpSyncEnabled":true, + "holdDownTimeInSec":50, + "ldpIgpSyncState":"Sync achieved" + } + +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r3/show_isis_ldp_sync_r2_eth1_shutdown.ref b/tests/topotests/ldp-sync-isis-topo1/r3/show_isis_ldp_sync_r2_eth1_shutdown.ref new file mode 100644 index 0000000000..9cb70a4758 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r3/show_isis_ldp_sync_r2_eth1_shutdown.ref @@ -0,0 +1,13 @@ +{ + "r3-eth1":{ + "ldpIgpSyncEnabled":false, + "holdDownTimeInSec":50, + "ldpIgpSyncState":"Sync not required" + }, + "r3-eth2":{ + "ldpIgpSyncEnabled":true, + "holdDownTimeInSec":50, + "ldpIgpSyncState":"Sync achieved" + } + +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r3/show_l2vpn_binding.ref b/tests/topotests/ldp-sync-isis-topo1/r3/show_l2vpn_binding.ref new file mode 100644 index 0000000000..2c63c08510 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r3/show_l2vpn_binding.ref @@ -0,0 +1,2 @@ +{ +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r3/show_l2vpn_vc.ref b/tests/topotests/ldp-sync-isis-topo1/r3/show_l2vpn_vc.ref new file mode 100644 index 0000000000..2c63c08510 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r3/show_l2vpn_vc.ref @@ -0,0 +1,2 @@ +{ +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r3/show_ldp_binding.ref b/tests/topotests/ldp-sync-isis-topo1/r3/show_ldp_binding.ref new file mode 100644 index 0000000000..e54bd6e755 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r3/show_ldp_binding.ref @@ -0,0 +1,44 @@ +{ + "bindings":[ + { + "addressFamily":"ipv4", + "prefix":"1.1.1.1/32", + "neighborId":"1.1.1.1", + "remoteLabel":"imp-null", + "inUse":1 + }, + { + "addressFamily":"ipv4", + "prefix":"1.1.1.1/32", + "neighborId":"2.2.2.2", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"2.2.2.2/32", + "neighborId":"1.1.1.1", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"2.2.2.2/32", + "neighborId":"2.2.2.2", + "remoteLabel":"imp-null", + "inUse":1 + }, + { + "addressFamily":"ipv4", + "prefix":"3.3.3.3/32", + "neighborId":"1.1.1.1", + "localLabel":"imp-null", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"3.3.3.3/32", + "neighborId":"2.2.2.2", + "localLabel":"imp-null", + "inUse":0 + } + ] +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r3/show_ldp_discovery.ref b/tests/topotests/ldp-sync-isis-topo1/r3/show_ldp_discovery.ref new file mode 100644 index 0000000000..42fa98d4da --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r3/show_ldp_discovery.ref @@ -0,0 +1,18 @@ +{ + "adjacencies":[ + { + "addressFamily":"ipv4", + "neighborId":"1.1.1.1", + "type":"link", + "interface":"r3-eth1", + "helloHoldtime":15 + }, + { + "addressFamily":"ipv4", + "neighborId":"2.2.2.2", + "type":"link", + "interface":"r3-eth2", + "helloHoldtime":15 + } + ] +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r3/show_ldp_igp_sync.ref b/tests/topotests/ldp-sync-isis-topo1/r3/show_ldp_igp_sync.ref new file mode 100644 index 0000000000..73261830c9 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r3/show_ldp_igp_sync.ref @@ -0,0 +1,16 @@ +{ + "r3-eth1":{ + "state":"labelExchangeComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"1.1.1.1" + }, + "r3-eth2":{ + "state":"labelExchangeComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"2.2.2.2" + } +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r3/show_ldp_igp_sync_r1_eth1_shutdown.ref b/tests/topotests/ldp-sync-isis-topo1/r3/show_ldp_igp_sync_r1_eth1_shutdown.ref new file mode 100644 index 0000000000..73261830c9 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r3/show_ldp_igp_sync_r1_eth1_shutdown.ref @@ -0,0 +1,16 @@ +{ + "r3-eth1":{ + "state":"labelExchangeComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"1.1.1.1" + }, + "r3-eth2":{ + "state":"labelExchangeComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"2.2.2.2" + } +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r3/show_ldp_neighbor.ref b/tests/topotests/ldp-sync-isis-topo1/r3/show_ldp_neighbor.ref new file mode 100644 index 0000000000..5c482da697 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r3/show_ldp_neighbor.ref @@ -0,0 +1,16 @@ +{ + "neighbors":[ + { + "addressFamily":"ipv4", + "neighborId":"1.1.1.1", + "state":"OPERATIONAL", + "transportAddress":"1.1.1.1" + }, + { + "addressFamily":"ipv4", + "neighborId":"2.2.2.2", + "state":"OPERATIONAL", + "transportAddress":"2.2.2.2" + } + ] +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r3/show_yang_interface_isis_adjacencies.ref b/tests/topotests/ldp-sync-isis-topo1/r3/show_yang_interface_isis_adjacencies.ref new file mode 100644 index 0000000000..0922192361 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r3/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1,42 @@ +{ + "frr-interface:lib": { + "interface": [ + { + "name": "r3-eth1", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0001", + "neighbor-extended-circuit-id": 0, + "state": "up" + } + ] + } + } + } + }, + { + "name": "r3-eth2", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0002", + "neighbor-extended-circuit-id": 0, + "state": "up" + } + ] + } + } + } + } + ] + } +} diff --git a/tests/topotests/ldp-sync-isis-topo1/r3/zebra.conf b/tests/topotests/ldp-sync-isis-topo1/r3/zebra.conf new file mode 100644 index 0000000000..b1919bd296 --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/r3/zebra.conf @@ -0,0 +1,32 @@ +log file zebra.log +! +hostname r3 +! +debug zebra rib detailed +debug zebra dplane detailed +debug zebra kernel +debug zebra nht +debug zebra pseudowires +! +interface lo + ip address 3.3.3.3/32 +! +interface r3-eth0 + description to s3 +! +interface r3-eth1 + description to s5 + ip address 10.0.2.3/24 +! +interface r3-eth2 + description to s6 + ip address 10.0.3.3/24 +! +!!interface r3-eth3 +!! description to s4 +!! ip address 10.0.1.3/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ldp-sync-isis-topo1/test_ldp_sync_isis_topo1.dot b/tests/topotests/ldp-sync-isis-topo1/test_ldp_sync_isis_topo1.dot new file mode 100644 index 0000000000..4f1bd22f7c --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/test_ldp_sync_isis_topo1.dot @@ -0,0 +1,111 @@ +## Color coding: +######################### +## Main FRR: #f08080 red +## Switches: #d0e0d0 gray +## RIP: #19e3d9 Cyan +## RIPng: #fcb314 dark yellow +## OSPFv2: #32b835 Green +## OSPFv3: #19e3d9 Cyan +## ISIS IPv4 #fcb314 dark yellow +## ISIS IPv6 #9a81ec purple +## BGP IPv4 #eee3d3 beige +## BGP IPv6 #fdff00 yellow +##### Colors (see http://www.color-hex.com/) + +graph template { + label="Test Topology - LDP-VPLS 1"; + + # Routers + ce1 [ + shape=doubleoctagon, + label="ce1", + fillcolor="#f08080", + style=filled, + ]; + ce2 [ + shape=doubleoctagon + label="ce2", + fillcolor="#f08080", + style=filled, + ]; + ce3 [ + shape=doubleoctagon + label="ce3", + fillcolor="#f08080", + style=filled, + ]; + r1 [ + shape=doubleoctagon, + label="r1", + fillcolor="#f08080", + style=filled, + ]; + r2 [ + shape=doubleoctagon + label="r2", + fillcolor="#f08080", + style=filled, + ]; + r3 [ + shape=doubleoctagon + label="r3", + fillcolor="#f08080", + style=filled, + ]; + + + # Switches + s1 [ + shape=oval, + label="VPLS\n172.16.1.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + s2 [ + shape=oval, + label="VPLS\n172.16.1.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + s3 [ + shape=oval, + label="VPLS\n172.16.1.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + s4 [ + shape=oval, + label="s4\n10.0.1.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + s5 [ + shape=oval, + label="s5\n10.0.2.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + s6 [ + shape=oval, + label="s6\n10.0.3.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + + # Connections + ce1 -- s1 [label="eth0\n.1"]; + ce2 -- s2 [label="eth0\n.2"]; + ce3 -- s3 [label="eth0\n.3"]; + + r1 -- s1 [label="eth0"]; + r1 -- s4 [label="eth1\n.1"]; + r1 -- s5 [label="eth2\n.1"]; + + r2 -- s2 [label="eth0"]; + r2 -- s4 [label="eth1\n.2"]; + r2 -- s6 [label="eth2\n.2"]; + + r3 -- s3 [label="eth0"]; + r3 -- s5 [label="eth1\n.3"]; + r3 -- s6 [label="eth2\n.3"]; +} diff --git a/tests/topotests/ldp-sync-isis-topo1/test_ldp_sync_isis_topo1.py b/tests/topotests/ldp-sync-isis-topo1/test_ldp_sync_isis_topo1.py new file mode 100644 index 0000000000..01f895891c --- /dev/null +++ b/tests/topotests/ldp-sync-isis-topo1/test_ldp_sync_isis_topo1.py @@ -0,0 +1,625 @@ +#!/usr/bin/env python + +# +# test_ldp_isis_topo1.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 by Volta Networks +# +# 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_ldp_vpls_topo1.py: + + +---------+ +---------+ + | | | | + | CE1 | | CE2 | + | | | | + +---------+ +---------+ +ce1-eth0 (172.16.1.1/24)| |ce2-eth0 (172.16.1.2/24) + | | + | | + rt1-eth0| |rt2-eth0 + +---------+ 10.0.1.0/24 +---------+ + | |rt1-eth1 | | + | RT1 +----------------+ RT2 | + | 1.1.1.1 | rt2-eth1| 2.2.2.2 | + | | | | + +---------+ +---------+ + rt1-eth2| |rt2-eth2 + | | + | | + 10.0.2.0/24| +---------+ |10.0.3.0/24 + | | | | + | | RT3 | | + +--------+ 3.3.3.3 +-------+ + rt3-eth2| |rt3-eth1 + +---------+ + |rt3-eth0 + | + | + ce3-eth0 (172.16.1.3/24)| + +---------+ + | | + | CE3 | + | | + +---------+ +""" + +import os +import re +import sys +import pytest +import json +from time import sleep +from functools import partial + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + + +class TemplateTopo(Topo): + "Test topology builder" + + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # + # Define FRR Routers + # + for router in ["ce1", "ce2", "ce3", "r1", "r2", "r3"]: + tgen.add_router(router) + + # + # Define connections + # + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["ce1"]) + switch.add_link(tgen.gears["r1"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["ce2"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["ce3"]) + switch.add_link(tgen.gears["r3"]) + + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s5") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r3"]) + + switch = tgen.add_switch("s6") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(TemplateTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + # For all registered routers, load the zebra configuration file + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + # Don't start isisd and ldpd in the CE nodes + if router.name[0] == "r": + router.load_config( + TopoRouter.RD_ISIS, os.path.join(CWD, "{}/isisd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_LDP, os.path.join(CWD, "{}/ldpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +def router_compare_json_output(rname, command, reference): + "Compare router JSON output" + + logger.info('Comparing router "%s" "%s" output', rname, command) + + tgen = get_topogen() + filename = "{}/{}/{}".format(CWD, rname, reference) + expected = json.loads(open(filename).read()) + + # Run test function until we get an result. + test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected) + _, diff = topotest.run_and_expect(test_func, None, count=320, wait=0.5) + assertmsg = '"{}" JSON output mismatches the expected result'.format(rname) + assert diff is None, assertmsg + + +def test_isis_convergence(): + logger.info("Test: check ISIS adjacencies") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, + "show yang operational-data /frr-interface:lib isisd", + "show_yang_interface_isis_adjacencies.ref") + +def test_rib(): + logger.info("Test: verify RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output(rname, "show ip route json", "show_ip_route.ref") + + +def test_ldp_adjacencies(): + logger.info("Test: verify LDP adjacencies") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show mpls ldp discovery json", "show_ldp_discovery.ref" + ) + + +def test_ldp_neighbors(): + logger.info("Test: verify LDP neighbors") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show mpls ldp neighbor json", "show_ldp_neighbor.ref" + ) + + +def test_ldp_bindings(): + logger.info("Test: verify LDP bindings") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show mpls ldp binding json", "show_ldp_binding.ref" + ) + + +def test_ldp_pwid_bindings(): + logger.info("Test: verify LDP PW-ID bindings") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show l2vpn atom binding json", "show_l2vpn_binding.ref" + ) + + +def test_ldp_pseudowires(): + logger.info("Test: verify LDP pseudowires") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show l2vpn atom vc json", "show_l2vpn_vc.ref" + ) + +def test_ldp_igp_sync(): + logger.info("Test: verify LDP igp-sync") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show mpls ldp igp-sync json", "show_ldp_igp_sync.ref" + ) + +def test_isis_ldp_sync(): + logger.info("Test: verify ISIS igp-sync") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r2", "r3"]: + (result, diff) = validate_show_isis_ldp_sync( + rname, "show_isis_ldp_sync.ref" + ) + assert result, "ISIS did not converge on {}:\n{}".format(rname, diff) + + for rname in ["r1", "r2", "r3"]: + (result, diff) = validate_show_isis_interface_detail( + rname, "show_isis_interface_detail.ref" + ) + assert result, "ISIS interface did not converge on {}:\n{}".format(rname, diff) + + +def test_r1_eth1_shutdown(): + logger.info("Test: verify behaviour after r1-eth1 is shutdown") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Shut down r1-r2 link */ + tgen = get_topogen() + tgen.gears["r1"].peer_link_enable("r1-eth1", False) + topotest.sleep(5, "Waiting for the network to reconverge") + + # check if the pseudowire is still up (using an alternate path for nexthop resolution) + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show l2vpn atom vc json", "show_l2vpn_vc.ref" + ) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show mpls ldp igp-sync json", "show_ldp_igp_sync_r1_eth1_shutdown.ref" + ) + + for rname in ["r1", "r2", "r3"]: + (result, diff) = validate_show_isis_ldp_sync( + rname, "show_isis_ldp_sync_r1_eth1_shutdown.ref" + ) + assert result, "ISIS did not converge on {}:\n{}".format(rname, diff) + + for rname in ["r1", "r2", "r3"]: + (result, diff) = validate_show_isis_interface_detail( + rname, "show_isis_interface_detail_r1_eth1_shutdown.ref" + ) + assert result, "ISIS interface did not converge on {}:\n{}".format(rname, diff) + + +def test_r1_eth1_no_shutdown(): + logger.info("Test: verify behaviour after r1-eth1 is no shutdown") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Run no shutdown on r1-eth1 interface */ + tgen = get_topogen() + tgen.gears["r1"].peer_link_enable("r1-eth1", True) + topotest.sleep(5, "Waiting for the network to reconverge") + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show mpls ldp igp-sync json", "show_ldp_igp_sync.ref" + ) + + for rname in ["r1", "r2", "r3"]: + (result, diff) = validate_show_isis_ldp_sync( + rname, "show_isis_ldp_sync.ref" + ) + assert result, "ISIS did not converge on {}:\n{}".format(rname, diff) + + for rname in ["r1", "r2", "r3"]: + (result, diff) = validate_show_isis_interface_detail( + rname, "show_isis_interface_detail.ref" + ) + assert result, "ISIS interface did not converge on {}:\n{}".format(rname, diff) + + +def test_r2_eth1_shutdown(): + logger.info("Test: verify behaviour after r2-eth1 is shutdown") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Shut down r1-r2 link */ + tgen = get_topogen() + tgen.gears["r2"].peer_link_enable("r2-eth1", False) + topotest.sleep(5, "Waiting for the network to reconverge") + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show mpls ldp igp-sync json", "show_ldp_igp_sync_r1_eth1_shutdown.ref" + ) + + for rname in ["r1", "r2", "r3"]: + (result, diff) = validate_show_isis_ldp_sync( + rname, "show_isis_ldp_sync_r2_eth1_shutdown.ref" + ) + assert result, "ISIS did not converge on {}:\n{}".format(rname, diff) + + for rname in ["r1", "r2", "r3"]: + (result, diff) = validate_show_isis_interface_detail( + rname, "show_isis_interface_detail_r2_eth1_shutdown.ref" + ) + assert result, "ISIS interface did not converge on {}:\n{}".format(rname, diff) + + +def test_r2_eth1_no_shutdown(): + logger.info("Test: verify behaviour after r2-eth1 is no shutdown") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Run no shutdown on r2-eth1 interface */ + tgen = get_topogen() + tgen.gears["r2"].peer_link_enable("r2-eth1", True) + topotest.sleep(5, "Waiting for the network to reconverge") + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show mpls ldp igp-sync json", "show_ldp_igp_sync.ref" + ) + + for rname in ["r1", "r2", "r3"]: + (result, diff) = validate_show_isis_ldp_sync( + rname, "show_isis_ldp_sync.ref" + ) + assert result, "ISIS did not converge on {}:\n{}".format(rname, diff) + + for rname in ["r1", "r2", "r3"]: + (result, diff) = validate_show_isis_interface_detail( + rname, "show_isis_interface_detail.ref" + ) + assert result, "ISIS interface did not converge on {}:\n{}".format(rname, diff) + + +# Memory leak test template +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) + + +# +# Auxiliary functions +# + +def parse_show_isis_ldp_sync(lines, rname): + """ + Parse the output of 'show isis mpls ldp sync' into a Python dict. + """ + interfaces = {} + + it = iter(lines) + + while True: + try: + interface = {} + interface_name = None + + line = it.next(); + + if line.startswith(rname + "-eth"): + interface_name = line + + line = it.next(); + + if line.startswith(" LDP-IGP Synchronization enabled: "): + interface["ldpIgpSyncEnabled"] = line.endswith("yes") + + line = it.next(); + + if line.startswith(" holddown timer in seconds: "): + interface["holdDownTimeInSec"] = int(line.split(": ")[-1]) + + line = it.next(); + + if line.startswith(" State: "): + interface["ldpIgpSyncState"] = line.split(": ")[-1] + + interfaces[interface_name] = interface + + except StopIteration: + break + + return interfaces + + +def show_isis_ldp_sync(router, rname): + """ + Get the show isis mpls ldp-sync info in a dictionary format. + + """ + out = topotest.normalize_text( + router.vtysh_cmd("show isis mpls ldp-sync") + ).splitlines() + + parsed = parse_show_isis_ldp_sync(out, rname) + + return parsed + + +def validate_show_isis_ldp_sync(rname, fname): + tgen = get_topogen() + + filename = "{0}/{1}/{2}".format(CWD, rname, fname) + expected = json.loads(open(filename).read()) + + router = tgen.gears[rname] + + def compare_isis_ldp_sync(router, expected): + "Helper function to test show isis mpls ldp-sync" + actual = show_isis_ldp_sync(router, rname) + return topotest.json_cmp(actual, expected) + + test_func = partial(compare_isis_ldp_sync, router, expected) + (result, diff) = topotest.run_and_expect(test_func, None, wait=0.5, count=160) + + return (result, diff) + + +def parse_show_isis_interface_detail(lines, rname): + """ + Parse the output of 'show isis interface detail' into a Python dict. + """ + areas = {} + area_id = None + + it = iter(lines) + + while True: + try: + line = it.next(); + + area_match = re.match(r"Area (.+):", line) + if not area_match: + continue + + area_id = area_match.group(1) + area = {} + + line = it.next(); + + while line.startswith(" Interface: "): + interface_name = re.split(':|,', line)[1].lstrip() + + area[interface_name]= [] + + # Look for keyword: Level-1 or Level-2 + while not line.startswith(" Level-"): + line = it.next(); + + while line.startswith(" Level-"): + + level = {} + + level_name = line.split()[0] + level['level'] = level_name + + line = it.next(); + + if line.startswith(" Metric:"): + level['metric'] = re.split(':|,', line)[1].lstrip() + + area[interface_name].append(level) + + # Look for keyword: Level-1 or Level-2 or Interface: + while not line.startswith(" Level-") and not line.startswith(" Interface: "): + line = it.next(); + + if line.startswith(" Level-"): + continue + + if line.startswith(" Interface: "): + break + + areas[area_id] = area + + except StopIteration: + + areas[area_id] = area + break + + return areas + + +def show_isis_interface_detail(router, rname): + """ + Get the show isis mpls ldp-sync info in a dictionary format. + + """ + out = topotest.normalize_text( + router.vtysh_cmd("show isis interface detail") + ).splitlines() + + logger.warning(out) + + parsed = parse_show_isis_interface_detail(out, rname) + + logger.warning(parsed) + + return parsed + + +def validate_show_isis_interface_detail(rname, fname): + tgen = get_topogen() + + filename = "{0}/{1}/{2}".format(CWD, rname, fname) + expected = json.loads(open(filename).read()) + + router = tgen.gears[rname] + + def compare_isis_interface_detail(router, expected): + "Helper function to test show isis interface detail" + actual = show_isis_interface_detail(router, rname) + return topotest.json_cmp(actual, expected) + + test_func = partial(compare_isis_interface_detail, router, expected) + (result, diff) = topotest.run_and_expect(test_func, None, wait=0.5, count=160) + + return (result, diff) + diff --git a/tests/topotests/ldp-sync-ospf-topo1/ce1/zebra.conf b/tests/topotests/ldp-sync-ospf-topo1/ce1/zebra.conf new file mode 100644 index 0000000000..6f165e2724 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/ce1/zebra.conf @@ -0,0 +1,12 @@ +log file zebra.log +! +hostname ce1 +! +interface ce1-eth0 + ip address 172.16.1.1/24 + no link-detect +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ldp-sync-ospf-topo1/ce2/zebra.conf b/tests/topotests/ldp-sync-ospf-topo1/ce2/zebra.conf new file mode 100644 index 0000000000..ac02d0f9a4 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/ce2/zebra.conf @@ -0,0 +1,12 @@ +log file zebra.log +! +hostname ce2 +! +interface ce2-eth0 + ip address 172.16.1.2/24 + no link-detect +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ldp-sync-ospf-topo1/ce3/zebra.conf b/tests/topotests/ldp-sync-ospf-topo1/ce3/zebra.conf new file mode 100644 index 0000000000..c6a5824d15 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/ce3/zebra.conf @@ -0,0 +1,12 @@ +log file zebra.log +! +hostname ce3 +! +interface ce3-eth0 + ip address 172.16.1.3/24 + no link-detect +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ldp-sync-ospf-topo1/r1/ldpd.conf b/tests/topotests/ldp-sync-ospf-topo1/r1/ldpd.conf new file mode 100644 index 0000000000..b9c32d3000 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r1/ldpd.conf @@ -0,0 +1,33 @@ +hostname r1 +log file ldpd.log +! +debug mpls ldp zebra +debug mpls ldp event +debug mpls ldp errors +debug mpls ldp sync +! +mpls ldp + router-id 1.1.1.1 + ! + address-family ipv4 + discovery transport-address 1.1.1.1 + label local allocate host-routes + ! + ttl-security disable + ! + interface r1-eth1 + ! + interface r1-eth2 + ! + ! +! +l2vpn CUST_A type vpls + member interface r1-eth0 + ! + member pseudowire r1-mpw0 + neighbor lsr-id 2.2.2.2 + pw-id 100 + ! +! +line vty +! diff --git a/tests/topotests/ldp-sync-ospf-topo1/r1/ospf-nbrs.txt b/tests/topotests/ldp-sync-ospf-topo1/r1/ospf-nbrs.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r1/ospf-nbrs.txt diff --git a/tests/topotests/ldp-sync-ospf-topo1/r1/ospfd.conf b/tests/topotests/ldp-sync-ospf-topo1/r1/ospfd.conf new file mode 100644 index 0000000000..eefcd1e71c --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r1/ospfd.conf @@ -0,0 +1,20 @@ +hostname r1 +log file ospfd.log +debug ospf zebra interface +debug ospf ldp-sync +! +router ospf + router-id 1.1.1.1 + network 10.0.1.1/24 area 0 + network 10.0.2.1/24 area 0 + network 1.1.1.1/32 area 0 + mpls ldp-sync + ! mpls ldp-sync holddown 50 +! +interface r1-eth1 + ip ospf network point-to-point + ! ip ospf mpls ldp-sync holddown 40 +! +interface r1-eth2 + ip ospf network point-to-point +! diff --git a/tests/topotests/ldp-sync-ospf-topo1/r1/show_ip_ospf_interface.ref b/tests/topotests/ldp-sync-ospf-topo1/r1/show_ip_ospf_interface.ref new file mode 100644 index 0000000000..8b2884786d --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r1/show_ip_ospf_interface.ref @@ -0,0 +1,11 @@ +{ + "interfaces":{ + "r1-eth1":{ + "cost":10 + }, + "r1-eth2":{ + "cost":10 + } + } +} + diff --git a/tests/topotests/ldp-sync-ospf-topo1/r1/show_ip_ospf_interface_r1_eth1_shutdown.ref b/tests/topotests/ldp-sync-ospf-topo1/r1/show_ip_ospf_interface_r1_eth1_shutdown.ref new file mode 100644 index 0000000000..b1a263e422 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r1/show_ip_ospf_interface_r1_eth1_shutdown.ref @@ -0,0 +1,8 @@ +{ + "interfaces":{ + "r1-eth2":{ + "cost":10 + } + } +} + diff --git a/tests/topotests/ldp-sync-ospf-topo1/r1/show_ip_ospf_interface_r2_eth1_shutdown.ref b/tests/topotests/ldp-sync-ospf-topo1/r1/show_ip_ospf_interface_r2_eth1_shutdown.ref new file mode 100644 index 0000000000..0c147338e3 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r1/show_ip_ospf_interface_r2_eth1_shutdown.ref @@ -0,0 +1,11 @@ +{ + "interfaces":{ + "r1-eth1":{ + "cost":65535 + }, + "r1-eth2":{ + "cost":10 + } + } +} + diff --git a/tests/topotests/ldp-sync-ospf-topo1/r1/show_ip_ospf_neighbor.json b/tests/topotests/ldp-sync-ospf-topo1/r1/show_ip_ospf_neighbor.json new file mode 100644 index 0000000000..3bfda39071 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r1/show_ip_ospf_neighbor.json @@ -0,0 +1,26 @@ +{ + "neighbors": { + "2.2.2.2": [ + { + "dbSummaryCounter": 0, + "retransmitCounter": 0, + "priority": 1, + "state": "Full/DROther", + "address": "10.0.1.2", + "ifaceName": "r1-eth1:10.0.1.1", + "requestCounter": 0 + } + ], + "3.3.3.3": [ + { + "dbSummaryCounter": 0, + "retransmitCounter": 0, + "priority": 1, + "state": "Full/DROther", + "address": "10.0.2.3", + "ifaceName": "r1-eth2:10.0.2.1", + "requestCounter": 0 + } + ] + } +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r1/show_ip_route.ref b/tests/topotests/ldp-sync-ospf-topo1/r1/show_ip_route.ref new file mode 100644 index 0000000000..8ccd60ca41 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r1/show_ip_route.ref @@ -0,0 +1,157 @@ +{ + "1.1.1.1/32":[ + { + "prefix":"1.1.1.1/32", + "protocol":"ospf", + "distance":110, + "metric":0, + "nexthops":[ + { + "directlyConnected":true, + "interfaceIndex":1, + "interfaceName":"lo", + "active":true + } + ] + }, + { + "prefix":"1.1.1.1/32", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceIndex":1, + "interfaceName":"lo", + "active":true + } + ] + } + ], + "2.2.2.2/32":[ + { + "prefix":"2.2.2.2/32", + "protocol":"ospf", + "selected":true, + "distance":110, + "metric":10, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceIndex":3, + "interfaceName":"r1-eth1", + "active":true + } + ] + } + ], + "3.3.3.3/32":[ + { + "prefix":"3.3.3.3/32", + "protocol":"ospf", + "selected":true, + "distance":110, + "metric":10, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.3", + "afi":"ipv4", + "interfaceIndex":4, + "interfaceName":"r1-eth2", + "active":true + } + ] + } + ], + "10.0.1.0/24":[ + { + "prefix":"10.0.1.0/24", + "protocol":"ospf", + "distance":110, + "metric":10, + "nexthops":[ + { + "directlyConnected":true, + "interfaceIndex":3, + "interfaceName":"r1-eth1", + "active":true + } + ] + }, + { + "prefix":"10.0.1.0/24", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceIndex":3, + "interfaceName":"r1-eth1", + "active":true + } + ] + } + ], + "10.0.2.0/24":[ + { + "prefix":"10.0.2.0/24", + "protocol":"ospf", + "distance":110, + "metric":10, + "nexthops":[ + { + "directlyConnected":true, + "interfaceIndex":4, + "interfaceName":"r1-eth2", + "active":true + } + ] + }, + { + "prefix":"10.0.2.0/24", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceIndex":4, + "interfaceName":"r1-eth2", + "active":true + } + ] + } + ], + "10.0.3.0/24":[ + { + "prefix":"10.0.3.0/24", + "protocol":"ospf", + "selected":true, + "distance":110, + "metric":20, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceIndex":3, + "interfaceName":"r1-eth1", + "active":true + }, + { + "fib":true, + "ip":"10.0.2.3", + "afi":"ipv4", + "interfaceIndex":4, + "interfaceName":"r1-eth2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r1/show_l2vpn_binding.ref b/tests/topotests/ldp-sync-ospf-topo1/r1/show_l2vpn_binding.ref new file mode 100644 index 0000000000..b3de7e2c66 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r1/show_l2vpn_binding.ref @@ -0,0 +1,16 @@ +{ + "2.2.2.2: 100":{ + "destination":"2.2.2.2", + "vcId":100, + "localLabel":16, + "localControlWord":1, + "localVcType":"Ethernet", + "localGroupID":0, + "localIfMtu":1500, + "remoteLabel":16, + "remoteControlWord":1, + "remoteVcType":"Ethernet", + "remoteGroupID":0, + "remoteIfMtu":1500 + } +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r1/show_l2vpn_vc.ref b/tests/topotests/ldp-sync-ospf-topo1/r1/show_l2vpn_vc.ref new file mode 100644 index 0000000000..29e9df1089 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r1/show_l2vpn_vc.ref @@ -0,0 +1,8 @@ +{ + "r1-mpw0":{ + "peerId":"2.2.2.2", + "vcId":100, + "VpnName":"CUST_A", + "status":"up" + } +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r1/show_ldp_binding.ref b/tests/topotests/ldp-sync-ospf-topo1/r1/show_ldp_binding.ref new file mode 100644 index 0000000000..b3a12ec53f --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r1/show_ldp_binding.ref @@ -0,0 +1,44 @@ +{ + "bindings":[ + { + "addressFamily":"ipv4", + "prefix":"1.1.1.1/32", + "neighborId":"2.2.2.2", + "localLabel":"imp-null", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"1.1.1.1/32", + "neighborId":"3.3.3.3", + "localLabel":"imp-null", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"2.2.2.2/32", + "neighborId":"2.2.2.2", + "remoteLabel":"imp-null", + "inUse":1 + }, + { + "addressFamily":"ipv4", + "prefix":"2.2.2.2/32", + "neighborId":"3.3.3.3", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"3.3.3.3/32", + "neighborId":"2.2.2.2", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"3.3.3.3/32", + "neighborId":"3.3.3.3", + "remoteLabel":"imp-null", + "inUse":1 + } + ] +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r1/show_ldp_discovery.ref b/tests/topotests/ldp-sync-ospf-topo1/r1/show_ldp_discovery.ref new file mode 100644 index 0000000000..9301e60c67 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r1/show_ldp_discovery.ref @@ -0,0 +1,25 @@ +{ + "adjacencies":[ + { + "addressFamily":"ipv4", + "neighborId":"2.2.2.2", + "type":"link", + "interface":"r1-eth1", + "helloHoldtime":15 + }, + { + "addressFamily":"ipv4", + "neighborId":"2.2.2.2", + "type":"targeted", + "peer":"2.2.2.2", + "helloHoldtime":45 + }, + { + "addressFamily":"ipv4", + "neighborId":"3.3.3.3", + "type":"link", + "interface":"r1-eth2", + "helloHoldtime":15 + } + ] +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r1/show_ldp_igp_sync.ref b/tests/topotests/ldp-sync-ospf-topo1/r1/show_ldp_igp_sync.ref new file mode 100644 index 0000000000..54d015fef9 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r1/show_ldp_igp_sync.ref @@ -0,0 +1,16 @@ +{ + "r1-eth1":{ + "state":"labelExchangeComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"2.2.2.2" + }, + "r1-eth2":{ + "state":"labelExchangeComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"3.3.3.3" + } +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r1/show_ldp_igp_sync_r1_eth1_shutdown.ref b/tests/topotests/ldp-sync-ospf-topo1/r1/show_ldp_igp_sync_r1_eth1_shutdown.ref new file mode 100644 index 0000000000..2232069f68 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r1/show_ldp_igp_sync_r1_eth1_shutdown.ref @@ -0,0 +1,16 @@ +{ + "r1-eth1":{ + "state":"labelExchangeNotComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"" + }, + "r1-eth2":{ + "state":"labelExchangeComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"3.3.3.3" + } +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r1/show_ldp_neighbor.ref b/tests/topotests/ldp-sync-ospf-topo1/r1/show_ldp_neighbor.ref new file mode 100644 index 0000000000..40d8ebeb90 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r1/show_ldp_neighbor.ref @@ -0,0 +1,16 @@ +{ + "neighbors":[ + { + "addressFamily":"ipv4", + "neighborId":"2.2.2.2", + "state":"OPERATIONAL", + "transportAddress":"2.2.2.2" + }, + { + "addressFamily":"ipv4", + "neighborId":"3.3.3.3", + "state":"OPERATIONAL", + "transportAddress":"3.3.3.3" + } + ] +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r1/show_ospf_ldp_sync.ref b/tests/topotests/ldp-sync-ospf-topo1/r1/show_ospf_ldp_sync.ref new file mode 100644 index 0000000000..3782071bf9 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r1/show_ospf_ldp_sync.ref @@ -0,0 +1,12 @@ +{ + "r1-eth1":{ + "ldpIgpSyncEnabled":true, + "holdDownTimeInSec":0, + "ldpIgpSyncState":"Sync achieved" + }, + "r1-eth2":{ + "ldpIgpSyncEnabled":true, + "holdDownTimeInSec":0, + "ldpIgpSyncState":"Sync achieved" + } +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r1/show_ospf_ldp_sync_r1_eth1_shutdown.ref b/tests/topotests/ldp-sync-ospf-topo1/r1/show_ospf_ldp_sync_r1_eth1_shutdown.ref new file mode 100644 index 0000000000..c2642c6483 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r1/show_ospf_ldp_sync_r1_eth1_shutdown.ref @@ -0,0 +1,7 @@ +{ + "r1-eth2":{ + "ldpIgpSyncEnabled":true, + "holdDownTimeInSec":0, + "ldpIgpSyncState":"Sync achieved" + } +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r1/show_ospf_ldp_sync_r2_eth1_shutdown.ref b/tests/topotests/ldp-sync-ospf-topo1/r1/show_ospf_ldp_sync_r2_eth1_shutdown.ref new file mode 100644 index 0000000000..6f180b048a --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r1/show_ospf_ldp_sync_r2_eth1_shutdown.ref @@ -0,0 +1,12 @@ +{ + "r1-eth1":{ + "ldpIgpSyncEnabled":true, + "holdDownTimeInSec":0, + "ldpIgpSyncState":"Sync not achieved" + }, + "r1-eth2":{ + "ldpIgpSyncEnabled":true, + "holdDownTimeInSec":0, + "ldpIgpSyncState":"Sync achieved" + } +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r1/zebra.conf b/tests/topotests/ldp-sync-ospf-topo1/r1/zebra.conf new file mode 100644 index 0000000000..ea047355ad --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r1/zebra.conf @@ -0,0 +1,29 @@ +log file zebra.log +! +hostname r1 +! +debug zebra kernel +debug zebra rib detailed +debug zebra dplane detailed +debug zebra nht +debug zebra pseudowires +debug zebra mpls +! +interface lo + ip address 1.1.1.1/32 +! +interface r1-eth0 + description to s1 +! +interface r1-eth1 + description to s4 + ip address 10.0.1.1/24 +! +interface r1-eth2 + description to s5 + ip address 10.0.2.1/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ldp-sync-ospf-topo1/r2/ldpd.conf b/tests/topotests/ldp-sync-ospf-topo1/r2/ldpd.conf new file mode 100644 index 0000000000..52398b1b72 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r2/ldpd.conf @@ -0,0 +1,33 @@ +hostname r2 +log file ldpd.log +! +debug mpls ldp zebra +debug mpls ldp event +debug mpls ldp errors +debug mpls ldp sync +! +mpls ldp + router-id 2.2.2.2 + ! + address-family ipv4 + discovery transport-address 2.2.2.2 + label local allocate host-routes + ! + ttl-security disable + ! + interface r2-eth1 + ! + interface r2-eth2 + ! + ! +! +l2vpn CUST_A type vpls + member interface r2-eth0 + ! + member pseudowire r2-mpw0 + neighbor lsr-id 1.1.1.1 + pw-id 100 + ! +! +line vty +! diff --git a/tests/topotests/ldp-sync-ospf-topo1/r2/ospfd.conf b/tests/topotests/ldp-sync-ospf-topo1/r2/ospfd.conf new file mode 100644 index 0000000000..f93f6aed56 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r2/ospfd.conf @@ -0,0 +1,19 @@ +hostname r2 +log file ospfd.log +debug ospf zebra interface +debug ospf ldp-sync +! +router ospf + router-id 2.2.2.2 + network 0.0.0.0/0 area 0 + mpls ldp-sync + mpls ldp-sync holddown 50 +! +interface r2-eth1 + ip ospf network point-to-point + ip ospf mpls ldp-sync holddown 300 +! +interface r2-eth2 + ip ospf network point-to-point + no ip ospf mpls ldp-sync +! diff --git a/tests/topotests/ldp-sync-ospf-topo1/r2/show_ip_ospf_interface.ref b/tests/topotests/ldp-sync-ospf-topo1/r2/show_ip_ospf_interface.ref new file mode 100644 index 0000000000..82806721e7 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r2/show_ip_ospf_interface.ref @@ -0,0 +1,11 @@ +{ + "interfaces":{ + "r2-eth1":{ + "cost":10 + }, + "r2-eth2":{ + "cost":10 + } + } +} + diff --git a/tests/topotests/ldp-sync-ospf-topo1/r2/show_ip_ospf_interface_r1_eth1_shutdown.ref b/tests/topotests/ldp-sync-ospf-topo1/r2/show_ip_ospf_interface_r1_eth1_shutdown.ref new file mode 100644 index 0000000000..71e8af1778 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r2/show_ip_ospf_interface_r1_eth1_shutdown.ref @@ -0,0 +1,11 @@ +{ + "interfaces":{ + "r2-eth1":{ + "cost":65535 + }, + "r2-eth2":{ + "cost":10 + } + } +} + diff --git a/tests/topotests/ldp-sync-ospf-topo1/r2/show_ip_ospf_interface_r2_eth1_shutdown.ref b/tests/topotests/ldp-sync-ospf-topo1/r2/show_ip_ospf_interface_r2_eth1_shutdown.ref new file mode 100644 index 0000000000..af9a9c80e5 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r2/show_ip_ospf_interface_r2_eth1_shutdown.ref @@ -0,0 +1,8 @@ +{ + "interfaces":{ + "r2-eth2":{ + "cost":10 + } + } +} + diff --git a/tests/topotests/ldp-sync-ospf-topo1/r2/show_ip_ospf_neighbor.json b/tests/topotests/ldp-sync-ospf-topo1/r2/show_ip_ospf_neighbor.json new file mode 100644 index 0000000000..5b7a5ebbb9 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r2/show_ip_ospf_neighbor.json @@ -0,0 +1,26 @@ +{ + "neighbors": { + "1.1.1.1": [ + { + "priority":1, + "state":"Full/DROther", + "address":"10.0.1.1", + "ifaceName":"r2-eth1:10.0.1.2", + "retransmitCounter":0, + "requestCounter":0, + "dbSummaryCounter":0 + } + ], + "3.3.3.3": [ + { + "priority":1, + "state":"Full/DROther", + "address":"10.0.3.3", + "ifaceName":"r2-eth2:10.0.3.2", + "retransmitCounter":0, + "requestCounter":0, + "dbSummaryCounter":0 + } + ] + } +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r2/show_ip_route.ref b/tests/topotests/ldp-sync-ospf-topo1/r2/show_ip_route.ref new file mode 100644 index 0000000000..7147c6a595 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r2/show_ip_route.ref @@ -0,0 +1,157 @@ +{ + "1.1.1.1/32":[ + { + "prefix":"1.1.1.1/32", + "protocol":"ospf", + "selected":true, + "distance":110, + "metric":10, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceIndex":3, + "interfaceName":"r2-eth1", + "active":true + } + ] + } + ], + "2.2.2.2/32":[ + { + "prefix":"2.2.2.2/32", + "protocol":"ospf", + "distance":110, + "metric":0, + "nexthops":[ + { + "directlyConnected":true, + "interfaceIndex":1, + "interfaceName":"lo", + "active":true + } + ] + }, + { + "prefix":"2.2.2.2/32", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceIndex":1, + "interfaceName":"lo", + "active":true + } + ] + } + ], + "3.3.3.3/32":[ + { + "prefix":"3.3.3.3/32", + "protocol":"ospf", + "selected":true, + "distance":110, + "metric":10, + "nexthops":[ + { + "fib":true, + "ip":"10.0.3.3", + "afi":"ipv4", + "interfaceIndex":4, + "interfaceName":"r2-eth2", + "active":true + } + ] + } + ], + "10.0.1.0/24":[ + { + "prefix":"10.0.1.0/24", + "protocol":"ospf", + "distance":110, + "metric":10, + "nexthops":[ + { + "directlyConnected":true, + "interfaceIndex":3, + "interfaceName":"r2-eth1", + "active":true + } + ] + }, + { + "prefix":"10.0.1.0/24", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceIndex":3, + "interfaceName":"r2-eth1", + "active":true + } + ] + } + ], + "10.0.2.0/24":[ + { + "prefix":"10.0.2.0/24", + "protocol":"ospf", + "selected":true, + "distance":110, + "metric":20, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceIndex":3, + "interfaceName":"r2-eth1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.3", + "afi":"ipv4", + "interfaceIndex":4, + "interfaceName":"r2-eth2", + "active":true + } + ] + } + ], + "10.0.3.0/24":[ + { + "prefix":"10.0.3.0/24", + "protocol":"ospf", + "distance":110, + "metric":10, + "nexthops":[ + { + "directlyConnected":true, + "interfaceIndex":4, + "interfaceName":"r2-eth2", + "active":true + } + ] + }, + { + "prefix":"10.0.3.0/24", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceIndex":4, + "interfaceName":"r2-eth2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r2/show_l2vpn_binding.ref b/tests/topotests/ldp-sync-ospf-topo1/r2/show_l2vpn_binding.ref new file mode 100644 index 0000000000..42c5a1cbd9 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r2/show_l2vpn_binding.ref @@ -0,0 +1,16 @@ +{ + "1.1.1.1: 100":{ + "destination":"1.1.1.1", + "vcId":100, + "localLabel":16, + "localControlWord":1, + "localVcType":"Ethernet", + "localGroupID":0, + "localIfMtu":1500, + "remoteLabel":16, + "remoteControlWord":1, + "remoteVcType":"Ethernet", + "remoteGroupID":0, + "remoteIfMtu":1500 + } +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r2/show_l2vpn_vc.ref b/tests/topotests/ldp-sync-ospf-topo1/r2/show_l2vpn_vc.ref new file mode 100644 index 0000000000..942ed23a1e --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r2/show_l2vpn_vc.ref @@ -0,0 +1,8 @@ +{ + "r2-mpw0":{ + "peerId":"1.1.1.1", + "vcId":100, + "VpnName":"CUST_A", + "status":"up" + } +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r2/show_ldp_binding.ref b/tests/topotests/ldp-sync-ospf-topo1/r2/show_ldp_binding.ref new file mode 100644 index 0000000000..c641fb47e6 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r2/show_ldp_binding.ref @@ -0,0 +1,44 @@ +{ + "bindings":[ + { + "addressFamily":"ipv4", + "prefix":"1.1.1.1/32", + "neighborId":"1.1.1.1", + "remoteLabel":"imp-null", + "inUse":1 + }, + { + "addressFamily":"ipv4", + "prefix":"1.1.1.1/32", + "neighborId":"3.3.3.3", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"2.2.2.2/32", + "neighborId":"1.1.1.1", + "localLabel":"imp-null", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"2.2.2.2/32", + "neighborId":"3.3.3.3", + "localLabel":"imp-null", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"3.3.3.3/32", + "neighborId":"1.1.1.1", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"3.3.3.3/32", + "neighborId":"3.3.3.3", + "remoteLabel":"imp-null", + "inUse":1 + } + ] +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r2/show_ldp_discovery.ref b/tests/topotests/ldp-sync-ospf-topo1/r2/show_ldp_discovery.ref new file mode 100644 index 0000000000..26801acade --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r2/show_ldp_discovery.ref @@ -0,0 +1,25 @@ +{ + "adjacencies":[ + { + "addressFamily":"ipv4", + "neighborId":"1.1.1.1", + "type":"link", + "interface":"r2-eth1", + "helloHoldtime":15 + }, + { + "addressFamily":"ipv4", + "neighborId":"1.1.1.1", + "type":"targeted", + "peer":"1.1.1.1", + "helloHoldtime":45 + }, + { + "addressFamily":"ipv4", + "neighborId":"3.3.3.3", + "type":"link", + "interface":"r2-eth2", + "helloHoldtime":15 + } + ] +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r2/show_ldp_igp_sync.ref b/tests/topotests/ldp-sync-ospf-topo1/r2/show_ldp_igp_sync.ref new file mode 100644 index 0000000000..f2b24d7d62 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r2/show_ldp_igp_sync.ref @@ -0,0 +1,16 @@ +{ + "r2-eth1":{ + "state":"labelExchangeComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"1.1.1.1" + }, + "r2-eth2":{ + "state":"labelExchangeComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"3.3.3.3" + } +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r2/show_ldp_igp_sync_r1_eth1_shutdown.ref b/tests/topotests/ldp-sync-ospf-topo1/r2/show_ldp_igp_sync_r1_eth1_shutdown.ref new file mode 100644 index 0000000000..b5508dd35c --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r2/show_ldp_igp_sync_r1_eth1_shutdown.ref @@ -0,0 +1,16 @@ +{ + "r2-eth1":{ + "state":"labelExchangeNotComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"" + }, + "r2-eth2":{ + "state":"labelExchangeComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"3.3.3.3" + } +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r2/show_ldp_neighbor.ref b/tests/topotests/ldp-sync-ospf-topo1/r2/show_ldp_neighbor.ref new file mode 100644 index 0000000000..eed35289ea --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r2/show_ldp_neighbor.ref @@ -0,0 +1,16 @@ +{ + "neighbors":[ + { + "addressFamily":"ipv4", + "neighborId":"1.1.1.1", + "state":"OPERATIONAL", + "transportAddress":"1.1.1.1" + }, + { + "addressFamily":"ipv4", + "neighborId":"3.3.3.3", + "state":"OPERATIONAL", + "transportAddress":"3.3.3.3" + } + ] +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r2/show_ospf_ldp_sync.ref b/tests/topotests/ldp-sync-ospf-topo1/r2/show_ospf_ldp_sync.ref new file mode 100644 index 0000000000..6c27a10427 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r2/show_ospf_ldp_sync.ref @@ -0,0 +1,12 @@ +{ + "r2-eth1":{ + "ldpIgpSyncEnabled":true, + "holdDownTimeInSec":300, + "ldpIgpSyncState":"Sync achieved" + }, + "r2-eth2":{ + "ldpIgpSyncEnabled":false, + "holdDownTimeInSec":50, + "ldpIgpSyncState":"Sync not required" + } +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r2/show_ospf_ldp_sync_r1_eth1_shutdown.ref b/tests/topotests/ldp-sync-ospf-topo1/r2/show_ospf_ldp_sync_r1_eth1_shutdown.ref new file mode 100644 index 0000000000..889f69ed7f --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r2/show_ospf_ldp_sync_r1_eth1_shutdown.ref @@ -0,0 +1,12 @@ +{ + "r2-eth1":{ + "ldpIgpSyncEnabled":true, + "holdDownTimeInSec":300, + "ldpIgpSyncState":"Holding down until Sync" + }, + "r2-eth2":{ + "ldpIgpSyncEnabled":false, + "holdDownTimeInSec":50, + "ldpIgpSyncState":"Sync not required" + } +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r2/show_ospf_ldp_sync_r2_eth1_shutdown.ref b/tests/topotests/ldp-sync-ospf-topo1/r2/show_ospf_ldp_sync_r2_eth1_shutdown.ref new file mode 100644 index 0000000000..d9036e124b --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r2/show_ospf_ldp_sync_r2_eth1_shutdown.ref @@ -0,0 +1,7 @@ +{ + "r2-eth2":{ + "ldpIgpSyncEnabled":false, + "holdDownTimeInSec":50, + "ldpIgpSyncState":"Sync not required" + } +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r2/zebra.conf b/tests/topotests/ldp-sync-ospf-topo1/r2/zebra.conf new file mode 100644 index 0000000000..c244442876 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r2/zebra.conf @@ -0,0 +1,28 @@ +log file zebra.log +! +hostname r2 +! +debug zebra rib detailed +debug zebra dplane detailed +debug zebra kernel +debug zebra nht +debug zebra pseudowires +! +interface lo + ip address 2.2.2.2/32 +! +interface r2-eth0 + description to s2 +! +interface r2-eth1 + description to s4 + ip address 10.0.1.2/24 +! +interface r2-eth2 + description to s6 + ip address 10.0.3.2/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ldp-sync-ospf-topo1/r3/ldpd.conf b/tests/topotests/ldp-sync-ospf-topo1/r3/ldpd.conf new file mode 100644 index 0000000000..2935caf13b --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r3/ldpd.conf @@ -0,0 +1,25 @@ +hostname r3 +log file ldpd.log +! +debug mpls ldp zebra +debug mpls ldp event +debug mpls ldp errors +debug mpls ldp sync +! +mpls ldp + router-id 3.3.3.3 + ! + address-family ipv4 + discovery transport-address 3.3.3.3 + label local allocate host-routes + ! + ttl-security disable + ! + interface r3-eth1 + ! + interface r3-eth2 + ! + ! +! +line vty +! diff --git a/tests/topotests/ldp-sync-ospf-topo1/r3/ospfd.conf b/tests/topotests/ldp-sync-ospf-topo1/r3/ospfd.conf new file mode 100644 index 0000000000..09eea759ad --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r3/ospfd.conf @@ -0,0 +1,18 @@ +hostname r3 +log file ospfd.log +debug ospf zebra interface +debug ospf ldp-sync +! +router ospf + router-id 3.3.3.3 + network 0.0.0.0/0 area 0 + mpls ldp-sync + mpls ldp-sync holddown 50 +! +interface r3-eth1 + ip ospf network point-to-point + no ip ospf mpls ldp-sync +! +interface r3-eth2 + ip ospf network point-to-point +! diff --git a/tests/topotests/ldp-sync-ospf-topo1/r3/show_ip_ospf_interface.ref b/tests/topotests/ldp-sync-ospf-topo1/r3/show_ip_ospf_interface.ref new file mode 100644 index 0000000000..aec97b30cb --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r3/show_ip_ospf_interface.ref @@ -0,0 +1,11 @@ +{ + "interfaces":{ + "r3-eth1":{ + "cost":10 + }, + "r3-eth2":{ + "cost":10 + } + } +} + diff --git a/tests/topotests/ldp-sync-ospf-topo1/r3/show_ip_ospf_interface_r1_eth1_shutdown.ref b/tests/topotests/ldp-sync-ospf-topo1/r3/show_ip_ospf_interface_r1_eth1_shutdown.ref new file mode 100644 index 0000000000..aec97b30cb --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r3/show_ip_ospf_interface_r1_eth1_shutdown.ref @@ -0,0 +1,11 @@ +{ + "interfaces":{ + "r3-eth1":{ + "cost":10 + }, + "r3-eth2":{ + "cost":10 + } + } +} + diff --git a/tests/topotests/ldp-sync-ospf-topo1/r3/show_ip_ospf_interface_r2_eth1_shutdown.ref b/tests/topotests/ldp-sync-ospf-topo1/r3/show_ip_ospf_interface_r2_eth1_shutdown.ref new file mode 100644 index 0000000000..aec97b30cb --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r3/show_ip_ospf_interface_r2_eth1_shutdown.ref @@ -0,0 +1,11 @@ +{ + "interfaces":{ + "r3-eth1":{ + "cost":10 + }, + "r3-eth2":{ + "cost":10 + } + } +} + diff --git a/tests/topotests/ldp-sync-ospf-topo1/r3/show_ip_ospf_neighbor.json b/tests/topotests/ldp-sync-ospf-topo1/r3/show_ip_ospf_neighbor.json new file mode 100644 index 0000000000..1b29b9f947 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r3/show_ip_ospf_neighbor.json @@ -0,0 +1,26 @@ +{ + "neighbors": { + "1.1.1.1": [ + { + "priority":1, + "state":"Full/DROther", + "address":"10.0.2.1", + "ifaceName":"r3-eth1:10.0.2.3", + "retransmitCounter":0, + "requestCounter":0, + "dbSummaryCounter":0 + } + ], + "2.2.2.2": [ + { + "priority":1, + "state":"Full/DROther", + "address":"10.0.3.2", + "ifaceName":"r3-eth2:10.0.3.3", + "retransmitCounter":0, + "requestCounter":0, + "dbSummaryCounter":0 + } + ] + } +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r3/show_ip_route.ref b/tests/topotests/ldp-sync-ospf-topo1/r3/show_ip_route.ref new file mode 100644 index 0000000000..d77de7c9e3 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r3/show_ip_route.ref @@ -0,0 +1,157 @@ +{ + "1.1.1.1/32":[ + { + "prefix":"1.1.1.1/32", + "protocol":"ospf", + "selected":true, + "distance":110, + "metric":10, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.1", + "afi":"ipv4", + "interfaceIndex":3, + "interfaceName":"r3-eth1", + "active":true + } + ] + } + ], + "2.2.2.2/32":[ + { + "prefix":"2.2.2.2/32", + "protocol":"ospf", + "selected":true, + "distance":110, + "metric":10, + "nexthops":[ + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceIndex":4, + "interfaceName":"r3-eth2", + "active":true + } + ] + } + ], + "3.3.3.3/32":[ + { + "prefix":"3.3.3.3/32", + "protocol":"ospf", + "distance":110, + "metric":0, + "nexthops":[ + { + "directlyConnected":true, + "interfaceIndex":1, + "interfaceName":"lo", + "active":true + } + ] + }, + { + "prefix":"3.3.3.3/32", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceIndex":1, + "interfaceName":"lo", + "active":true + } + ] + } + ], + "10.0.1.0/24":[ + { + "prefix":"10.0.1.0/24", + "protocol":"ospf", + "selected":true, + "distance":110, + "metric":20, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.1", + "afi":"ipv4", + "interfaceIndex":3, + "interfaceName":"r3-eth1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceIndex":4, + "interfaceName":"r3-eth2", + "active":true + } + ] + } + ], + "10.0.2.0/24":[ + { + "prefix":"10.0.2.0/24", + "protocol":"ospf", + "distance":110, + "metric":10, + "nexthops":[ + { + "directlyConnected":true, + "interfaceIndex":3, + "interfaceName":"r3-eth1", + "active":true + } + ] + }, + { + "prefix":"10.0.2.0/24", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceIndex":3, + "interfaceName":"r3-eth1", + "active":true + } + ] + } + ], + "10.0.3.0/24":[ + { + "prefix":"10.0.3.0/24", + "protocol":"ospf", + "distance":110, + "metric":10, + "nexthops":[ + { + "directlyConnected":true, + "interfaceIndex":4, + "interfaceName":"r3-eth2", + "active":true + } + ] + }, + { + "prefix":"10.0.3.0/24", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceIndex":4, + "interfaceName":"r3-eth2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r3/show_l2vpn_binding.ref b/tests/topotests/ldp-sync-ospf-topo1/r3/show_l2vpn_binding.ref new file mode 100644 index 0000000000..2c63c08510 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r3/show_l2vpn_binding.ref @@ -0,0 +1,2 @@ +{ +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r3/show_l2vpn_vc.ref b/tests/topotests/ldp-sync-ospf-topo1/r3/show_l2vpn_vc.ref new file mode 100644 index 0000000000..2c63c08510 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r3/show_l2vpn_vc.ref @@ -0,0 +1,2 @@ +{ +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r3/show_ldp_binding.ref b/tests/topotests/ldp-sync-ospf-topo1/r3/show_ldp_binding.ref new file mode 100644 index 0000000000..e54bd6e755 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r3/show_ldp_binding.ref @@ -0,0 +1,44 @@ +{ + "bindings":[ + { + "addressFamily":"ipv4", + "prefix":"1.1.1.1/32", + "neighborId":"1.1.1.1", + "remoteLabel":"imp-null", + "inUse":1 + }, + { + "addressFamily":"ipv4", + "prefix":"1.1.1.1/32", + "neighborId":"2.2.2.2", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"2.2.2.2/32", + "neighborId":"1.1.1.1", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"2.2.2.2/32", + "neighborId":"2.2.2.2", + "remoteLabel":"imp-null", + "inUse":1 + }, + { + "addressFamily":"ipv4", + "prefix":"3.3.3.3/32", + "neighborId":"1.1.1.1", + "localLabel":"imp-null", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"3.3.3.3/32", + "neighborId":"2.2.2.2", + "localLabel":"imp-null", + "inUse":0 + } + ] +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r3/show_ldp_discovery.ref b/tests/topotests/ldp-sync-ospf-topo1/r3/show_ldp_discovery.ref new file mode 100644 index 0000000000..42fa98d4da --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r3/show_ldp_discovery.ref @@ -0,0 +1,18 @@ +{ + "adjacencies":[ + { + "addressFamily":"ipv4", + "neighborId":"1.1.1.1", + "type":"link", + "interface":"r3-eth1", + "helloHoldtime":15 + }, + { + "addressFamily":"ipv4", + "neighborId":"2.2.2.2", + "type":"link", + "interface":"r3-eth2", + "helloHoldtime":15 + } + ] +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r3/show_ldp_igp_sync.ref b/tests/topotests/ldp-sync-ospf-topo1/r3/show_ldp_igp_sync.ref new file mode 100644 index 0000000000..73261830c9 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r3/show_ldp_igp_sync.ref @@ -0,0 +1,16 @@ +{ + "r3-eth1":{ + "state":"labelExchangeComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"1.1.1.1" + }, + "r3-eth2":{ + "state":"labelExchangeComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"2.2.2.2" + } +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r3/show_ldp_igp_sync_r1_eth1_shutdown.ref b/tests/topotests/ldp-sync-ospf-topo1/r3/show_ldp_igp_sync_r1_eth1_shutdown.ref new file mode 100644 index 0000000000..73261830c9 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r3/show_ldp_igp_sync_r1_eth1_shutdown.ref @@ -0,0 +1,16 @@ +{ + "r3-eth1":{ + "state":"labelExchangeComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"1.1.1.1" + }, + "r3-eth2":{ + "state":"labelExchangeComplete", + "waitTime":10, + "waitTimeRemaining":0, + "timerRunning":false, + "peerLdpId":"2.2.2.2" + } +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r3/show_ldp_neighbor.ref b/tests/topotests/ldp-sync-ospf-topo1/r3/show_ldp_neighbor.ref new file mode 100644 index 0000000000..5c482da697 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r3/show_ldp_neighbor.ref @@ -0,0 +1,16 @@ +{ + "neighbors":[ + { + "addressFamily":"ipv4", + "neighborId":"1.1.1.1", + "state":"OPERATIONAL", + "transportAddress":"1.1.1.1" + }, + { + "addressFamily":"ipv4", + "neighborId":"2.2.2.2", + "state":"OPERATIONAL", + "transportAddress":"2.2.2.2" + } + ] +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r3/show_ospf_ldp_sync.ref b/tests/topotests/ldp-sync-ospf-topo1/r3/show_ospf_ldp_sync.ref new file mode 100644 index 0000000000..b417ab040a --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r3/show_ospf_ldp_sync.ref @@ -0,0 +1,12 @@ +{ + "r3-eth1":{ + "ldpIgpSyncEnabled":false, + "holdDownTimeInSec":50, + "ldpIgpSyncState":"Sync not required" + }, + "r3-eth2":{ + "ldpIgpSyncEnabled":true, + "holdDownTimeInSec":50, + "ldpIgpSyncState":"Sync achieved" + } +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r3/show_ospf_ldp_sync_r1_eth1_shutdown.ref b/tests/topotests/ldp-sync-ospf-topo1/r3/show_ospf_ldp_sync_r1_eth1_shutdown.ref new file mode 100644 index 0000000000..b417ab040a --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r3/show_ospf_ldp_sync_r1_eth1_shutdown.ref @@ -0,0 +1,12 @@ +{ + "r3-eth1":{ + "ldpIgpSyncEnabled":false, + "holdDownTimeInSec":50, + "ldpIgpSyncState":"Sync not required" + }, + "r3-eth2":{ + "ldpIgpSyncEnabled":true, + "holdDownTimeInSec":50, + "ldpIgpSyncState":"Sync achieved" + } +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r3/show_ospf_ldp_sync_r2_eth1_shutdown.ref b/tests/topotests/ldp-sync-ospf-topo1/r3/show_ospf_ldp_sync_r2_eth1_shutdown.ref new file mode 100644 index 0000000000..b417ab040a --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r3/show_ospf_ldp_sync_r2_eth1_shutdown.ref @@ -0,0 +1,12 @@ +{ + "r3-eth1":{ + "ldpIgpSyncEnabled":false, + "holdDownTimeInSec":50, + "ldpIgpSyncState":"Sync not required" + }, + "r3-eth2":{ + "ldpIgpSyncEnabled":true, + "holdDownTimeInSec":50, + "ldpIgpSyncState":"Sync achieved" + } +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/r3/zebra.conf b/tests/topotests/ldp-sync-ospf-topo1/r3/zebra.conf new file mode 100644 index 0000000000..b1919bd296 --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/r3/zebra.conf @@ -0,0 +1,32 @@ +log file zebra.log +! +hostname r3 +! +debug zebra rib detailed +debug zebra dplane detailed +debug zebra kernel +debug zebra nht +debug zebra pseudowires +! +interface lo + ip address 3.3.3.3/32 +! +interface r3-eth0 + description to s3 +! +interface r3-eth1 + description to s5 + ip address 10.0.2.3/24 +! +interface r3-eth2 + description to s6 + ip address 10.0.3.3/24 +! +!!interface r3-eth3 +!! description to s4 +!! ip address 10.0.1.3/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ldp-sync-ospf-topo1/test_ldp_sync_ospf_topo1.dot b/tests/topotests/ldp-sync-ospf-topo1/test_ldp_sync_ospf_topo1.dot new file mode 100644 index 0000000000..4f1bd22f7c --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/test_ldp_sync_ospf_topo1.dot @@ -0,0 +1,111 @@ +## Color coding: +######################### +## Main FRR: #f08080 red +## Switches: #d0e0d0 gray +## RIP: #19e3d9 Cyan +## RIPng: #fcb314 dark yellow +## OSPFv2: #32b835 Green +## OSPFv3: #19e3d9 Cyan +## ISIS IPv4 #fcb314 dark yellow +## ISIS IPv6 #9a81ec purple +## BGP IPv4 #eee3d3 beige +## BGP IPv6 #fdff00 yellow +##### Colors (see http://www.color-hex.com/) + +graph template { + label="Test Topology - LDP-VPLS 1"; + + # Routers + ce1 [ + shape=doubleoctagon, + label="ce1", + fillcolor="#f08080", + style=filled, + ]; + ce2 [ + shape=doubleoctagon + label="ce2", + fillcolor="#f08080", + style=filled, + ]; + ce3 [ + shape=doubleoctagon + label="ce3", + fillcolor="#f08080", + style=filled, + ]; + r1 [ + shape=doubleoctagon, + label="r1", + fillcolor="#f08080", + style=filled, + ]; + r2 [ + shape=doubleoctagon + label="r2", + fillcolor="#f08080", + style=filled, + ]; + r3 [ + shape=doubleoctagon + label="r3", + fillcolor="#f08080", + style=filled, + ]; + + + # Switches + s1 [ + shape=oval, + label="VPLS\n172.16.1.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + s2 [ + shape=oval, + label="VPLS\n172.16.1.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + s3 [ + shape=oval, + label="VPLS\n172.16.1.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + s4 [ + shape=oval, + label="s4\n10.0.1.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + s5 [ + shape=oval, + label="s5\n10.0.2.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + s6 [ + shape=oval, + label="s6\n10.0.3.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + + # Connections + ce1 -- s1 [label="eth0\n.1"]; + ce2 -- s2 [label="eth0\n.2"]; + ce3 -- s3 [label="eth0\n.3"]; + + r1 -- s1 [label="eth0"]; + r1 -- s4 [label="eth1\n.1"]; + r1 -- s5 [label="eth2\n.1"]; + + r2 -- s2 [label="eth0"]; + r2 -- s4 [label="eth1\n.2"]; + r2 -- s6 [label="eth2\n.2"]; + + r3 -- s3 [label="eth0"]; + r3 -- s5 [label="eth1\n.3"]; + r3 -- s6 [label="eth2\n.3"]; +} diff --git a/tests/topotests/ldp-sync-ospf-topo1/test_ldp_sync_ospf_topo1.py b/tests/topotests/ldp-sync-ospf-topo1/test_ldp_sync_ospf_topo1.py new file mode 100644 index 0000000000..9694fa982f --- /dev/null +++ b/tests/topotests/ldp-sync-ospf-topo1/test_ldp_sync_ospf_topo1.py @@ -0,0 +1,429 @@ +#!/usr/bin/env python + +# +# test_ldp_ospf_topo1.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 by Volta Networks +# +# 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_ldp_vpls_topo1.py: + + +---------+ +---------+ + | | | | + | CE1 | | CE2 | + | | | | + +---------+ +---------+ +ce1-eth0 (172.16.1.1/24)| |ce2-eth0 (172.16.1.2/24) + | | + | | + rt1-eth0| |rt2-eth0 + +---------+ 10.0.1.0/24 +---------+ + | |rt1-eth1 | | + | RT1 +----------------+ RT2 | + | 1.1.1.1 | rt2-eth1| 2.2.2.2 | + | | | | + +---------+ +---------+ + rt1-eth2| |rt2-eth2 + | | + | | + 10.0.2.0/24| +---------+ |10.0.3.0/24 + | | | | + | | RT3 | | + +--------+ 3.3.3.3 +-------+ + rt3-eth2| |rt3-eth1 + +---------+ + |rt3-eth0 + | + | + ce3-eth0 (172.16.1.3/24)| + +---------+ + | | + | CE3 | + | | + +---------+ +""" + +import os +import sys +import pytest +import json +from time import sleep +from functools import partial + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + + +class TemplateTopo(Topo): + "Test topology builder" + + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # + # Define FRR Routers + # + for router in ["ce1", "ce2", "ce3", "r1", "r2", "r3"]: + tgen.add_router(router) + + # + # Define connections + # + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["ce1"]) + switch.add_link(tgen.gears["r1"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["ce2"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["ce3"]) + switch.add_link(tgen.gears["r3"]) + + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s5") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r3"]) + + switch = tgen.add_switch("s6") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(TemplateTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + # For all registered routers, load the zebra configuration file + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + # Don't start ospfd and ldpd in the CE nodes + if router.name[0] == "r": + router.load_config( + TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_LDP, os.path.join(CWD, "{}/ldpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +def router_compare_json_output(rname, command, reference): + "Compare router JSON output" + + logger.info('Comparing router "%s" "%s" output', rname, command) + + tgen = get_topogen() + filename = "{}/{}/{}".format(CWD, rname, reference) + expected = json.loads(open(filename).read()) + + # Run test function until we get an result. + test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected) + _, diff = topotest.run_and_expect(test_func, None, count=320, wait=0.5) + assertmsg = '"{}" JSON output mismatches the expected result'.format(rname) + assert diff is None, assertmsg + + +def test_ospf_convergence(): + logger.info("Test: check OSPF adjacencies") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show ip ospf neighbor json", "show_ip_ospf_neighbor.json" + ) + + +def test_rib(): + logger.info("Test: verify RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output(rname, "show ip route json", "show_ip_route.ref") + + +def test_ldp_adjacencies(): + logger.info("Test: verify LDP adjacencies") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show mpls ldp discovery json", "show_ldp_discovery.ref" + ) + + +def test_ldp_neighbors(): + logger.info("Test: verify LDP neighbors") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show mpls ldp neighbor json", "show_ldp_neighbor.ref" + ) + + +def test_ldp_bindings(): + logger.info("Test: verify LDP bindings") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show mpls ldp binding json", "show_ldp_binding.ref" + ) + + +def test_ldp_pwid_bindings(): + logger.info("Test: verify LDP PW-ID bindings") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show l2vpn atom binding json", "show_l2vpn_binding.ref" + ) + + +def test_ldp_pseudowires(): + logger.info("Test: verify LDP pseudowires") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show l2vpn atom vc json", "show_l2vpn_vc.ref" + ) + +def test_ldp_igp_sync(): + logger.info("Test: verify LDP igp-sync") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show mpls ldp igp-sync json", "show_ldp_igp_sync.ref" + ) + +def test_ospf_ldp_sync(): + logger.info("Test: verify OSPF igp-sync") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show ip ospf mpls ldp-sync json", "show_ospf_ldp_sync.ref" + ) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show ip ospf interface json", "show_ip_ospf_interface.ref" + ) + + +def test_r1_eth1_shutdown(): + logger.info("Test: verify behaviour after r1-eth1 is shutdown") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Shut down r1-r2 link */ + tgen = get_topogen() + tgen.gears["r1"].peer_link_enable("r1-eth1", False) + topotest.sleep(5, "Waiting for the network to reconverge") + + # check if the pseudowire is still up (using an alternate path for nexthop resolution) + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show l2vpn atom vc json", "show_l2vpn_vc.ref" + ) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show mpls ldp igp-sync json", "show_ldp_igp_sync_r1_eth1_shutdown.ref" + ) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show ip ospf mpls ldp-sync json", "show_ospf_ldp_sync_r1_eth1_shutdown.ref" + ) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show ip ospf interface json", "show_ip_ospf_interface_r1_eth1_shutdown.ref" + ) + +def test_r1_eth1_no_shutdown(): + logger.info("Test: verify behaviour after r1-eth1 is no shutdown") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Run no shutdown on r1-eth1 interface */ + tgen = get_topogen() + tgen.gears["r1"].peer_link_enable("r1-eth1", True) + topotest.sleep(5, "Waiting for the network to reconverge") + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show mpls ldp igp-sync json", "show_ldp_igp_sync.ref" + ) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show ip ospf mpls ldp-sync json", "show_ospf_ldp_sync.ref" + ) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show ip ospf interface json", "show_ip_ospf_interface.ref" + ) + +def test_r2_eth1_shutdown(): + logger.info("Test: verify behaviour after r2-eth1 is shutdown") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Shut down r1-r2 link */ + tgen = get_topogen() + tgen.gears["r2"].peer_link_enable("r2-eth1", False) + topotest.sleep(5, "Waiting for the network to reconverge") + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show mpls ldp igp-sync json", "show_ldp_igp_sync_r1_eth1_shutdown.ref" + ) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show ip ospf mpls ldp-sync json", "show_ospf_ldp_sync_r2_eth1_shutdown.ref" + ) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show ip ospf interface json", "show_ip_ospf_interface_r2_eth1_shutdown.ref" + ) + +def test_r2_eth1_no_shutdown(): + logger.info("Test: verify behaviour after r2-eth1 is no shutdown") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Run no shutdown on r2-eth1 interface */ + tgen = get_topogen() + tgen.gears["r2"].peer_link_enable("r2-eth1", True) + topotest.sleep(5, "Waiting for the network to reconverge") + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show mpls ldp igp-sync json", "show_ldp_igp_sync.ref" + ) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show ip ospf mpls ldp-sync json", "show_ospf_ldp_sync.ref" + ) + + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show ip ospf interface json", "show_ip_ospf_interface.ref" + ) + +# Memory leak test template +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/ldp-vpls-topo1/test_ldp_vpls_topo1.py b/tests/topotests/ldp-vpls-topo1/test_ldp_vpls_topo1.py index a1662dc411..0b8bf4de0e 100755..100644 --- a/tests/topotests/ldp-vpls-topo1/test_ldp_vpls_topo1.py +++ b/tests/topotests/ldp-vpls-topo1/test_ldp_vpls_topo1.py @@ -130,7 +130,7 @@ def setup_module(mod): router_list = tgen.routers() # For all registered routers, load the zebra configuration file - for rname, router in router_list.iteritems(): + for rname, router in router_list.items(): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ) diff --git a/tests/topotests/lib/bgp.py b/tests/topotests/lib/bgp.py index e2f887bb89..a3d846edbb 100644 --- a/tests/topotests/lib/bgp.py +++ b/tests/topotests/lib/bgp.py @@ -367,7 +367,7 @@ def __create_bgp_unicast_neighbor( bgp_data = input_dict["address_family"] - for addr_type, addr_dict in bgp_data.iteritems(): + for addr_type, addr_dict in bgp_data.items(): if not addr_dict: continue @@ -391,12 +391,11 @@ def __create_bgp_unicast_neighbor( del_action = advertise_network_dict.setdefault("delete", False) # Generating IPs for verification - prefix = str(ipaddress.ip_network(unicode(network[0])).prefixlen) network_list = generate_ips(network, no_of_network) for ip in network_list: - ip = str(ipaddress.ip_network(unicode(ip)).network_address) + ip = str(ipaddress.ip_network(unicode(ip))) - cmd = "network {}/{}".format(ip, prefix) + cmd = "network {}".format(ip) if del_action: cmd = "no {}".format(cmd) @@ -471,7 +470,7 @@ def __create_bgp_unicast_neighbor( ) config_data.extend(neigh_data) - for addr_type, addr_dict in bgp_data.iteritems(): + for addr_type, addr_dict in bgp_data.items(): if not addr_dict or not check_address_types(addr_type): continue @@ -509,7 +508,7 @@ def __create_l2vpn_evpn_address_family( bgp_data = input_dict["address_family"] - for family_type, family_dict in bgp_data.iteritems(): + for family_type, family_dict in bgp_data.items(): if family_type != "l2vpn": continue @@ -665,8 +664,8 @@ def __create_bgp_neighbor(topo, input_dict, router, addr_type, add_neigh=True): bgp_data = input_dict["address_family"] neigh_data = bgp_data[addr_type]["unicast"]["neighbor"] - for name, peer_dict in neigh_data.iteritems(): - for dest_link, peer in peer_dict["dest_link"].iteritems(): + for name, peer_dict in neigh_data.items(): + for dest_link, peer in peer_dict["dest_link"].items(): nh_details = topo[name] if "vrfs" in topo[router] or type(nh_details["bgp"]) is list: @@ -770,8 +769,8 @@ def __create_bgp_unicast_address_family( bgp_data = input_dict["address_family"] neigh_data = bgp_data[addr_type]["unicast"]["neighbor"] - for peer_name, peer_dict in deepcopy(neigh_data).iteritems(): - for dest_link, peer in peer_dict["dest_link"].iteritems(): + for peer_name, peer_dict in deepcopy(neigh_data).items(): + for dest_link, peer in peer_dict["dest_link"].items(): deactivate = None activate = None nh_details = topo[peer_name] @@ -779,7 +778,7 @@ def __create_bgp_unicast_address_family( deactivate_addr_family = peer.setdefault("deactivate", None) # Loopback interface if "source_link" in peer and peer["source_link"] == "lo": - for destRouterLink, data in sorted(nh_details["links"].iteritems()): + for destRouterLink, data in sorted(nh_details["links"].items()): if "type" in data and data["type"] == "loopback": if dest_link == destRouterLink: ip_addr = nh_details["links"][destRouterLink][ @@ -961,7 +960,7 @@ def modify_bgp_config_when_bgpd_down(tgen, topo, input_dict): # Copy bgp config file to /etc/frr for dut in input_dict.keys(): router_list = tgen.routers() - for router, rnode in router_list.iteritems(): + for router, rnode in router_list.items(): if router != dut: continue @@ -1083,7 +1082,7 @@ def verify_bgp_convergence(tgen, topo, dut=None): """ logger.debug("Entering lib API: verify_bgp_convergence()") - for router, rnode in tgen.routers().iteritems(): + for router, rnode in tgen.routers().items(): if "bgp" not in topo["routers"][router]: continue @@ -1462,9 +1461,9 @@ def verify_as_numbers(tgen, topo, input_dict): bgp_neighbors = bgp_addr_type[addr_type]["unicast"]["neighbor"] - for bgp_neighbor, peer_data in bgp_neighbors.iteritems(): + for bgp_neighbor, peer_data in bgp_neighbors.items(): remote_as = input_dict[bgp_neighbor]["bgp"]["local_as"] - for dest_link, peer_dict in peer_data["dest_link"].iteritems(): + for dest_link, peer_dict in peer_data["dest_link"].items(): neighbor_ip = None data = topo["routers"][bgp_neighbor]["links"] @@ -1534,7 +1533,7 @@ def verify_bgp_convergence_from_running_config(tgen, dut=None): logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) - for router, rnode in tgen.routers().iteritems(): + for router, rnode in tgen.routers().items(): if dut is not None and dut != router: continue @@ -1686,8 +1685,8 @@ def clear_bgp_and_verify(tgen, topo, router): for addr_type in bgp_addr_type: bgp_neighbors = bgp_addr_type[addr_type]["unicast"]["neighbor"] - for bgp_neighbor, peer_data in bgp_neighbors.iteritems(): - for dest_link, peer_dict in peer_data["dest_link"].iteritems(): + 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 dest_link in data: @@ -1768,8 +1767,8 @@ def clear_bgp_and_verify(tgen, topo, router): for addr_type in bgp_addr_type: bgp_neighbors = bgp_addr_type[addr_type]["unicast"]["neighbor"] - for bgp_neighbor, peer_data in bgp_neighbors.iteritems(): - for dest_link, peer_dict in peer_data["dest_link"].iteritems(): + 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 dest_link in data: @@ -1873,8 +1872,8 @@ def verify_bgp_timers_and_functionality(tgen, topo, input_dict): continue bgp_neighbors = bgp_addr_type[addr_type]["unicast"]["neighbor"] - for bgp_neighbor, peer_data in bgp_neighbors.iteritems(): - for dest_link, peer_dict in peer_data["dest_link"].iteritems(): + 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"] keepalivetimer = peer_dict["keepalivetimer"] @@ -2116,7 +2115,7 @@ def verify_bgp_attributes( """ logger.debug("Entering lib API: verify_bgp_attributes()") - for router, rnode in tgen.routers().iteritems(): + for router, rnode in tgen.routers().items(): if router != dut: continue @@ -2198,7 +2197,7 @@ def verify_bgp_attributes( return True -@retry(attempts=4, wait=2, return_is_str=True, initial_wait=2) +@retry(attempts=5, wait=2, return_is_str=True) def verify_best_path_as_per_bgp_attribute( tgen, addr_type, router, input_dict, attribute ): @@ -2330,7 +2329,7 @@ def verify_best_path_as_per_bgp_attribute( # - rule is IGP>EGP>INCOMPLETE _next_hop = [ key - for (key, value) in attribute_dict.iteritems() + for (key, value) in attribute_dict.items() if value == "IGP" ][0] compare = "" @@ -2513,7 +2512,7 @@ def verify_best_path_as_per_admin_distance( return True -@retry(attempts=5, wait=2, return_is_str=True, initial_wait=2) +@retry(attempts=6, wait=2, return_is_str=True) def verify_bgp_rib(tgen, addr_type, dut, input_dict, next_hop=None, aspath=None): """ This API is to verify whether bgp rib has any @@ -2550,7 +2549,7 @@ def verify_bgp_rib(tgen, addr_type, dut, input_dict, next_hop=None, aspath=None) list1 = [] list2 = [] for routerInput in input_dict.keys(): - for router, rnode in router_list.iteritems(): + for router, rnode in router_list.items(): if router != dut: continue @@ -2773,7 +2772,7 @@ def verify_bgp_rib(tgen, addr_type, dut, input_dict, next_hop=None, aspath=None) return True -@retry(attempts=4, wait=2, return_is_str=True, initial_wait=2) +@retry(attempts=5, wait=2, return_is_str=True) def verify_graceful_restart(tgen, topo, addr_type, input_dict, dut, peer): """ This API is to verify verify_graceful_restart configuration of DUT and @@ -2834,7 +2833,7 @@ def verify_graceful_restart(tgen, topo, addr_type, input_dict, dut, peer): logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) - for router, rnode in tgen.routers().iteritems(): + for router, rnode in tgen.routers().items(): if router != dut: continue @@ -3022,7 +3021,7 @@ def verify_graceful_restart(tgen, topo, addr_type, input_dict, dut, peer): return True -@retry(attempts=4, wait=2, return_is_str=True, initial_wait=2) +@retry(attempts=5, wait=2, return_is_str=True) def verify_r_bit(tgen, topo, addr_type, input_dict, dut, peer): """ This API is to verify r_bit in the BGP gr capability advertised @@ -3082,7 +3081,7 @@ def verify_r_bit(tgen, topo, addr_type, input_dict, dut, peer): logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) - for router, rnode in tgen.routers().iteritems(): + for router, rnode in tgen.routers().items(): if router != dut: continue @@ -3140,7 +3139,7 @@ def verify_r_bit(tgen, topo, addr_type, input_dict, dut, peer): return True -@retry(attempts=4, wait=2, return_is_str=True, initial_wait=2) +@retry(attempts=5, wait=2, return_is_str=True) def verify_eor(tgen, topo, addr_type, input_dict, dut, peer): """ This API is to verify EOR @@ -3200,7 +3199,7 @@ def verify_eor(tgen, topo, addr_type, input_dict, dut, peer): """ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) - for router, rnode in tgen.routers().iteritems(): + for router, rnode in tgen.routers().items(): if router != dut: continue @@ -3303,7 +3302,7 @@ def verify_eor(tgen, topo, addr_type, input_dict, dut, peer): return True -@retry(attempts=4, wait=2, return_is_str=True, initial_wait=2) +@retry(attempts=5, wait=2, return_is_str=True) def verify_f_bit(tgen, topo, addr_type, input_dict, dut, peer): """ This API is to verify f_bit in the BGP gr capability advertised @@ -3365,7 +3364,7 @@ def verify_f_bit(tgen, topo, addr_type, input_dict, dut, peer): logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) - for router, rnode in tgen.routers().iteritems(): + for router, rnode in tgen.routers().items(): if router != dut: continue @@ -3443,7 +3442,7 @@ def verify_f_bit(tgen, topo, addr_type, input_dict, dut, peer): return True -@retry(attempts=4, wait=2, return_is_str=True, initial_wait=2) +@retry(attempts=5, wait=2, return_is_str=True) def verify_graceful_restart_timers(tgen, topo, addr_type, input_dict, dut, peer): """ This API is to verify graceful restart timers, configured and recieved @@ -3491,7 +3490,7 @@ def verify_graceful_restart_timers(tgen, topo, addr_type, input_dict, dut, peer) logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) - for router, rnode in tgen.routers().iteritems(): + for router, rnode in tgen.routers().items(): if router != dut: continue @@ -3569,7 +3568,7 @@ def verify_graceful_restart_timers(tgen, topo, addr_type, input_dict, dut, peer) return True -@retry(attempts=4, wait=2, return_is_str=True, initial_wait=2) +@retry(attempts=5, wait=2, return_is_str=True) def verify_gr_address_family(tgen, topo, addr_type, addr_family, dut): """ This API is to verify gr_address_family in the BGP gr capability advertised @@ -3595,7 +3594,7 @@ def verify_gr_address_family(tgen, topo, addr_type, addr_family, dut): logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) - for router, rnode in tgen.routers().iteritems(): + for router, rnode in tgen.routers().items(): if router != dut: continue @@ -3659,7 +3658,7 @@ def verify_gr_address_family(tgen, topo, addr_type, addr_family, dut): logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) -@retry(attempts=5, wait=2, return_is_str=True, initial_wait=2) +@retry(attempts=6, wait=2, return_is_str=True) def verify_attributes_for_evpn_routes( tgen, topo, @@ -4060,7 +4059,7 @@ def verify_attributes_for_evpn_routes( return False -@retry(attempts=5, wait=2, return_is_str=True, initial_wait=2) +@retry(attempts=6, wait=2, return_is_str=True) def verify_evpn_routes( tgen, topo, dut, input_dict, routeType=5, EthTag=0, next_hop=None ): diff --git a/tests/topotests/lib/common_config.py b/tests/topotests/lib/common_config.py index 1846d43138..6dd8d646f3 100644 --- a/tests/topotests/lib/common_config.py +++ b/tests/topotests/lib/common_config.py @@ -36,6 +36,8 @@ import ConfigParser import traceback import socket import ipaddress +import platform + if sys.version_info[0] > 2: import io @@ -46,7 +48,7 @@ else: from lib.topolog import logger, logger_config from lib.topogen import TopoRouter, get_topogen -from lib.topotest import interface_set_status +from lib.topotest import interface_set_status, version_cmp FRRCFG_FILE = "frr_json.conf" FRRCFG_BKUP_FILE = "frr_json_initial.conf" @@ -256,6 +258,7 @@ def create_common_configuration( "route_maps": "! Route Maps Config\n", "bgp": "! BGP Config\n", "vrf": "! VRF Config\n", + "ospf": "! OSPF Config\n", } ) @@ -352,7 +355,7 @@ def kill_mininet_routers_process(tgen): """ router_list = tgen.routers() - for rname, router in router_list.iteritems(): + for rname, router in router_list.items(): daemon_list = [ "zebra", "ospfd", @@ -379,7 +382,7 @@ def check_router_status(tgen): try: router_list = tgen.routers() - for router, rnode in router_list.iteritems(): + for router, rnode in router_list.items(): result = rnode.check_router_running() if result != "": @@ -609,7 +612,7 @@ def get_frr_ipv6_linklocal(tgen, router, intf=None, vrf=None): """ router_list = tgen.routers() - for rname, rnode in router_list.iteritems(): + for rname, rnode in router_list.items(): if rname != router: continue @@ -667,7 +670,7 @@ def generate_support_bundle(): test_name = sys._getframe(2).f_code.co_name TMPDIR = os.path.join(LOGDIR, tgen.modname) - for rname, rnode in router_list.iteritems(): + for rname, rnode in router_list.items(): logger.info("Generating support bundle for {}".format(rname)) rnode.run("mkdir -p /var/log/frr") bundle_log = rnode.run("python2 /usr/lib/frr/generate_support_bundle.py") @@ -682,7 +685,7 @@ def generate_support_bundle(): return True -def start_topology(tgen): +def start_topology(tgen, daemon=None): """ Starting topology, create tmp files which are loaded to routers to start deamons and then start routers @@ -734,9 +737,16 @@ def start_topology(tgen): router.load_config( TopoRouter.RD_ZEBRA, "{}/{}/zebra.conf".format(TMPDIR, rname) ) + # Loading empty bgpd.conf file to router, to start the bgp deamon router.load_config(TopoRouter.RD_BGP, "{}/{}/bgpd.conf".format(TMPDIR, rname)) + if daemon and 'ospfd' in daemon: + # Loading empty ospf.conf file to router, to start the bgp deamon + router.load_config( + TopoRouter.RD_OSPF, + '{}/{}/ospfd.conf'.format(TMPDIR, rname) + ) # Starting routers logger.info("Starting all routers once topology is created") tgen.start_router() @@ -809,6 +819,24 @@ def number_to_column(routerName): return ord(routerName[0]) - 97 +def topo_daemons(tgen, topo): + """ + Returns daemon list required for the suite based on topojson. + """ + daemon_list = [] + + router_list = tgen.routers() + ROUTER_LIST = sorted( + router_list.keys(), key=lambda x: int(re_search("\d+", x).group(0)) + ) + + for rtr in ROUTER_LIST: + if 'ospf' in topo['routers'][rtr] and 'ospfd' not in daemon_list: + daemon_list.append('ospfd') + + return daemon_list + + ############################################# # Common APIs, will be used by all protocols ############################################# @@ -876,7 +904,7 @@ def create_vrf_cfg(tgen, topo, input_dict=None, build=False): input_dict = deepcopy(input_dict) try: - for c_router, c_data in input_dict.iteritems(): + for c_router, c_data in input_dict.items(): rnode = tgen.routers()[c_router] if "vrfs" in c_data: for vrf in c_data["vrfs"]: @@ -921,7 +949,7 @@ def create_vrf_cfg(tgen, topo, input_dict=None, build=False): if "links" in c_data: for destRouterLink, data in sorted( - c_data["links"].iteritems() + c_data["links"].items() ): # Loopback interfaces if "type" in data and data["type"] == "loopback": @@ -1156,7 +1184,7 @@ def find_interface_with_greater_ip(topo, router, loopback=True, interface=True): lo_list = [] interfaces_list = [] lo_exists = False - for destRouterLink, data in sorted(link_data.iteritems()): + for destRouterLink, data in sorted(link_data.items()): if loopback: if "type" in data and data["type"] == "loopback": lo_exists = True @@ -1350,9 +1378,9 @@ def create_interfaces_cfg(tgen, topo, build=False): topo = deepcopy(topo) try: - for c_router, c_data in topo.iteritems(): + for c_router, c_data in topo.items(): interface_data = [] - for destRouterLink, data in sorted(c_data["links"].iteritems()): + for destRouterLink, data in sorted(c_data["links"].items()): # Loopback interfaces if "type" in data and data["type"] == "loopback": interface_name = destRouterLink @@ -1392,6 +1420,58 @@ def create_interfaces_cfg(tgen, topo, build=False): else: interface_data.append("ipv6 address {}\n".format(intf_addr)) + if 'ospf' in data: + ospf_data = data['ospf'] + if 'area' in ospf_data: + intf_ospf_area = c_data["links"][destRouterLink][ + "ospf"]["area"] + if "delete" in data and data["delete"]: + interface_data.append("no ip ospf area") + else: + interface_data.append("ip ospf area {}".format( + intf_ospf_area + )) + + if "hello_interval" in ospf_data: + intf_ospf_hello = c_data["links"][destRouterLink][ + "ospf"]["hello_interval"] + if "delete" in data and data["delete"]: + interface_data.append("no ip ospf "\ + " hello-interval") + else: + interface_data.append("ip ospf "\ + " hello-interval {}".format(intf_ospf_hello)) + + if "dead_interval" in ospf_data: + intf_ospf_dead = c_data["links"][destRouterLink][ + "ospf"]["dead_interval"] + if "delete" in data and data["delete"]: + interface_data.append("no ip ospf"\ + " dead-interval") + else: + interface_data.append("ip ospf "\ + " dead-interval {}".format(intf_ospf_dead)) + + if "network" in ospf_data: + intf_ospf_nw = c_data["links"][destRouterLink][ + "ospf"]["network"] + if "delete" in data and data["delete"]: + interface_data.append("no ip ospf"\ + " network {}".format(intf_ospf_nw)) + else: + interface_data.append("ip ospf"\ + " network {}".format(intf_ospf_nw)) + + if "priority" in ospf_data: + intf_ospf_nw = c_data["links"][destRouterLink][ + "ospf"]["priority"] + + if "delete" in data and data["delete"]: + interface_data.append("no ip ospf"\ + " priority") + else: + interface_data.append("ip ospf"\ + " priority {}".format(intf_ospf_nw)) result = create_common_configuration( tgen, c_router, interface_data, "interface_config", build=build ) @@ -1574,11 +1654,11 @@ def create_prefix_lists(tgen, input_dict, build=False): config_data = [] prefix_lists = input_dict[router]["prefix_lists"] - for addr_type, prefix_data in prefix_lists.iteritems(): + for addr_type, prefix_data in prefix_lists.items(): if not check_address_types(addr_type): continue - for prefix_name, prefix_list in prefix_data.iteritems(): + for prefix_name, prefix_list in prefix_data.items(): for prefix_dict in prefix_list: if "action" not in prefix_dict or "network" not in prefix_dict: errormsg = "'action' or network' missing in" " input_dict" @@ -1715,7 +1795,7 @@ def create_route_maps(tgen, input_dict, build=False): logger.debug("route_maps not present in input_dict") continue rmap_data = [] - for rmap_name, rmap_value in input_dict[router]["route_maps"].iteritems(): + for rmap_name, rmap_value in input_dict[router]["route_maps"].items(): for rmap_dict in rmap_value: del_action = rmap_dict.setdefault("delete", False) @@ -2462,7 +2542,7 @@ def configure_interface_mac(tgen, input_dict): ############################################# # Verification APIs ############################################# -@retry(attempts=5, wait=2, return_is_str=True, initial_wait=2) +@retry(attempts=6, wait=2, return_is_str=True) def verify_rib( tgen, addr_type, @@ -2527,7 +2607,7 @@ def verify_rib( additional_nexthops_in_required_nhs = [] found_hops = [] for routerInput in input_dict.keys(): - for router, rnode in router_list.iteritems(): + for router, rnode in router_list.items(): if router != dut: continue @@ -2880,7 +2960,7 @@ def verify_fib_routes(tgen, addr_type, dut, input_dict, next_hop=None): router_list = tgen.routers() for routerInput in input_dict.keys(): - for router, rnode in router_list.iteritems(): + for router, rnode in router_list.items(): if router != dut: continue @@ -3136,7 +3216,7 @@ def verify_fib_routes(tgen, addr_type, dut, input_dict, next_hop=None): router_list = tgen.routers() for routerInput in input_dict.keys(): - for router, rnode in router_list.iteritems(): + for router, rnode in router_list.items(): if router != dut: continue @@ -3495,7 +3575,7 @@ def verify_prefix_lists(tgen, input_dict): return True -@retry(attempts=2, wait=4, return_is_str=True, initial_wait=2) +@retry(attempts=3, wait=4, return_is_str=True) def verify_route_maps(tgen, input_dict): """ Running "show route-map" command and verifying given route-map @@ -3746,7 +3826,7 @@ def verify_cli_json(tgen, input_dict): return True -@retry(attempts=2, wait=4, return_is_str=True, initial_wait=2) +@retry(attempts=3, wait=4, return_is_str=True) def verify_evpn_vni(tgen, input_dict): """ API to verify evpn vni details using "show evpn vni detail json" @@ -3864,7 +3944,7 @@ def verify_evpn_vni(tgen, input_dict): return False -@retry(attempts=2, wait=4, return_is_str=True, initial_wait=2) +@retry(attempts=3, wait=4, return_is_str=True) def verify_vrf_vni(tgen, input_dict): """ API to verify vrf vni details using "show vrf vni json" @@ -3973,3 +4053,30 @@ def verify_vrf_vni(tgen, input_dict): logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) return False + + +def required_linux_kernel_version(required_version): + """ + This API is used to check linux version compatibility of the test suite. + If version mentioned in required_version is higher than the linux kernel + of the system, test suite will be skipped. This API returns true or errormsg. + + Parameters + ---------- + * `required_version` : Kernel version required for the suites to run. + + Usage + ----- + result = linux_kernel_version_lowerthan('4.15') + + Returns + ------- + errormsg(str) or True + """ + system_kernel = platform.release() + if version_cmp(system_kernel, required_version) < 0: + error_msg = ('These tests will not run on kernel "{}", ' + 'they require kernel >= {})'.format(system_kernel, + required_version )) + return error_msg + return True diff --git a/tests/topotests/lib/ltemplate.py b/tests/topotests/lib/ltemplate.py index a76d8e4b08..192c121008 100644 --- a/tests/topotests/lib/ltemplate.py +++ b/tests/topotests/lib/ltemplate.py @@ -82,7 +82,7 @@ class LTemplate(): router_list = tgen.routers() # For all registred routers, load the zebra configuration file - for rname, router in router_list.iteritems(): + for rname, router in router_list.items(): logger.info("Setting up %s" % rname) for rd_val in TopoRouter.RD: config = os.path.join(self.testdir, '{}/{}.conf'.format(rname,TopoRouter.RD[rd_val])) diff --git a/tests/topotests/lib/ospf.py b/tests/topotests/lib/ospf.py new file mode 100644 index 0000000000..a2351bf747 --- /dev/null +++ b/tests/topotests/lib/ospf.py @@ -0,0 +1,1182 @@ +# +# Copyright (c) 2020 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. +# + +from copy import deepcopy +import traceback +from time import sleep +from lib.topolog import logger +import ipaddr + + +# Import common_config to use commomnly used APIs +from lib.common_config import (create_common_configuration, + InvalidCLIError, retry, + generate_ips, + check_address_types, + validate_ip_address, + run_frr_cmd) + +LOGDIR = "/tmp/topotests/" +TMPDIR = None + +################################ +# Configure procs +################################ + +def create_router_ospf( + tgen, topo, input_dict=None, build=False, + load_config=True): + """ + API to configure ospf on router. + + Parameters + ---------- + * `tgen` : Topogen object + * `topo` : json file data + * `input_dict` : Input dict data, required when configuring from testcase + * `build` : Only for initial setup phase this is set as True. + * `load_config` : Loading the config to router this is set as True. + + Usage + ----- + input_dict = { + "r1": { + "ospf": { + "router_id": "22.22.22.22", + "area": [{ "id":0.0.0.0, "type": "nssa"}] + } + } + + result = create_router_ospf(tgen, topo, input_dict) + + Returns + ------- + True or False + """ + logger.debug("Entering lib API: create_router_ospf()") + result = False + + if not input_dict: + input_dict = deepcopy(topo) + else: + 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 + + result = __create_ospf_global( + tgen, input_dict, router, build, load_config) + if result is True: + ospf_data = input_dict[router]["ospf"] + + + logger.debug("Exiting lib API: create_router_ospf()") + return result + + +def __create_ospf_global( + tgen, input_dict, router, build=False, + load_config=True): + """ + Helper API to create ospf global configuration. + + Parameters + ---------- + * `tgen` : Topogen object + * `input_dict` : Input dict data, required when configuring from testcase + * `router` : router to be configured. + * `build` : Only for initial setup phase this is set as True. + * `load_config` : Loading the config to router this is set as True. + + Returns + ------- + True or False + """ + + result = False + logger.debug("Entering lib API: __create_ospf_global()") + try: + + ospf_data = input_dict[router]["ospf"] + del_ospf_action = ospf_data.setdefault("delete", False) + if del_ospf_action: + config_data = ["no router ospf"] + result = create_common_configuration(tgen, router, config_data, + "ospf", build, + load_config) + return result + + config_data = [] + cmd = "router 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 ospf router-id") + if router_id: + config_data.append("ospf router-id {}".format( + router_id)) + + # 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) + result = create_common_configuration(tgen, router, config_data, + "ospf", build, load_config) + + # 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"]) + + _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) + + del_action = summary.setdefault("delete", False) + if del_action: + cmd = "no {}".format(cmd) + config_data.append(cmd) + result = create_common_configuration(tgen, router, config_data, + "ospf", build, load_config) + + except InvalidCLIError: + # Traceback + errormsg = traceback.format_exc() + logger.error(errormsg) + return errormsg + + logger.debug("Exiting lib API: create_ospf_global()") + return result + + +def create_router_ospf6( + tgen, topo, input_dict=None, build=False, + load_config=True): + """ + API to configure ospf on router + + Parameters + ---------- + * `tgen` : Topogen object + * `topo` : json file data + * `input_dict` : Input dict data, required when configuring from testcase + * `build` : Only for initial setup phase this is set as True. + + Usage + ----- + input_dict = { + "r1": { + "ospf6": { + "router_id": "22.22.22.22", + } + } + + Returns + ------- + True or False + """ + logger.debug("Entering lib API: create_router_ospf()") + result = False + + if not input_dict: + input_dict = deepcopy(topo) + else: + 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 + + result = __create_ospf_global( + tgen, input_dict, router, build, load_config) + + logger.debug("Exiting lib API: create_router_ospf()") + return result + + +def __create_ospf6_global( + tgen, input_dict, router, build=False, + load_config=True): + """ + Helper API to create ospf global configuration. + + Parameters + ---------- + * `tgen` : Topogen object + * `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. + + Returns + ------- + True or False + """ + + result = False + logger.debug("Entering lib API: __create_ospf_global()") + try: + + ospf_data = input_dict[router]["ospf6"] + del_ospf_action = ospf_data.setdefault("delete", False) + if del_ospf_action: + config_data = ["no ipv6 router ospf"] + result = create_common_configuration(tgen, router, config_data, + "ospf", build, + load_config) + return result + + config_data = [] + cmd = "router ospf" + + config_data.append(cmd) + + 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 ospf router-id") + if router_id: + config_data.append("ospf router-id {}".format( + router_id)) + + result = create_common_configuration(tgen, router, config_data, + "ospf", build, load_config) + except InvalidCLIError: + # Traceback + errormsg = traceback.format_exc() + logger.error(errormsg) + return errormsg + + logger.debug("Exiting lib API: create_ospf_global()") + return result + +def config_ospf_interface (tgen, topo, input_dict=None, build=False, + load_config=True): + """ + API to configure ospf on router. + + Parameters + ---------- + * `tgen` : Topogen object + * `topo` : json file data + * `input_dict` : Input dict data, required when configuring from testcase + * `build` : Only for initial setup phase this is set as True. + * `load_config` : Loading the config to router this is set as True. + + Usage + ----- + r1_ospf_auth = { + "r1": { + "links": { + "r2": { + "ospf": { + "authentication": 'message-digest', + "authentication-key": "ospf", + "message-digest-key": "10" + } + } + } + } + } + result = config_ospf_interface(tgen, topo, r1_ospf_auth) + + Returns + ------- + True or False + """ + logger.debug("Enter lib config_ospf_interface") + if not input_dict: + input_dict = deepcopy(topo) + else: + input_dict = deepcopy(input_dict) + for router in input_dict.keys(): + config_data = [] + for lnk in input_dict[router]['links'].keys(): + if "ospf" not in input_dict[router]['links'][lnk]: + logger.debug("Router %s: ospf configs is not present in" + "input_dict, passed input_dict", router, + input_dict) + continue + ospf_data = input_dict[router]['links'][lnk]['ospf'] + data_ospf_area = ospf_data.setdefault("area", None) + data_ospf_auth = ospf_data.setdefault("authentication", None) + data_ospf_dr_priority = ospf_data.setdefault("priority", None) + data_ospf_cost = ospf_data.setdefault("cost", None) + + try: + intf = topo['routers'][router]['links'][lnk]['interface'] + except KeyError: + intf = topo['switches'][router]['links'][lnk]['interface'] + + # interface + cmd = "interface {}".format(intf) + + config_data.append(cmd) + # interface area config + if data_ospf_area: + cmd = "ip ospf area {}".format(data_ospf_area) + config_data.append(cmd) + # interface ospf auth + if data_ospf_auth: + if data_ospf_auth == 'null': + cmd = "ip ospf authentication null" + elif data_ospf_auth == 'message-digest': + cmd = "ip ospf authentication message-digest" + else: + cmd = "ip ospf authentication" + + if 'del_action' in ospf_data: + cmd = "no {}".format(cmd) + config_data.append(cmd) + + if "message-digest-key" in ospf_data: + cmd = "ip ospf message-digest-key {} md5 {}".format( + ospf_data["message-digest-key"],ospf_data[ + "authentication-key"]) + if 'del_action' in ospf_data: + cmd = "no {}".format(cmd) + config_data.append(cmd) + + if "authentication-key" in ospf_data and \ + "message-digest-key" not in ospf_data: + cmd = "ip ospf authentication-key {}".format(ospf_data[ + "authentication-key"]) + if 'del_action' in ospf_data: + cmd = "no {}".format(cmd) + config_data.append(cmd) + + # interface ospf dr priority + if data_ospf_dr_priority in ospf_data: + cmd = "ip ospf priority {}".format( + ospf_data["priority"]) + if 'del_action' in ospf_data: + cmd = "no {}".format(cmd) + config_data.append(cmd) + + # interface ospf cost + if data_ospf_cost in ospf_data: + cmd = "ip ospf cost {}".format( + ospf_data["cost"]) + if 'del_action' in ospf_data: + cmd = "no {}".format(cmd) + config_data.append(cmd) + + if build: + return config_data + else: + result = create_common_configuration(tgen, router, config_data, + "interface_config", + build=build) + logger.debug("Exiting lib API: create_igmp_config()") + return result + +def clear_ospf(tgen, router): + """ + This API is to clear ospf neighborship by running + clear ip ospf interface * command, + + Parameters + ---------- + * `tgen`: topogen object + * `router`: device under test + + Usage + ----- + clear_ospf(tgen, "r1") + """ + + logger.debug("Entering lib API: clear_ospf()") + if router not in tgen.routers(): + return False + + rnode = tgen.routers()[router] + + # Clearing OSPF + logger.info("Clearing ospf process for router %s..", router) + + run_frr_cmd(rnode, "clear ip ospf interface ") + + logger.debug("Exiting lib API: clear_ospf()") + + +################################ +# Verification procs +################################ +@retry(attempts=40, wait=2, return_is_str=True) +def verify_ospf_neighbor(tgen, topo, dut=None, input_dict=None, lan=False): + """ + This API is to verify ospf neighborship by running + show ip ospf neighbour command, + + Parameters + ---------- + * `tgen` : Topogen object + * `topo` : json file data + * `dut`: device under test + * `input_dict` : Input dict data, required when configuring from testcase + * `lan` : verify neighbors in lan topology + + Usage + ----- + 1. To check FULL neighbors. + verify_ospf_neighbor(tgen, topo, dut=dut) + + 2. To check neighbors with their roles. + input_dict = { + "r0": { + "ospf": { + "neighbors": { + "r1": { + "state": "Full", + "role": "DR" + }, + "r2": { + "state": "Full", + "role": "DROther" + }, + "r3": { + "state": "Full", + "role": "DROther" + } + } + } + } + } + result = verify_ospf_neighbor(tgen, topo, dut, input_dict, lan=True) + + Returns + ------- + True or False (Error Message) + """ + logger.debug("Entering lib API: verify_ospf_neighbor()") + result = False + if input_dict: + for router, rnode in tgen.routers().iteritems(): + if 'ospf' not in topo['routers'][router]: + continue + + if dut is not None and dut != router: + continue + + logger.info("Verifying OSPF neighborship on router %s:", router) + show_ospf_json = run_frr_cmd(rnode, + "show ip ospf neighbor all json", isjson=True) + + # Verifying output dictionary show_ospf_json is empty or not + if not bool(show_ospf_json): + errormsg = "OSPF is not running" + return errormsg + + ospf_data_list = input_dict[router]["ospf"] + ospf_nbr_list = ospf_data_list['neighbors'] + + for ospf_nbr, nbr_data in ospf_nbr_list.items(): + data_ip = topo['routers'][ospf_nbr]['links'] + data_rid = topo['routers'][ospf_nbr]['ospf']['router_id'] + if ospf_nbr in data_ip: + nbr_details = nbr_data[ospf_nbr] + elif lan: + for switch in topo['switches']: + if 'ospf' in topo['switches'][switch]['links'][router]: + neighbor_ip = data_ip[switch]['ipv4'].split("/")[0] + else: + continue + else: + neighbor_ip = data_ip[router]['ipv4'].split("/")[0] + + nh_state = None + neighbor_ip = neighbor_ip.lower() + nbr_rid = data_rid + try: + nh_state = show_ospf_json[nbr_rid][0][ + 'state'].split('/')[0] + intf_state = show_ospf_json[nbr_rid][0][ + 'state'].split('/')[1] + except KeyError: + errormsg = "[DUT: {}] OSPF peer {} missing".format(router, + nbr_rid) + return errormsg + + nbr_state = nbr_data.setdefault("state",None) + nbr_role = nbr_data.setdefault("role",None) + + if nbr_state: + if nbr_state == nh_state: + logger.info("[DUT: {}] OSPF Nbr is {}:{} State {}".format + (router, ospf_nbr, nbr_rid, nh_state)) + result = True + else: + errormsg = ("[DUT: {}] OSPF is not Converged, neighbor" + " state is {}".format(router, nh_state)) + return errormsg + if nbr_role: + if nbr_role == intf_state: + logger.info("[DUT: {}] OSPF Nbr is {}: {} Role {}".format( + router, ospf_nbr, nbr_rid, nbr_role)) + else: + errormsg = ("[DUT: {}] OSPF is not Converged with rid" + "{}, role is {}".format(router, nbr_rid, intf_state)) + return errormsg + continue + else: + for router, rnode in tgen.routers().iteritems(): + if 'ospf' not in topo['routers'][router]: + continue + + if dut is not None and dut != router: + continue + + logger.info("Verifying OSPF neighborship on router %s:", router) + show_ospf_json = run_frr_cmd(rnode, + "show ip ospf neighbor all json", isjson=True) + # Verifying output dictionary show_ospf_json is empty or not + if not bool(show_ospf_json): + errormsg = "OSPF is not running" + return errormsg + + ospf_data_list = topo["routers"][router]["ospf"] + ospf_neighbors = ospf_data_list['neighbors'] + total_peer = 0 + total_peer = len(ospf_neighbors.keys()) + no_of_ospf_nbr = 0 + ospf_nbr_list = ospf_data_list['neighbors'] + no_of_peer = 0 + for ospf_nbr, nbr_data in ospf_nbr_list.items(): + if nbr_data: + data_ip = topo['routers'][nbr_data["nbr"]]['links'] + data_rid = topo['routers'][nbr_data["nbr"]][ + 'ospf']['router_id'] + else: + data_ip = topo['routers'][ospf_nbr]['links'] + data_rid = topo['routers'][ospf_nbr]['ospf']['router_id'] + if ospf_nbr in data_ip: + nbr_details = nbr_data[ospf_nbr] + elif lan: + for switch in topo['switches']: + if 'ospf' in topo['switches'][switch]['links'][router]: + neighbor_ip = data_ip[switch]['ipv4'].split("/")[0] + else: + continue + else: + neighbor_ip = data_ip[router]['ipv4'].split("/")[0] + + nh_state = None + neighbor_ip = neighbor_ip.lower() + nbr_rid = data_rid + try: + nh_state = show_ospf_json[nbr_rid][0][ + 'state'].split('/')[0] + except KeyError: + errormsg = "[DUT: {}] OSPF peer {} missing,from "\ + "{} ".format(router, + nbr_rid, ospf_nbr) + return errormsg + + if nh_state == 'Full': + no_of_peer += 1 + + if no_of_peer == total_peer: + logger.info("[DUT: {}] OSPF is Converged".format(router)) + result = True + else: + errormsg = ("[DUT: {}] OSPF is not Converged".format(router)) + return errormsg + + logger.debug("Exiting API: verify_ospf_neighbor()") + return result + +@retry(attempts=21, wait=2, return_is_str=True) +def verify_ospf_rib(tgen, dut, input_dict, next_hop=None, + tag=None, metric=None, fib=None): + """ + This API is to verify ospf routes by running + show ip ospf route command. + + Parameters + ---------- + * `tgen` : Topogen object + * `dut`: device under test + * `input_dict` : Input dict data, required when configuring from testcase + * `next_hop` : next to be verified + * `tag` : tag to be verified + * `metric` : metric to be verified + * `fib` : True if the route is installed in FIB. + + Usage + ----- + input_dict = { + "r1": { + "static_routes": [ + { + "network": ip_net, + "no_of_ip": 1, + "routeType": "N" + } + ] + } + } + + result = verify_ospf_rib(tgen, dut, input_dict,next_hop=nh) + + Returns + ------- + True or False (Error Message) + """ + + logger.info("Entering lib API: verify_ospf_rib()") + result = False + router_list = tgen.routers() + additional_nexthops_in_required_nhs = [] + found_hops = [] + for routerInput in input_dict.keys(): + for router, rnode in router_list.iteritems(): + if router != dut: + continue + + logger.info("Checking router %s RIB:", router) + + # Verifying RIB routes + command = "show ip ospf route" + + found_routes = [] + missing_routes = [] + + if "static_routes" in input_dict[routerInput] or \ + "prefix" in input_dict[routerInput]: + if "prefix" in input_dict[routerInput]: + static_routes = input_dict[routerInput]["prefix"] + else: + static_routes = input_dict[routerInput]["static_routes"] + + + for static_route in static_routes: + cmd = "{}".format(command) + + cmd = "{} json".format(cmd) + + ospf_rib_json = run_frr_cmd(rnode, cmd, isjson=True) + + # Verifying output dictionary ospf_rib_json is not empty + if bool(ospf_rib_json) is False: + errormsg = "[DUT: {}] No routes found in OSPF route " \ + "table".format(router) + return errormsg + + network = static_route["network"] + no_of_ip = static_route.setdefault("no_of_ip", 1) + _tag = static_route.setdefault("tag", None) + _rtype = static_route.setdefault("routeType", None) + + + # Generating IPs for verification + ip_list = generate_ips(network, no_of_ip) + st_found = False + nh_found = False + + for st_rt in ip_list: + st_rt = str(ipaddr.IPNetwork(unicode(st_rt))) + + _addr_type = validate_ip_address(st_rt) + if _addr_type != 'ipv4': + continue + + if st_rt in ospf_rib_json: + st_found = True + found_routes.append(st_rt) + + if fib and next_hop: + if type(next_hop) is not list: + next_hop = [next_hop] + + for mnh in range(0, len(ospf_rib_json[st_rt])): + if 'fib' in ospf_rib_json[st_rt][ + mnh]["nexthops"][0]: + found_hops.append([rib_r[ + "ip"] for rib_r in ospf_rib_json[ + st_rt][mnh]["nexthops"]]) + + if found_hops[0]: + missing_list_of_nexthops = \ + set(found_hops[0]).difference(next_hop) + additional_nexthops_in_required_nhs = \ + set(next_hop).difference(found_hops[0]) + + if additional_nexthops_in_required_nhs: + logger.info( + "Nexthop " + "%s is not active for route %s in " + "RIB of router %s\n", + additional_nexthops_in_required_nhs, + st_rt, dut) + errormsg = ( + "Nexthop {} is not active" + " for route {} in RIB of router" + " {}\n".format( + additional_nexthops_in_required_nhs, + st_rt, dut)) + return errormsg + else: + nh_found = True + + elif next_hop and fib is None: + if type(next_hop) is not list: + next_hop = [next_hop] + found_hops = [rib_r["ip"] for rib_r in + ospf_rib_json[st_rt][ + "nexthops"]] + + if found_hops: + missing_list_of_nexthops = \ + set(found_hops).difference(next_hop) + additional_nexthops_in_required_nhs = \ + set(next_hop).difference(found_hops) + + if additional_nexthops_in_required_nhs: + logger.info( + "Missing nexthop %s for route"\ + " %s in RIB of router %s\n", \ + additional_nexthops_in_required_nhs, \ + st_rt, dut) + errormsg=("Nexthop {} is Missing for "\ + "route {} in RIB of router {}\n".format( + additional_nexthops_in_required_nhs, + st_rt, dut)) + return errormsg + else: + nh_found = True + if _rtype: + if "routeType" not in ospf_rib_json[ + st_rt]: + errormsg = ("[DUT: {}]: routeType missing" + "for route {} in OSPF RIB \n".\ + format(dut, st_rt)) + return errormsg + elif _rtype != ospf_rib_json[st_rt][ + "routeType"]: + errormsg = ("[DUT: {}]: routeType mismatch" + "for route {} in OSPF RIB \n".\ + format(dut, st_rt)) + return errormsg + else: + logger.info("DUT: {}]: Found routeType {}" + "for route {}".\ + format(dut, _rtype, st_rt)) + if tag: + if "tag" not in ospf_rib_json[ + st_rt]: + errormsg = ("[DUT: {}]: tag is not" + " present for" + " route {} in RIB \n".\ + format(dut, st_rt + )) + return errormsg + + if _tag != ospf_rib_json[ + st_rt]["tag"]: + errormsg = ("[DUT: {}]: tag value {}" + " is not matched for" + " route {} in RIB \n".\ + format(dut, _tag, st_rt, + )) + return errormsg + + if metric is not None: + if "type2cost" not in ospf_rib_json[ + st_rt]: + errormsg = ("[DUT: {}]: metric is" + " not present for" + " route {} in RIB \n".\ + format(dut, st_rt)) + return errormsg + + if metric != ospf_rib_json[ + st_rt]["type2cost"]: + errormsg = ("[DUT: {}]: metric value " + "{} is not matched for " + "route {} in RIB \n".\ + format(dut, metric, st_rt, + )) + return errormsg + + else: + missing_routes.append(st_rt) + + if nh_found: + logger.info("[DUT: {}]: Found next_hop {} for all OSPF" + " routes in RIB".format(router, next_hop)) + + if len(missing_routes) > 0: + errormsg = ("[DUT: {}]: Missing route in RIB, " + "routes: {}".\ + format(dut, missing_routes)) + return errormsg + + if found_routes: + logger.info("[DUT: %s]: Verified routes in RIB, found" + " routes are: %s\n", dut, found_routes) + result = True + + logger.info("Exiting lib API: verify_ospf_rib()") + return result + + +@retry(attempts=10, wait=2, return_is_str=True) +def verify_ospf_interface(tgen, topo, dut=None,lan=False, input_dict=None): + """ + This API is to verify ospf routes by running + show ip ospf interface command. + + Parameters + ---------- + * `tgen` : Topogen object + * `topo` : topology descriptions + * `dut`: device under test + * `lan`: if set to true this interface belongs to LAN. + * `input_dict` : Input dict data, required when configuring from testcase + + Usage + ----- + input_dict= { + 'r0': { + 'links':{ + 's1': { + 'ospf':{ + 'priority':98, + 'timerDeadSecs': 4, + 'area': '0.0.0.3', + 'mcastMemberOspfDesignatedRouters': True, + 'mcastMemberOspfAllRouters': True, + 'ospfEnabled': True, + + } + } + } + } + } + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) + + Returns + ------- + True or False (Error Message) + """ + + logger.debug("Entering lib API: verify_ospf_interface()") + result = False + for router, rnode in tgen.routers().iteritems(): + if 'ospf' not in topo['routers'][router]: + continue + + if dut is not None and dut != router: + continue + + logger.info("Verifying OSPF interface on router %s:", router) + show_ospf_json = run_frr_cmd(rnode, "show ip ospf interface json", + isjson=True) + + # Verifying output dictionary show_ospf_json is empty or not + if not bool(show_ospf_json): + errormsg = "OSPF is not running" + return errormsg + + # To find neighbor ip type + ospf_intf_data = input_dict[router]["links"] + for ospf_intf, intf_data in ospf_intf_data.items(): + intf = topo['routers'][router]['links'][ospf_intf]['interface'] + if intf in show_ospf_json['interfaces']: + for intf_attribute in intf_data['ospf']: + if intf_data['ospf'][intf_attribute] == show_ospf_json[ + 'interfaces'][intf][intf_attribute]: + logger.info("[DUT: %s] OSPF interface %s: %s is %s", + router, intf, intf_attribute, intf_data['ospf'][ + intf_attribute]) + else: + errormsg= "[DUT: {}] OSPF interface {}: {} is {}, \ + Expected is {}".format(router, intf, intf_attribute, + intf_data['ospf'][intf_attribute], show_ospf_json[ + 'interfaces'][intf][intf_attribute]) + return errormsg + result = True + logger.debug("Exiting API: verify_ospf_interface()") + return result + + +@retry(attempts=11, wait=2, return_is_str=True) +def verify_ospf_database(tgen, topo, dut, input_dict): + """ + This API is to verify ospf lsa's by running + show ip ospf database command. + + Parameters + ---------- + * `tgen` : Topogen object + * `dut`: device under test + * `input_dict` : Input dict data, required when configuring from testcase + * `topo` : next to be verified + + Usage + ----- + input_dict = { + "areas": { + "0.0.0.0": { + "Router Link States": { + "100.1.1.0-100.1.1.0": { + "LSID": "100.1.1.0", + "Advertised router": "100.1.1.0", + "LSA Age": 130, + "Sequence Number": "80000006", + "Checksum": "a703", + "Router links": 3 + } + }, + "Net Link States": { + "10.0.0.2-100.1.1.1": { + "LSID": "10.0.0.2", + "Advertised router": "100.1.1.1", + "LSA Age": 137, + "Sequence Number": "80000001", + "Checksum": "9583" + } + }, + }, + } + } + result = verify_ospf_database(tgen, topo, dut, input_dict) + + Returns + ------- + True or False (Error Message) + """ + + result = False + router = dut + logger.debug("Entering lib API: verify_ospf_database()") + + 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 interface on router %s:", dut) + show_ospf_json = run_frr_cmd(rnode, "show ip ospf database json", + isjson=True) + # Verifying output dictionary show_ospf_json is empty or not + if not bool(show_ospf_json): + errormsg = "OSPF is not running" + return errormsg + + # for inter and inter lsa's + ospf_db_data = input_dict.setdefault("areas", None) + ospf_external_lsa = input_dict.setdefault( + 'AS External Link States', None) + if ospf_db_data: + for ospf_area, area_lsa in ospf_db_data.items(): + if ospf_area in show_ospf_json['areas']: + if 'Router Link States' in area_lsa: + for lsa in area_lsa['Router Link States']: + if lsa in show_ospf_json['areas'][ospf_area][ + 'Router Link States']: + logger.info( + "[DUT: %s] OSPF LSDB area %s:Router " + "LSA %s", router, ospf_area, lsa) + result = True + else: + errormsg = \ + "[DUT: {}] OSPF LSDB area {}: expected" \ + " Router LSA is {}".format(router, ospf_area, lsa) + return errormsg + if 'Net Link States' in area_lsa: + for lsa in area_lsa['Net Link States']: + if lsa in show_ospf_json['areas'][ospf_area][ + 'Net Link States']: + logger.info( + "[DUT: %s] OSPF LSDB area %s:Network " + "LSA %s", router, ospf_area, lsa) + result = True + else: + errormsg = \ + "[DUT: {}] OSPF LSDB area {}: expected" \ + " Network LSA is {}".format(router, ospf_area, lsa) + return errormsg + if 'Summary Link States' in area_lsa: + for lsa in area_lsa['Summary Link States']: + if lsa in show_ospf_json['areas'][ospf_area][ + 'Summary Link States']: + logger.info( + "[DUT: %s] OSPF LSDB area %s:Summary " + "LSA %s", router, ospf_area, lsa) + result = True + else: + errormsg = \ + "[DUT: {}] OSPF LSDB area {}: expected" \ + " Summary LSA is {}".format(router, ospf_area, lsa) + return errormsg + if 'ASBR-Summary Link States' in area_lsa: + for lsa in area_lsa['ASBR-Summary Link States']: + if lsa in show_ospf_json['areas'][ospf_area][ + 'ASBR-Summary Link States']: + logger.info( + "[DUT: %s] OSPF LSDB area %s:ASBR Summary " + "LSA %s", router, ospf_area, lsa) + result = True + else: + errormsg = \ + "[DUT: {}] OSPF LSDB area {}: expected" \ + " ASBR Summary LSA is {}".format( + router, ospf_area, lsa) + return errormsg + if ospf_external_lsa: + for ospf_ext_lsa, ext_lsa_data in ospf_external_lsa.items(): + if ospf_ext_lsa in show_ospf_json['AS External Link States']: + logger.info( + "[DUT: %s] OSPF LSDB:External LSA %s", + router, ospf_ext_lsa) + result = True + else: + errormsg = \ + "[DUT: {}] OSPF LSDB : expected" \ + " External LSA is {}".format(router, ospf_ext_lsa) + return errormsg + + logger.debug("Exiting API: verify_ospf_database()") + return result + + + +@retry(attempts=10, wait=2, return_is_str=True) +def verify_ospf_summary(tgen, topo, dut, input_dict): + """ + This API is to verify ospf routes by running + show ip ospf interface command. + + Parameters + ---------- + * `tgen` : Topogen object + * `topo` : topology descriptions + * `dut`: device under test + * `input_dict` : Input dict data, required when configuring from testcase + + Usage + ----- + input_dict = { + "11.0.0.0/8": { + "Summary address": "11.0.0.0/8", + "Metric-type": "E2", + "Metric": 20, + "Tag": 0, + "External route count": 5 + } + } + result = verify_ospf_summary(tgen, topo, dut, input_dict) + + Returns + ------- + True or False (Error Message) + """ + + logger.debug("Entering lib API: verify_ospf_summary()") + result = False + router = dut + + logger.info("Verifying OSPF summary on router %s:", router) + + if 'ospf' not in topo['routers'][dut]: + errormsg = "[DUT: {}] OSPF is not configured on the router.".format( + router) + return errormsg + + rnode = tgen.routers()[dut] + show_ospf_json = run_frr_cmd(rnode, "show ip ospf summary detail json", + isjson=True) + + # Verifying output dictionary show_ospf_json is empty or not + if not bool(show_ospf_json): + errormsg = "OSPF is not running" + return errormsg + + # To find neighbor ip type + ospf_summary_data = input_dict + for ospf_summ, summ_data in ospf_summary_data.items(): + if ospf_summ not in show_ospf_json: + continue + summary = ospf_summary_data[ospf_summ]['Summary address'] + if summary in show_ospf_json: + for summ in summ_data: + if summ_data[summ] == show_ospf_json[summary][summ]: + logger.info("[DUT: %s] OSPF summary %s:%s is %s", + router, summary, summ, summ_data[summ]) + result = True + else: + errormsg = ("[DUT: {}] OSPF summary {}:{} is %s, " + "Expected is {}".format(router,summary, summ, + show_ospf_json[summary][summ])) + return errormsg + + logger.debug("Exiting API: verify_ospf_summary()") + return result diff --git a/tests/topotests/lib/topogen.py b/tests/topotests/lib/topogen.py index a6cc5280ec..b9f82877e2 100644 --- a/tests/topotests/lib/topogen.py +++ b/tests/topotests/lib/topogen.py @@ -254,7 +254,7 @@ class Topogen(object): ```py tgen = get_topogen() router_dict = tgen.get_gears(TopoRouter) - for router_name, router in router_dict.iteritems(): + for router_name, router in router_dict.items(): # Do stuff ``` * List iteration: @@ -267,7 +267,7 @@ class Topogen(object): """ return dict( (name, gear) - for name, gear in self.gears.iteritems() + for name, gear in self.gears.items() if isinstance(gear, geartype) ) @@ -316,7 +316,7 @@ class Topogen(object): """ if router is None: # pylint: disable=r1704 - for _, router in self.routers().iteritems(): + for _, router in self.routers().items(): router.start() else: if isinstance(router, str): @@ -430,7 +430,7 @@ class TopoGear(object): def __str__(self): links = "" - for myif, dest in self.links.iteritems(): + for myif, dest in self.links.items(): _, destif = dest if links != "": links += "," @@ -684,7 +684,7 @@ class TopoRouter(TopoGear): # Enable all daemon command logging, logging files # and set them to the start dir. - for daemon, enabled in nrouter.daemons.iteritems(): + for daemon, enabled in nrouter.daemons.items(): if enabled == 0: continue self.vtysh_cmd( @@ -733,7 +733,7 @@ class TopoRouter(TopoGear): # Enable all daemon command logging, logging files # and set them to the start dir. - for daemon, enabled in nrouter.daemons.iteritems(): + for daemon, enabled in nrouter.daemons.items(): for d in daemons: if enabled == 0: continue diff --git a/tests/topotests/lib/topojson.py b/tests/topotests/lib/topojson.py index 9c2baedde4..b3af09aa99 100644 --- a/tests/topotests/lib/topojson.py +++ b/tests/topotests/lib/topojson.py @@ -23,6 +23,9 @@ from json import dumps as json_dumps from re import search as re_search import ipaddress import pytest +import ipaddr +from copy import deepcopy + # Import topogen and topotest helpers from lib.topolog import logger @@ -41,7 +44,7 @@ from lib.common_config import ( ) from lib.bgp import create_router_bgp - +from lib.ospf import create_router_ospf ROUTER_LIST = [] @@ -58,12 +61,27 @@ def build_topo_from_json(tgen, topo): topo["routers"].keys(), key=lambda x: int(re_search("\d+", x).group(0)) ) + SWITCH_LIST = [] + if "switches" in topo: + SWITCH_LIST = sorted( + topo["switches"].keys(), key=lambda x: int(re_search("\d+", x).group(0)) + ) + listRouters = ROUTER_LIST[:] + listSwitches = SWITCH_LIST[:] + listAllRouters = deepcopy(listRouters) + dictSwitches = {} + for routerN in ROUTER_LIST: logger.info("Topo: Add router {}".format(routerN)) tgen.add_router(routerN) listRouters.append(routerN) + for switchN in SWITCH_LIST: + logger.info("Topo: Add switch {}".format(switchN)) + dictSwitches[switchN] = tgen.add_switch(switchN) + listSwitches.append(switchN) + if "ipv4base" in topo: ipv4Next = ipaddress.IPv4Address(topo["link_ip_start"]["ipv4"]) ipv4Step = 2 ** (32 - topo["link_ip_start"]["v4mask"]) @@ -91,7 +109,7 @@ def build_topo_from_json(tgen, topo): return int(re_search("\d+", x).group(0)) for destRouterLink, data in sorted( - topo["routers"][curRouter]["links"].iteritems(), + topo["routers"][curRouter]["links"].items(), key=lambda x: link_sort(x[0]), ): currRouter_lo_json = topo["routers"][curRouter]["links"][destRouterLink] @@ -191,6 +209,72 @@ def build_topo_from_json(tgen, topo): ), ) + switch_count = 0 + add_switch_to_topo = [] + while listSwitches != []: + curSwitch = listSwitches.pop(0) + # Physical Interfaces + if "links" in topo['switches'][curSwitch]: + for destRouterLink, data in sorted( + topo['switches'][curSwitch]['links'].iteritems()): + + # Loopback interfaces + if "dst_node" in data: + destRouter = data['dst_node'] + + elif "-" in destRouterLink: + # Spliting and storing destRouterLink data in tempList + tempList = destRouterLink.split("-") + # destRouter + destRouter = tempList.pop(0) + else: + destRouter = destRouterLink + + if destRouter in listAllRouters: + + topo['routers'][destRouter]['links'][curSwitch] = \ + deepcopy(topo['switches'][curSwitch]['links'][destRouterLink]) + + # Assigning name to interfaces + topo['routers'][destRouter]['links'][curSwitch]['interface'] = \ + '{}-{}-eth{}'.format(destRouter, curSwitch, topo['routers'] \ + [destRouter]['nextIfname']) + + topo['switches'][curSwitch]['links'][destRouter]['interface'] = \ + '{}-{}-eth{}'.format(curSwitch, destRouter, topo['routers'] \ + [destRouter]['nextIfname']) + + topo['routers'][destRouter]['nextIfname'] += 1 + + # Add links + dictSwitches[curSwitch].add_link(tgen.gears[destRouter], \ + topo['switches'][curSwitch]['links'][destRouter]['interface'], + topo['routers'][destRouter]['links'][curSwitch]['interface'], + ) + + # IPv4 + if 'ipv4' in topo['routers'][destRouter]['links'][curSwitch]: + if topo['routers'][destRouter]['links'][curSwitch]['ipv4'] == 'auto': + topo['routers'][destRouter]['links'][curSwitch]['ipv4'] = \ + '{}/{}'.format(ipv4Next, topo['link_ip_start'][ \ + 'v4mask']) + ipv4Next += 1 + # IPv6 + if 'ipv6' in topo['routers'][destRouter]['links'][curSwitch]: + if topo['routers'][destRouter]['links'][curSwitch]['ipv6'] == 'auto': + topo['routers'][destRouter]['links'][curSwitch]['ipv6'] = \ + '{}/{}'.format(ipv6Next, topo['link_ip_start'][ \ + 'v6mask']) + ipv6Next = ipaddr.IPv6Address(int(ipv6Next) + ipv6Step) + + logger.debug( + "Generated link data for router: %s\n%s", + curRouter, + json_dumps( + topo["routers"][curRouter]["links"], indent=4, sort_keys=True + ), + ) + def build_config_from_json(tgen, topo, save_bkup=True): """ @@ -210,6 +294,7 @@ def build_config_from_json(tgen, topo, save_bkup=True): ("bgp_community_list", create_bgp_community_lists), ("route_maps", create_route_maps), ("bgp", create_router_bgp), + ("ospf", create_router_ospf) ] ) diff --git a/tests/topotests/ospf-sr-topo1/test_ospf_sr_topo1.py b/tests/topotests/ospf-sr-topo1/test_ospf_sr_topo1.py index 6792c56b3b..86fc90e665 100755..100644 --- a/tests/topotests/ospf-sr-topo1/test_ospf_sr_topo1.py +++ b/tests/topotests/ospf-sr-topo1/test_ospf_sr_topo1.py @@ -93,7 +93,7 @@ def setup_module(mod): router_list = tgen.routers() - for rname, router in router_list.iteritems(): + for rname, router in router_list.items(): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ) 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 130d0c85f9..4ec09b10d3 100755..100644 --- a/tests/topotests/ospf-topo1-vrf/test_ospf_topo1_vrf.py +++ b/tests/topotests/ospf-topo1-vrf/test_ospf_topo1_vrf.py @@ -84,7 +84,7 @@ def setup_module(mod): router_list = tgen.routers() # check for zebra capability - for rname, router in router_list.iteritems(): + for rname, router in router_list.items(): if router.check_capability(TopoRouter.RD_ZEBRA, "--vrfwnetns") == False: return pytest.skip( "Skipping OSPF VRF NETNS feature. VRF NETNS backend not available on FRR" @@ -106,7 +106,7 @@ def setup_module(mod): "ip netns exec {0}-cust1 ifconfig {0}-eth1 up", ] - for rname, router in router_list.iteritems(): + for rname, router in router_list.items(): # create VRF rx-cust1 and link rx-eth0 to rx-cust1 for cmd in cmds: @@ -141,7 +141,7 @@ def teardown_module(mod): ] router_list = tgen.routers() - for rname, router in router_list.iteritems(): + for rname, router in router_list.items(): for cmd in cmds: tgen.net[rname].cmd(cmd.format(rname)) tgen.stop_topology() @@ -169,7 +169,7 @@ def test_ospf_convergence(): if tgen.routers_have_failure(): pytest.skip("skipped because of router(s) failure") - for rname, router in tgen.routers().iteritems(): + for rname, router in tgen.routers().items(): logger.info('Waiting for router "%s" convergence', rname) # Load expected results from the command @@ -216,7 +216,7 @@ def test_ospf_json(): if tgen.routers_have_failure(): pytest.skip("skipped because of router(s) failure") - for rname, router in tgen.routers().iteritems(): + for rname, router in tgen.routers().items(): logger.info( 'Comparing router "%s" "show ip ospf vrf %s-cust1 json" output', router.name, @@ -283,7 +283,7 @@ def test_ospf_link_down(): ) # Expect convergence on all routers - for rname, router in tgen.routers().iteritems(): + for rname, router in tgen.routers().items(): logger.info('Waiting for router "%s" convergence after link failure', rname) # Load expected results from the command reffile = os.path.join(CWD, "{}/ospfroute_down.txt".format(rname)) diff --git a/tests/topotests/ospf-topo1/test_ospf_topo1.py b/tests/topotests/ospf-topo1/test_ospf_topo1.py index d734f378e7..3af60fd48f 100755..100644 --- a/tests/topotests/ospf-topo1/test_ospf_topo1.py +++ b/tests/topotests/ospf-topo1/test_ospf_topo1.py @@ -95,7 +95,7 @@ def setup_module(mod): ospf6_config = "ospf6d.conf-pre-v4" router_list = tgen.routers() - for rname, router in router_list.iteritems(): + for rname, router in router_list.items(): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ) @@ -146,7 +146,7 @@ def test_ospf_convergence(): if tgen.routers_have_failure(): pytest.skip("skipped because of router(s) failure") - for router, rnode in tgen.routers().iteritems(): + for router, rnode in tgen.routers().items(): logger.info('Waiting for router "%s" convergence', router) # Load expected results from the command @@ -335,7 +335,7 @@ def test_ospf_link_down(): router3.peer_link_enable("r3-eth0", False) # Expect convergence on all routers - for router, rnode in tgen.routers().iteritems(): + for router, rnode in tgen.routers().items(): logger.info('Waiting for router "%s" convergence after link failure', router) # Load expected results from the command reffile = os.path.join(CWD, "{}/ospfroute_down.txt".format(router)) diff --git a/tests/topotests/ospf-topo2/test_ospf_topo2.py b/tests/topotests/ospf-topo2/test_ospf_topo2.py index a04d841214..0b6f568462 100755..100644 --- a/tests/topotests/ospf-topo2/test_ospf_topo2.py +++ b/tests/topotests/ospf-topo2/test_ospf_topo2.py @@ -76,7 +76,7 @@ def setup_module(mod): tgen.start_topology() router_list = tgen.routers() - for rname, router in router_list.iteritems(): + for rname, router in router_list.items(): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, '{}/zebra.conf'.format(rname)) @@ -118,7 +118,7 @@ def test_ospf_convergence(): if tgen.routers_have_failure(): pytest.skip('skipped because of router(s) failure') - for router, rnode in tgen.routers().iteritems(): + for router, rnode in tgen.routers().items(): logger.info('Waiting for router "%s" convergence', router) json_file = '{}/{}/ospf-route.json'.format(CWD, router) diff --git a/tests/topotests/ospf6-topo1/test_ospf6_topo1.py b/tests/topotests/ospf6-topo1/test_ospf6_topo1.py index 30c09ea606..8e3a329f10 100755..100644 --- a/tests/topotests/ospf6-topo1/test_ospf6_topo1.py +++ b/tests/topotests/ospf6-topo1/test_ospf6_topo1.py @@ -164,7 +164,7 @@ def setup_module(mod): # tgen.mininet_cli() router_list = tgen.routers() - for rname, router in router_list.iteritems(): + for rname, router in router_list.items(): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ) @@ -209,7 +209,7 @@ def test_ospf6_converged(): sys.stdout.flush() # Look for any node not yet converged - for router, rnode in tgen.routers().iteritems(): + for router, rnode in tgen.routers().items(): resStr = rnode.vtysh_cmd("show ipv6 ospf neigh") isConverged = False @@ -287,7 +287,7 @@ def test_ospfv3_routingTable(): # tgen.mininet_cli() # Verify OSPFv3 Routing Table - for router, rnode in tgen.routers().iteritems(): + for router, rnode in tgen.routers().items(): logger.info('Waiting for router "%s" convergence', router) # Load expected results from the command diff --git a/tests/topotests/ospf_basic_functionality/ospf_authentication.json b/tests/topotests/ospf_basic_functionality/ospf_authentication.json new file mode 100644 index 0000000000..4edb9f25a8 --- /dev/null +++ b/tests/topotests/ospf_basic_functionality/ospf_authentication.json @@ -0,0 +1,166 @@ +{ + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 24 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32 + }, + "routers": { + "r0": { + "links": { + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf": { + "router_id": "100.1.1.0", + "neighbors": { + "r1": {}, + "r2": {}, + "r3": {} + }, + "redistribute": [ + { + "redist_type": "static" + }, + { + "redist_type": "connected" + } + ] + } + }, + "r1": { + "links": { + "r0": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf": { + "router_id": "100.1.1.1", + "neighbors": { + "r0": {}, + "r2": {}, + "r3": {} + } + } + }, + "r2": { + "links": { + "r0": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf": { + "router_id": "100.1.1.2", + "neighbors": { + "r1": {}, + "r0": {}, + "r3": {} + } + } + }, + "r3": { + "links": { + "r0": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf": { + "router_id": "100.1.1.3", + "neighbors": { + "r0": {}, + "r1": {}, + "r2": {} + } + } + } + } +}
\ No newline at end of file diff --git a/tests/topotests/ospf_basic_functionality/ospf_ecmp.json b/tests/topotests/ospf_basic_functionality/ospf_ecmp.json new file mode 100644 index 0000000000..ea5b9a4655 --- /dev/null +++ b/tests/topotests/ospf_basic_functionality/ospf_ecmp.json @@ -0,0 +1,342 @@ +{ + + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 24 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32 + }, + "routers": { + "r0": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r1-link1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r1-link2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r1-link3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r1-link4": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r1-link5": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r1-link6": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r1-link7": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-point" + } + } + }, + "ospf": { + "router_id": "100.1.1.0", + "neighbors": { + "r1": {}, + "r1-link1": { + "nbr": "r1" + }, + "r1-link2": { + "nbr": "r1" + }, + "r1-link3": { + "nbr": "r1" + }, + "r1-link4": { + "nbr": "r1" + }, + "r1-link5": { + "nbr": "r1" + }, + "r1-link6": { + "nbr": "r1" + }, + "r1-link7": { + "nbr": "r1" + }, + "r2": {}, + "r3": {} + } + } + }, + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r0": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r0-link1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r0-link2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r0-link3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r0-link4": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r0-link5": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r0-link6": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r0-link7": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3-link0": { + "ipv4": "auto", + "description": "DummyIntftoR3" + } + }, + "ospf": { + "router_id": "100.1.1.1", + "neighbors": { + "r0": {}, + "r0-link1": { + "nbr": "r0" + }, + "r0-link2": { + "nbr": "r0" + }, + "r0-link3": { + "nbr": "r0" + }, + "r0-link4": { + "nbr": "r0" + }, + "r0-link5": { + "nbr": "r0" + }, + "r0-link6": { + "nbr": "r0" + }, + "r0-link7": { + "nbr": "r0" + }, + "r2": {}, + "r3": {} + } + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r0": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf": { + "router_id": "100.1.1.2", + "neighbors": { + "r1": {}, + "r0": {}, + "r3": {} + } + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r0": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-point" + } + }, + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r1-link0": { + "ipv4": "auto", + "description": "DummyIntftoR1", + "ospf": { + "area": "0.0.0.0" + } + } + }, + "ospf": { + "router_id": "100.1.1.3", + "neighbors": { + "r0": {}, + "r1": {}, + "r2": {} + } + } + } + } +}
\ No newline at end of file diff --git a/tests/topotests/ospf_basic_functionality/ospf_ecmp_lan.json b/tests/topotests/ospf_basic_functionality/ospf_ecmp_lan.json new file mode 100644 index 0000000000..ecfd35d639 --- /dev/null +++ b/tests/topotests/ospf_basic_functionality/ospf_ecmp_lan.json @@ -0,0 +1,234 @@ +{ + + "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": "auto", + "ospf": { + "area": "0.0.0.3", + "hello_interval": 1, + "dead_interval": 4, + "priority": 98 + } + }, + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.3", + "hello_interval": 1, + "dead_interval": 4, + "priority": 99 + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.3", + "hello_interval": 1, + "dead_interval": 4, + "priority": 0 + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.3", + "hello_interval": 1, + "dead_interval": 4, + "priority": 0 + } + }, + "r4": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.3", + "hello_interval": 1, + "dead_interval": 4, + "priority": 0 + } + }, + "r5": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.3", + "hello_interval": 1, + "dead_interval": 4, + "priority": 0 + } + }, + "r6": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.3", + "hello_interval": 1, + "dead_interval": 4, + "priority": 0 + } + }, + "r7": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.3", + "hello_interval": 1, + "dead_interval": 4, + "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" + }, + "r3-link0": { + "ipv4": "auto", + "description": "DummyIntftoR3" + } + }, + "ospf": { + "router_id": "100.1.1.1", + "neighbors": { + "r0": {}, + "r2": {}, + "r3": {} + } + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + } + }, + "ospf": { + "router_id": "100.1.1.2", + "area": [{ + "id": "0.0.0.2", + "type": "nssa" + }], + "neighbors": { + "r1": {}, + "r0": {} + } + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r1-link0": { + "ipv4": "auto", + "description": "DummyIntftoR1", + "ospf": { + "area": "0.0.0.3" + } + } + }, + "ospf": { + "router_id": "100.1.1.3", + "area": [{ + "id": "0.0.0.2", + "type": "nssa" + }], + "neighbors": { + "r0": {}, + "r1": {} + } + } + }, + "r4": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + } + }, + "ospf": { + "router_id": "100.1.1.4", + "neighbors": { + "r0": {}, + "r1": {} + } + } + }, + "r5": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + } + }, + "ospf": { + "router_id": "100.1.1.5", + "neighbors": { + "r0": {}, + "r1": {} + } + } + }, + "r6": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + } + }, + "ospf": { + "router_id": "100.1.1.6", + "neighbors": { + "r0": {}, + "r1": {} + } + } + }, + "r7": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + } + }, + "ospf": { + "router_id": "100.1.1.7", + "neighbors": { + "r0": {}, + "r1": {} + } + } + } + } +}
\ No newline at end of file diff --git a/tests/topotests/ospf_basic_functionality/ospf_lan.json b/tests/topotests/ospf_basic_functionality/ospf_lan.json new file mode 100644 index 0000000000..126934c344 --- /dev/null +++ b/tests/topotests/ospf_basic_functionality/ospf_lan.json @@ -0,0 +1,138 @@ +{ + + "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": "auto", + "ospf": { + "area": "0.0.0.3", + "hello_interval": 1, + "dead_interval": 4, + "priority": 98 + } + }, + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.3", + "hello_interval": 1, + "dead_interval": 4, + "priority": 99 + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.3", + "hello_interval": 1, + "dead_interval": 4, + "priority": 0 + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.3", + "hello_interval": 1, + "dead_interval": 4, + "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" + }, + "r3-link0": { + "ipv4": "auto", + "description": "DummyIntftoR3" + } + }, + "ospf": { + "router_id": "100.1.1.1", + "neighbors": { + "r0": {}, + "r2": {}, + "r3": {} + } + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + } + }, + "ospf": { + "router_id": "100.1.1.2", + "area": [{ + "id": "0.0.0.2", + "type": "nssa" + }], + "neighbors": { + "r1": {}, + "r0": {} + } + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r1-link0": { + "ipv4": "auto", + "description": "DummyIntftoR1", + "ospf": { + "area": "0.0.0.3" + } + } + }, + "ospf": { + "router_id": "100.1.1.3", + "area": [{ + "id": "0.0.0.2", + "type": "nssa" + }], + "neighbors": { + "r0": {}, + "r1": {} + } + } + } + } +}
\ No newline at end of file diff --git a/tests/topotests/ospf_basic_functionality/ospf_nssa.json b/tests/topotests/ospf_basic_functionality/ospf_nssa.json new file mode 100644 index 0000000000..f95a297bb3 --- /dev/null +++ b/tests/topotests/ospf_basic_functionality/ospf_nssa.json @@ -0,0 +1,188 @@ +{ + + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 24 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32 + }, + "routers": { + "r0": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto" + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-point" + } + } + }, + "ospf": { + "router_id": "100.1.1.0", + "neighbors": { + "r1": {}, + "r3": {} + } + } + }, + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r0": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.2", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.2", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3-link0": { + "ipv4": "auto", + "description": "DummyIntftoR3" + } + }, + "ospf": { + "router_id": "100.1.1.1", + "area": [{ + "id": "0.0.0.2", + "type": "nssa" + }], + "neighbors": { + "r0": {}, + "r2": {}, + "r3": {} + } + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r0": { + "ipv4": "auto" + }, + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.2", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.2", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf": { + "router_id": "100.1.1.2", + "area": [{ + "id": "0.0.0.2", + "type": "nssa" + }], + "neighbors": { + "r1": {}, + "r3": {} + } + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r0": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-point" + } + }, + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.2", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.2", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r1-link0": { + "ipv4": "auto", + "description": "DummyIntftoR1", + "ospf": { + "area": "0.0.0.3" + } + } + }, + "ospf": { + "router_id": "100.1.1.3", + "area": [{ + "id": "0.0.0.2", + "type": "nssa" + }], + "neighbors": { + "r0": {}, + "r1": {}, + "r2": {} + } + } + } + } +}
\ No newline at end of file diff --git a/tests/topotests/ospf_basic_functionality/ospf_routemaps.json b/tests/topotests/ospf_basic_functionality/ospf_routemaps.json new file mode 100644 index 0000000000..60a8434d42 --- /dev/null +++ b/tests/topotests/ospf_basic_functionality/ospf_routemaps.json @@ -0,0 +1,159 @@ +{ + + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 24 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32 + }, + "routers": { + "r0": { + "links": { + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf": { + "router_id": "100.1.1.0", + "neighbors": { + "r1": {}, + "r2": {}, + "r3": {} + } + } + }, + "r1": { + "links": { + "r0": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf": { + "router_id": "100.1.1.1", + "neighbors": { + "r0": {}, + "r2": {}, + "r3": {} + } + } + }, + "r2": { + "links": { + "r0": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf": { + "router_id": "100.1.1.2", + "neighbors": { + "r1": {}, + "r0": {}, + "r3": {} + } + } + }, + "r3": { + "links": { + "r0": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf": { + "router_id": "100.1.1.3", + "neighbors": { + "r0": {}, + "r1": {}, + "r2": {} + } + } + } + } +}
\ No newline at end of file diff --git a/tests/topotests/ospf_basic_functionality/ospf_rte_calc.json b/tests/topotests/ospf_basic_functionality/ospf_rte_calc.json new file mode 100644 index 0000000000..9062a09091 --- /dev/null +++ b/tests/topotests/ospf_basic_functionality/ospf_rte_calc.json @@ -0,0 +1,168 @@ +{ + + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 24 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32 + }, + "routers": { + "r0": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf": { + "router_id": "100.1.1.0", + "neighbors": { + "r1": {}, + "r2": {} + } + } + }, + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r0": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf": { + "router_id": "100.1.1.1", + "neighbors": { + "r0": {}, + "r2": {}, + "r3": {} + } + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r0": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf": { + "router_id": "100.1.1.2", + "neighbors": { + "r1": {}, + "r0": {}, + "r3": {} + } + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r0": { + "ipv4": "auto" + }, + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf": { + "router_id": "100.1.1.3", + "neighbors": { + "r1": {}, + "r2": {} + } + } + } + } +}
\ No newline at end of file diff --git a/tests/topotests/ospf_basic_functionality/ospf_single_area.json b/tests/topotests/ospf_basic_functionality/ospf_single_area.json new file mode 100644 index 0000000000..c595912887 --- /dev/null +++ b/tests/topotests/ospf_basic_functionality/ospf_single_area.json @@ -0,0 +1,188 @@ +{ + + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 24 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32 + }, + "routers": { + "r0": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-point" + } + } + }, + "ospf": { + "router_id": "100.1.1.0", + "neighbors": { + "r1": {}, + "r2": {}, + "r3": {} + } + } + }, + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r0": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3-link0": { + "ipv4": "auto", + "description": "DummyIntftoR3" + } + }, + "ospf": { + "router_id": "100.1.1.1", + "neighbors": { + "r0": {}, + "r2": {}, + "r3": {} + } + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r0": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf": { + "router_id": "100.1.1.2", + "neighbors": { + "r1": {}, + "r0": {}, + "r3": {} + } + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "type": "loopback" + }, + "r0": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-point" + } + }, + "r1": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv4": "auto", + "ospf": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r1-link0": { + "ipv4": "auto", + "description": "DummyIntftoR1", + "ospf": { + "area": "0.0.0.0" + } + } + }, + "ospf": { + "router_id": "100.1.1.3", + "neighbors": { + "r0": {}, + "r1": {}, + "r2": {} + } + } + } + } +}
\ No newline at end of file diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_authentication.py b/tests/topotests/ospf_basic_functionality/test_ospf_authentication.py new file mode 100644 index 0000000000..a2f9c03ab4 --- /dev/null +++ b/tests/topotests/ospf_basic_functionality/test_ospf_authentication.py @@ -0,0 +1,891 @@ +#!/usr/bin/python + +# +# Copyright (c) 2020 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 +from time import sleep +from copy import deepcopy +import json + +# 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, "../lib/")) + +# 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, + step, + shutdown_bringup_interface, + topo_daemons +) +from lib.topolog import logger +from lib.topojson import build_topo_from_json, build_config_from_json +from lib.ospf import verify_ospf_neighbor, config_ospf_interface, clear_ospf +from ipaddress import IPv4Address + +# Global variables +topo = None +# Reading the data from JSON File for topology creation +jsonFile = "{}/ospf_authentication.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) +""" +TOPOOLOGY = + Please view in a fixed-width font such as Courier. + +---+ A1 +---+ + +R1 +------------+R2 | + +-+-+- +--++ + | -- -- | + | -- A0 -- | + A0| ---- | + | ---- | A2 + | -- -- | + | -- -- | + +-+-+- +-+-+ + +R0 +-------------+R3 | + +---+ A3 +---+ + +TESTCASES = +1. Verify ospf authentication with Simple password authentication. +2. Verify ospf authentication with MD5 authentication. +3. Verify ospf authentication with different authentication methods. + + """ + + +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 + 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) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + 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() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +# ################################## +# Test cases start here. +# ################################## + + +def test_ospf_authentication_simple_pass_tc28_p1(request): + """ + OSPF Authentication - Verify ospf authentication with Simple + password authentication. + + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + global topo + step("Bring up the base config.") + reset_config_on_routers(tgen) + step( + "Configure ospf with on R1 and R2, enable ospf on R1 interface" + "connected to R2 with simple password authentication using ip ospf " + "authentication Simple password cmd." + ) + + r1_ospf_auth = { + "r1": { + "links": { + "r2": {"ospf": {"authentication": True, "authentication-key": "ospf"}} + } + } + } + result = config_ospf_interface(tgen, topo, r1_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("clear ip ospf after configuring the authentication.") + clear_ospf(tgen, "r1") + + step("Verify that the neighbour is not FULL between R1 and R2.") + dut = "r1" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, expected=False) + assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step( + "On R2 enable ospf on interface with simple password authentication " + "using ip ospf authentication Simple password cmd." + ) + + r2_ospf_auth = { + "r2": { + "links": { + "r1": {"ospf": {"authentication": True, "authentication-key": "ospf"}} + } + } + } + result = config_ospf_interface(tgen, topo, r2_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that the neighbour is FULL between R1 and R2 " + "using show ip ospf neighbor cmd." + ) + + dut = "r2" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step( + "Disable simple password authentication on R2 using no ip ospf " + "authentication Simple password cmd." + ) + r2_ospf_auth = { + "r2": { + "links": { + "r1": { + "ospf": { + "authentication": True, + "authentication-key": "ospf", + "del_action": True, + } + } + } + } + } + result = config_ospf_interface(tgen, topo, r2_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify on R1 neighbour is deleted for R2 after dead interval expiry") + # wait till the dead time expiry + sleep(6) + dut = "r2" + ospf_covergence = verify_ospf_neighbor( + tgen, topo, dut=dut, expected=False, attempts=5 + ) + assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("Again On R2 enable ospf on interface with Simple password auth") + r2_ospf_auth = { + "r2": { + "links": { + "r1": {"ospf": {"authentication": True, "authentication-key": "ospf"}} + } + } + } + result = config_ospf_interface(tgen, topo, r2_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that the neighbour is FULL between R1 and R2 using" + " show ip ospf neighbor cmd." + ) + + dut = "r2" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("Shut no shut interface on R1") + dut = "r1" + intf = topo["routers"]["r1"]["links"]["r2"]["interface"] + shutdown_bringup_interface(tgen, dut, intf, False) + + dut = "r2" + step( + "Verify that the neighbour is not FULL between R1 and R2 using " + "show ip ospf neighbor cmd." + ) + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, expected=False) + assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + dut = "r1" + shutdown_bringup_interface(tgen, dut, intf, True) + + step( + "Verify that the neighbour is FULL between R1 and R2 using " + "show ip ospf neighbor cmd." + ) + + dut = "r2" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("Change Ip address on R1 and R2") + + topo_modify_change_ip = deepcopy(topo) + intf_ip = topo_modify_change_ip["routers"]["r1"]["links"]["r2"]["ipv4"] + topo_modify_change_ip["routers"]["r1"]["links"]["r2"]["ipv4"] = str( + IPv4Address(unicode(intf_ip.split("/")[0])) + 3 + ) + "/{}".format(intf_ip.split("/")[1]) + + build_config_from_json(tgen, topo_modify_change_ip, save_bkup=False) + + reset_config_on_routers(tgen, routerName="r1") + dut = "r1" + intf = topo["routers"]["r1"]["links"]["r2"]["interface"] + shutdown_bringup_interface(tgen, dut, intf, False) + shutdown_bringup_interface(tgen, dut, intf, True) + + # clear ip ospf after configuring the authentication. + clear_ospf(tgen, "r1") + + r1_ospf_auth = { + "r1": { + "links": { + "r2": {"ospf": {"authentication": True, "authentication-key": "ospf"}} + } + } + } + result = config_ospf_interface(tgen, topo, r1_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that the neighbour is FULL between R1 and R2 with new " + "ip address using show ip ospf " + ) + + dut = "r1" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + write_test_footer(tc_name) + + +def test_ospf_authentication_md5_tc29_p1(request): + """ + OSPF Authentication - Verify ospf authentication with MD5 authentication. + + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + global topo + step("Bring up the base config.") + reset_config_on_routers(tgen) + step( + "Configure ospf with on R1 and R2, enable ospf on R1 interface " + "connected to R2 with message-digest authentication using ip " + "ospf authentication message-digest cmd." + ) + + r1_ospf_auth = { + "r1": { + "links": { + "r2": { + "ospf": { + "authentication": "message-digest", + "authentication-key": "ospf", + "message-digest-key": "10", + } + } + } + } + } + result = config_ospf_interface(tgen, topo, r1_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify that the neighbour is not FULL between R1 and R2.") + # wait for dead time expiry. + sleep(6) + dut = "r1" + ospf_covergence = verify_ospf_neighbor( + tgen, topo, dut=dut, expected=False, attempts=3 + ) + assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step( + "On R2 enable ospf on interface with message-digest authentication" + " using ip ospf authentication message-digest password cmd." + ) + + r2_ospf_auth = { + "r2": { + "links": { + "r1": { + "ospf": { + "authentication": "message-digest", + "authentication-key": "ospf", + "message-digest-key": "10", + } + } + } + } + } + result = config_ospf_interface(tgen, topo, r2_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that the neighbour is FULL between R1 and R2 " + "using show ip ospf neighbor cmd." + ) + + dut = "r2" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step( + "Disable message-digest authentication on R2 using no ip ospf " + "authentication message-digest password cmd." + ) + + r2_ospf_auth = { + "r2": { + "links": { + "r1": { + "ospf": { + "authentication": "message-digest", + "authentication-key": "ospf", + "message-digest-key": "10", + "del_action": True, + } + } + } + } + } + result = config_ospf_interface(tgen, topo, r2_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify on R1 ,nbr is deleted for R2 after dead interval expiry") + # wait till the dead timer expiry + sleep(6) + dut = "r2" + ospf_covergence = verify_ospf_neighbor( + tgen, topo, dut=dut, expected=False, attempts=5 + ) + assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("Again On R2 enable ospf on interface with message-digest auth") + r2_ospf_auth = { + "r2": { + "links": { + "r1": { + "ospf": { + "authentication": "message-digest", + "authentication-key": "ospf", + "message-digest-key": "10", + } + } + } + } + } + result = config_ospf_interface(tgen, topo, r2_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that the neighbour is FULL between R1 and R2 using" + " show ip ospf neighbor cmd." + ) + + dut = "r2" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("Shut no shut interface on R1") + dut = "r1" + intf = topo["routers"]["r1"]["links"]["r2"]["interface"] + shutdown_bringup_interface(tgen, dut, intf, False) + + dut = "r2" + step( + "Verify that the neighbour is not FULL between R1 and R2 using " + "show ip ospf neighbor cmd." + ) + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, expected=False) + assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + dut = "r1" + shutdown_bringup_interface(tgen, dut, intf, True) + + step( + "Verify that the neighbour is FULL between R1 and R2 using " + "show ip ospf neighbor cmd." + ) + + dut = "r2" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("Change Ip address on R1 and R2") + + topo_modify_change_ip = deepcopy(topo) + + intf_ip = topo_modify_change_ip["routers"]["r1"]["links"]["r2"]["ipv4"] + + topo_modify_change_ip["routers"]["r1"]["links"]["r2"]["ipv4"] = str( + IPv4Address(unicode(intf_ip.split("/")[0])) + 3 + ) + "/{}".format(intf_ip.split("/")[1]) + + build_config_from_json(tgen, topo_modify_change_ip, save_bkup=False) + + reset_config_on_routers(tgen, routerName="r1") + dut = "r1" + intf = topo["routers"]["r1"]["links"]["r2"]["interface"] + shutdown_bringup_interface(tgen, dut, intf, False) + shutdown_bringup_interface(tgen, dut, intf, True) + clear_ospf(tgen, "r1") + r1_ospf_auth = { + "r1": { + "links": { + "r2": { + "ospf": { + "authentication": "message-digest", + "authentication-key": "ospf", + "message-digest-key": "10", + } + } + } + } + } + result = config_ospf_interface(tgen, topo, r1_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that the neighbour is FULL between R1 and R2 with new " + "ip address using show ip ospf " + ) + + dut = "r1" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + write_test_footer(tc_name) + + +def test_ospf_authentication_different_auths_tc30_p1(request): + """ + OSPF Authentication - Verify ospf authentication with different + authentication methods. + + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + global topo + step("Bring up the base config.") + reset_config_on_routers(tgen) + step( + "Configure ospf with on R1 and R2, enable ospf on R1 interface " + "connected to R2 with message-digest authentication using ip " + "ospf authentication message-digest cmd." + ) + + r1_ospf_auth = { + "r1": { + "links": { + "r2": { + "ospf": { + "authentication": "message-digest", + "authentication-key": "ospf", + "message-digest-key": "10", + } + } + } + } + } + result = config_ospf_interface(tgen, topo, r1_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + # wait for dead timer expiry + sleep(6) + step("Verify that the neighbour is not FULL between R1 and R2.") + dut = "r1" + ospf_covergence = verify_ospf_neighbor( + tgen, topo, dut=dut, expected=False, attempts=5 + ) + assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step( + "On R2 enable ospf on interface with message-digest authentication" + " using ip ospf authentication message-digest password cmd." + ) + + r2_ospf_auth = { + "r2": { + "links": { + "r1": { + "ospf": { + "authentication": "message-digest", + "authentication-key": "ospf", + "message-digest-key": "10", + } + } + } + } + } + result = config_ospf_interface(tgen, topo, r2_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that the neighbour is FULL between R1 and R2 " + "using show ip ospf neighbor cmd." + ) + + dut = "r2" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step(" Delete the configured password on both the routers.") + + r2_ospf_auth = { + "r2": { + "links": { + "r1": { + "ospf": { + "authentication": "message-digest", + "authentication-key": "ospf", + "message-digest-key": "10", + "del_action": True, + } + } + } + } + } + result = config_ospf_interface(tgen, topo, r2_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + r1_ospf_auth = { + "r1": { + "links": { + "r2": { + "ospf": { + "authentication": "message-digest", + "authentication-key": "ospf", + "message-digest-key": "10", + "del_action": True, + } + } + } + } + } + result = config_ospf_interface(tgen, topo, r1_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that the deletion is successful and neighbour is FULL" + " between R1 and R2 using show ip ospf neighbor cmd." + ) + + dut = "r2" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("Change the authentication type to simple password.") + r1_ospf_auth = { + "r1": { + "links": { + "r2": {"ospf": {"authentication": True, "authentication-key": "ospf"}} + } + } + } + result = config_ospf_interface(tgen, topo, r1_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + r2_ospf_auth = { + "r2": { + "links": { + "r1": {"ospf": {"authentication": True, "authentication-key": "ospf"}} + } + } + } + result = config_ospf_interface(tgen, topo, r2_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that the deletion is successful and neighbour is" + " FULL between R1 and R2 using show ip " + ) + + dut = "r2" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("Change the password in simple password.") + + r1_ospf_auth = { + "r1": { + "links": { + "r2": {"ospf": {"authentication": True, "authentication-key": "OSPFv4"}} + } + } + } + result = config_ospf_interface(tgen, topo, r1_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + r2_ospf_auth = { + "r2": { + "links": { + "r1": {"ospf": {"authentication": True, "authentication-key": "OSPFv4"}} + } + } + } + result = config_ospf_interface(tgen, topo, r2_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that the deletion is successful and neighbour is" + " FULL between R1 and R2 using show ip " + ) + + dut = "r2" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("Delete the password authentication on the interface ") + + r1_ospf_auth = { + "r1": { + "links": { + "r2": { + "ospf": { + "authentication": True, + "authentication-key": "OSPFv4", + "del_action": True, + } + } + } + } + } + result = config_ospf_interface(tgen, topo, r1_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + r2_ospf_auth = { + "r2": { + "links": { + "r1": { + "ospf": { + "authentication": True, + "authentication-key": "OSPFv4", + "del_action": True, + } + } + } + } + } + result = config_ospf_interface(tgen, topo, r2_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that the deletion is successful and neighbour is" + " FULL between R1 and R2 using show ip " + ) + + dut = "r2" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("Enable Md5 authentication on the interface") + + r1_ospf_auth = { + "r1": { + "links": { + "r2": { + "ospf": { + "authentication": "message-digest", + "authentication-key": "ospf", + "message-digest-key": "10", + } + } + } + } + } + result = config_ospf_interface(tgen, topo, r1_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + r2_ospf_auth = { + "r2": { + "links": { + "r1": { + "ospf": { + "authentication": "message-digest", + "authentication-key": "ospf", + "message-digest-key": "10", + } + } + } + } + } + result = config_ospf_interface(tgen, topo, r2_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that the neighbour is FULL between R1 and R2 using" + " show ip ospf neighbor cmd." + ) + + dut = "r2" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("Change the MD5 authentication password") + + r1_ospf_auth = { + "r1": { + "links": { + "r2": { + "ospf": { + "authentication": "message-digest", + "authentication-key": "OSPFv4", + "message-digest-key": "10", + } + } + } + } + } + result = config_ospf_interface(tgen, topo, r1_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + r2_ospf_auth = { + "r2": { + "links": { + "r1": { + "ospf": { + "authentication": "message-digest", + "authentication-key": "OSPFv4", + "message-digest-key": "10", + } + } + } + } + } + result = config_ospf_interface(tgen, topo, r2_ospf_auth) + 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/ospf_basic_functionality/test_ospf_ecmp.py b/tests/topotests/ospf_basic_functionality/test_ospf_ecmp.py new file mode 100644 index 0000000000..399fa02230 --- /dev/null +++ b/tests/topotests/ospf_basic_functionality/test_ospf_ecmp.py @@ -0,0 +1,495 @@ +#!/usr/bin/python + +# +# Copyright (c) 2020 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 + +# 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, "../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from mininet.topo import Topo +from lib.topogen import Topogen, get_topogen +from ipaddress import IPv4Address + +# 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 +) +from lib.topolog import logger + +from lib.topojson import build_topo_from_json, build_config_from_json +from lib.ospf import ( + verify_ospf_neighbor, + config_ospf_interface, + clear_ospf, + verify_ospf_rib, + create_router_ospf, + verify_ospf_interface, +) + +topo = None + +# Reading the data from JSON File for topology creation +jsonFile = "{}/ospf_ecmp.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", + ] +} +""" +TOPOLOGY : + Please view in a fixed-width font such as Courier. + +---+ A1 +---+ + +R1 +------------+R2 | + +-+-+- +--++ + | -- -- | + | -- A0 -- | + A0| ---- | + | ---- | A2 + | -- -- | + | -- -- | + +-+-+- +-+-+ + +R0 +-------------+R3 | + +---+ A3 +---+ + +TESTCASES : +1. Verify OSPF ECMP with max path configured as 8 (ECMPconfigured at FRR level) +2. Verify OSPF ECMP with max path configured as 2 (Edge having 2 uplink ports) + """ + + +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 + 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) + # 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 + ) + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + 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() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +def red_static(dut, config=True): + """Local def for Redstribute static routes inside ospf.""" + global topo + tgen = get_topogen() + if config: + ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "static"}]}}} + else: + ospf_red = { + dut: {"ospf": {"redistribute": [{"redist_type": "static", "delete": True}]}} + } + result = create_router_ospf(tgen, topo, ospf_red) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + +def red_connected(dut, config=True): + """Local def for Redstribute connected routes inside ospf.""" + global topo + tgen = get_topogen() + if config: + ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "connected"}]}}} + else: + ospf_red = { + dut: { + "ospf": { + "redistribute": [{"redist_type": "connected", "del_action": True}] + } + } + } + result = create_router_ospf(tgen, topo, ospf_red) + assert result is True, "Testcase: Failed \n Error: {}".format(result) + + +# ################################## +# Test cases start here. +# ################################## + + +def test_ospf_ecmp_tc16_p0(request): + """ + Verify OSPF ECMP. + + Verify OSPF ECMP with max path configured as 8 (ECMP + configured at FRR level) + """ + 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 + step("Bring up the base config as per the topology") + step("Configure 8 interfaces between R1 and R2 and enable ospf in area 0.") + reset_config_on_routers(tgen) + step("Verify that OSPF is up with 8 neighborship sessions.") + dut = "r1" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("Configure a static route in R0 and redistribute in OSPF.") + + 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) + + dut = "r0" + red_static(dut) + + step("Verify that route in R2 in stalled with 8 next hops.") + nh = [] + for item in range(1, 7): + nh.append(topo["routers"]["r0"]["links"]["r1-link1"]["ipv4"].split("/")[0]) + + nh2 = topo["routers"]["r0"]["links"]["r1"]["ipv4"].split("/")[0] + + nh.append(nh2) + + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + protocol = "ospf" + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("shut no shut all the interfaces on the remote router - R2") + dut = "r1" + for intfr in range(1, 7): + intf = topo["routers"]["r1"]["links"]["r0-link{}".format(intfr)]["interface"] + shutdown_bringup_interface(tgen, dut, intf, False) + + result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh, expected=False) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + protocol = "ospf" + result = verify_rib( + tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh, expected=False + ) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for intfr in range(1, 7): + intf = topo["routers"]["r1"]["links"]["r0-link{}".format(intfr)]["interface"] + shutdown_bringup_interface(tgen, dut, intf, True) + + result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + protocol = "ospf" + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("shut no shut on all the interfaces on DUT (r1)") + for intfr in range(1, 7): + intf = topo["routers"]["r1"]["links"]["r0-link{}".format(intfr)]["interface"] + shutdown_bringup_interface(tgen, dut, intf, False) + + for intfr in range(1, 7): + intf = topo["routers"]["r1"]["links"]["r0-link{}".format(intfr)]["interface"] + shutdown_bringup_interface(tgen, dut, intf, True) + + step( + "Verify that all the neighbours are up and routes are installed" + " with 8 next hop in ospf and ip route tables on R1." + ) + + step("Verify that OSPF is up with 8 neighborship sessions.") + dut = "r1" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + protocol = "ospf" + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step(" Un configure static route on R0") + + dut = "r0" + red_static(dut, config=False) + + # Wait for R0 to flush external LSAs. + sleep(10) + + step("Verify that route is withdrawn from R2.") + dut = "r1" + result = verify_ospf_rib( + tgen, dut, input_dict, next_hop=nh, attempts=5, expected=False + ) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + protocol = "ospf" + result = verify_rib( + tgen, + "ipv4", + dut, + input_dict, + protocol=protocol, + next_hop=nh, + attempts=5, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Re configure the static route in R0.") + dut = "r0" + red_static(dut) + + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + protocol = "ospf" + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_ospf_ecmp_tc17_p0(request): + """ + Verify OSPF ECMP. + + Verify OSPF ECMP with max path configured as 2 (Edge having 2 uplink ports) + """ + 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 + step("Bring up the base config as per the topology") + step("Configure 2 interfaces between R1 and R2 & enable ospf in area 0.") + reset_config_on_routers(tgen) + step("Verify that OSPF is up with 2 neighborship sessions.") + dut = "r1" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("Configure a static route in R0 and redistribute in OSPF.") + + 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) + + dut = "r0" + red_static(dut) + + step("Verify that route in R2 in stalled with 2 next hops.") + + nh1 = topo["routers"]["r0"]["links"]["r1-link1"]["ipv4"].split("/")[0] + nh2 = topo["routers"]["r0"]["links"]["r1"]["ipv4"].split("/")[0] + nh = [nh1, nh2] + + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + protocol = "ospf" + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step(" Un configure static route on R0") + + dut = "r0" + red_static(dut, config=False) + # sleep till the route gets withdrawn + sleep(10) + + step("Verify that route is withdrawn from R2.") + dut = "r1" + result = verify_ospf_rib( + tgen, dut, input_dict, next_hop=nh, attempts=5, expected=False + ) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + protocol = "ospf" + result = verify_rib( + tgen, + "ipv4", + dut, + input_dict, + protocol=protocol, + next_hop=nh, + attempts=5, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Reconfigure the static route in R0.Change ECMP value to 2.") + dut = "r0" + red_static(dut) + + step("Configure cost on R0 as 100") + r0_ospf_cost = {"r0": {"links": {"r1": {"ospf": {"cost": 100}}}}} + result = config_ospf_interface(tgen, topo, r0_ospf_cost) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + protocol = "ospf" + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh) + 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/ospf_basic_functionality/test_ospf_ecmp_lan.py b/tests/topotests/ospf_basic_functionality/test_ospf_ecmp_lan.py new file mode 100644 index 0000000000..17a3676e2e --- /dev/null +++ b/tests/topotests/ospf_basic_functionality/test_ospf_ecmp_lan.py @@ -0,0 +1,369 @@ +#!/usr/bin/python + +# +# Copyright (c) 2020 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 + +# 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, "../lib/")) + +# 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, + create_interfaces_cfg, + write_test_footer, + reset_config_on_routers, + verify_rib, + create_static_routes, + check_address_types, + step, + create_route_maps, + shutdown_bringup_interface, + stop_router, + start_router, + topo_daemons +) +from lib.bgp import verify_bgp_convergence, create_router_bgp +from lib.topolog import logger +from lib.topojson import build_topo_from_json, build_config_from_json + +from lib.ospf import ( + verify_ospf_neighbor, + config_ospf_interface, + clear_ospf, + verify_ospf_rib, + create_router_ospf, + verify_ospf_interface, +) +from ipaddress import IPv4Address + +# Global variables +topo = None +# Reading the data from JSON File for topology creation + +jsonFile = "{}/ospf_ecmp_lan.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +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"], +} +""" +TOPOOLOGY = + Please view in a fixed-width font such as Courier. + Topo : Broadcast Networks + +---+ +---+ +---+ +---+ + |R0 + +R1 + +R2 + +R3 | + +-+-+ +-+-+ +-+-+ +-+-+ + | | | | + | | | | + --+-----------+--------------+---------------+----- + Ethernet Segment + +TESTCASES = +1. Verify OSPF ECMP with max path configured as 8 + (Edge having 1 uplink port as broadcast network, + connect to 8 TORs - LAN case) + + """ + + +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 + 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) + # Api call verify whether OSPF is converged + ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + 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 red_static(dut, config=True): + """Local def for Redstribute static routes inside ospf.""" + global topo + tgen = get_topogen() + if config: + ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "static"}]}}} + else: + ospf_red = { + dut: { + "ospf": { + "redistribute": [{"redist_type": "static", "del_action": True}] + } + } + } + result = create_router_ospf(tgen, topo, ospf_red) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + +def red_connected(dut, config=True): + """Local def for Redstribute connected routes inside ospf.""" + global topo + tgen = get_topogen() + if config: + ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "connected"}]}}} + else: + ospf_red = { + dut: { + "ospf": { + "redistribute": [{"redist_type": "connected", "del_action": True}] + } + } + } + result = create_router_ospf(tgen, topo, ospf_red) + assert result is True, "Testcase: Failed \n Error: {}".format(result) + + +# ################################## +# Test cases start here. +# ################################## + + +def test_ospf_lan_ecmp_tc18_p0(request): + """ + OSPF ECMP. + + Verify OSPF ECMP with max path configured as 8 + (Edge having 1 uplink port as broadcast network, + connect to 8 TORs - LAN case) + + """ + 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 + step("Bring up the base config as per the topology") + step(". Configure ospf in all the routers on LAN interface.") + reset_config_on_routers(tgen) + step("Verify that OSPF is up with 8 neighborship sessions.") + + ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step( + "Configure a static route in all the routes and " + "redistribute static/connected in OSPF." + ) + + for rtr in topo["routers"]: + input_dict = { + rtr: { + "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 + ) + + dut = rtr + red_static(dut) + + step( + "Verify that route in R0 in stalled with 8 hops. " + "Verify ospf route table and ip route table." + ) + + nh = [] + for rtr in topo["routers"]: + nh.append(topo["routers"][rtr]["links"]["s1"]["ipv4"].split("/")[0]) + nh.remove(topo["routers"]["r1"]["links"]["s1"]["ipv4"].split("/")[0]) + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + protocol = "ospf" + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step(" clear ip ospf interface on DUT(r0)") + clear_ospf(tgen, "r0") + + step( + "Verify that after clearing the ospf interface all the " + "neighbours are up and routes are installed with 8 next hop " + "in ospf and ip route tables on R0" + ) + + dut = "r0" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, lan=True) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step(" clear ip ospf interface on R2") + clear_ospf(tgen, "r2") + + dut = "r2" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, lan=True) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("Delete static/connected cmd in ospf in all the routes one by one.") + for rtr in topo["routers"]: + input_dict = { + rtr: { + "static_routes": [ + { + "network": NETWORK["ipv4"][0], + "no_of_ip": 5, + "next_hop": "Null0", + "delete": True, + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that all the routes are withdrawn from R0") + dut = "r1" + result = verify_ospf_rib( + tgen, dut, input_dict, next_hop=nh, attempts=5, expected=False + ) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + protocol = "ospf" + result = verify_rib( + tgen, + "ipv4", + dut, + input_dict, + protocol=protocol, + next_hop=nh, + attempts=5, + 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_lan.py b/tests/topotests/ospf_basic_functionality/test_ospf_lan.py new file mode 100644 index 0000000000..f261104206 --- /dev/null +++ b/tests/topotests/ospf_basic_functionality/test_ospf_lan.py @@ -0,0 +1,724 @@ +#!/usr/bin/python + +# +# Copyright (c) 2020 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 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, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# 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, + create_interfaces_cfg, + write_test_footer, + reset_config_on_routers, + verify_rib, + create_static_routes, + check_address_types, + step, + create_route_maps, + shutdown_bringup_interface, + stop_router, + start_router, + topo_daemons +) +from lib.bgp import verify_bgp_convergence, create_router_bgp +from lib.topolog import logger +from lib.topojson import build_topo_from_json, build_config_from_json +from lib.ospf import ( + verify_ospf_neighbor, + config_ospf_interface, + clear_ospf, + verify_ospf_rib, + create_router_ospf, + verify_ospf_interface, +) +from ipaddress import IPv4Address + +# Global variables +topo = None +# Reading the data from JSON File for topology creation +jsonFile = "{}/ospf_lan.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +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", + ] +} + +""" +Topology: + + Please view in a fixed-width font such as Courier. + Topo : Broadcast Networks + +---+ +---+ +---+ +---+ + |R0 + +R1 + +R2 + +R3 | + +-+-+ +-+-+ +-+-+ +-+-+ + | | | | + | | | | + --+-----------+--------------+---------------+----- + Ethernet Segment + +Testcases: +1. OSPF Hello protocol - Verify DR BDR Elections +2. OSPF IFSM -Verify state change events on DR / BDR / DR Other + + """ + + +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 + 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) + # Api call verify whether OSPF is converged + ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + 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 red_static(dut, config=True): + """Local def for Redstribute static routes inside ospf.""" + global topo + tgen = get_topogen() + if config: + ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "static"}]}}} + else: + ospf_red = { + dut: { + "ospf": { + "redistribute": [{"redist_type": "static", "del_action": True}] + } + } + } + result = create_router_ospf(tgen, topo, ospf_red) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + +def red_connected(dut, config=True): + """Local def for Redstribute connected routes inside ospf.""" + global topo + tgen = get_topogen() + if config: + ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "connected"}]}}} + else: + ospf_red = { + dut: { + "ospf": { + "redistribute": [{"redist_type": "connected", "del_action": True}] + } + } + } + result = create_router_ospf(tgen, topo, ospf_red) + assert result is True, "Testcase: Failed \n Error: {}".format(result) + + +# ################################## +# Test cases start here. +# ################################## + + +def test_ospf_lan_tc1_p0(request): + """ + OSPF Hello protocol - Verify DR BDR Elections + + """ + 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 + step("Bring up the base config as per the topology") + reset_config_on_routers(tgen) + step("Verify that DR BDR DRother are elected in the LAN.") + input_dict = { + "r0": { + "ospf": { + "neighbors": { + "r1": {"state": "Full", "role": "DR"}, + "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) + + step( + "Verify that all the routers are in FULL state with DR and BDR " + "in the topology" + ) + + input_dict = { + "r1": { + "ospf": { + "neighbors": { + "r0": {"state": "Full", "role": "Backup"}, + "r2": {"state": "Full", "role": "DROther"}, + "r3": {"state": "Full", "role": "DROther"}, + } + } + } + } + dut = "r1" + result = verify_ospf_neighbor(tgen, topo, dut, input_dict, lan=True) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Configure DR pririty 100 on R0 and clear ospf neighbors " "on all the routers." + ) + + input_dict = { + "r0": { + "links": { + "s1": { + "interface": topo["routers"]["r0"]["links"]["s1"]["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 ["r0", "r1", "r2", "r3"]: + 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) + + step( + "Configure DR pririty 150 on R0 and clear ospf neighbors " "on all the routers." + ) + + input_dict = { + "r0": { + "links": { + "s1": { + "interface": topo["routers"]["r0"]["links"]["s1"]["interface"], + "ospf": {"priority": 150}, + } + } + } + } + + 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 ["r0", "r1"]: + 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) + + step("Configure DR priority 0 on R0 & Clear ospf nbrs on all the routers") + + input_dict = { + "r0": { + "links": { + "s1": { + "interface": topo["routers"]["r0"]["links"]["s1"]["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 ["r1"]: + clear_ospf(tgen, rtr) + + step("Verify that DR election is triggered and R0 is elected as DRother") + 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) + + step( + "Configure DR priority to default on R0 and Clear ospf neighbors" + " on all the routers" + ) + + input_dict = { + "r0": { + "links": { + "s1": { + "interface": topo["routers"]["r0"]["links"]["s1"]["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 ["r0", "r1"]: + 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) + + step("Shut interface on R0") + dut = "r0" + intf = topo["routers"]["r0"]["links"]["s1"]["interface"] + shutdown_bringup_interface(tgen, dut, intf, False) + + result = verify_ospf_neighbor(tgen, topo, dut, lan=True, expected=False) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("No Shut interface on R0") + dut = "r0" + intf = topo["routers"]["r0"]["links"]["s1"]["interface"] + shutdown_bringup_interface(tgen, dut, intf, True) + + input_dict = { + "r0": { + "ospf": { + "neighbors": { + "r1": {"state": "Full", "role": "DR"}, + "r2": {"state": "Full", "role": "DROther"}, + "r3": {"state": "Full", "role": "DROther"}, + } + } + } + } + step("Verify that after no shut ospf neighbours are full on R0.") + result = verify_ospf_neighbor(tgen, topo, dut, input_dict, lan=True) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Clear ospf on DR router in the topology.") + clear_ospf(tgen, "r0") + + step("Verify that BDR is getting promoted to DR after clear.") + step("Verify that all the nbrs are in FULL state with the elected DR.") + result = verify_ospf_neighbor(tgen, topo, dut, input_dict, lan=True) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Change the ip on LAN intf on R0 to other ip from the same subnet.") + topo_modify_change_ip = deepcopy(topo) + intf_ip = topo_modify_change_ip["routers"]["r0"]["links"]["s1"]["ipv4"] + topo_modify_change_ip["routers"]["r0"]["links"]["s1"]["ipv4"] = str( + IPv4Address(unicode(intf_ip.split("/")[0])) + 3 + ) + "/{}".format(intf_ip.split("/")[1]) + + build_config_from_json(tgen, topo_modify_change_ip, save_bkup=False) + + step( + "Verify that OSPF is in FULL state with other routers with " + "newly configured IP." + ) + result = verify_ospf_neighbor(tgen, topo, dut, input_dict, lan=True) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Change the ospf router id on the R0 and clear ip ospf interface.") + change_rid = {"r0": {"ospf": {"router_id": "100.1.1.100"}}} + + result = create_router_ospf(tgen, topo, change_rid) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + topo["routers"]["r0"]["ospf"]["router_id"] = "100.1.1.100" + step("Reload the FRR router") + + stop_router(tgen, "r0") + start_router(tgen, "r0") + + step( + "Verify that OSPF is in FULL state with other routers with" + " newly configured router id." + ) + input_dict = { + "r1": { + "ospf": { + "neighbors": { + "r0": {"state": "Full", "role": "Backup"}, + "r2": {"state": "Full", "role": "DROther"}, + "r3": {"state": "Full", "role": "DROther"}, + } + } + } + } + dut = "r1" + result = verify_ospf_neighbor(tgen, topo, dut, input_dict, lan=True) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Reconfigure the original router id and clear ip ospf interface.") + change_rid = {"r0": {"ospf": {"router_id": "100.1.1.0"}}} + result = create_router_ospf(tgen, topo, change_rid) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + topo["routers"]["r0"]["ospf"]["router_id"] = "100.1.1.0" + step("Reload the FRR router") + # stop/start -> restart FRR router and verify + stop_router(tgen, "r0") + start_router(tgen, "r0") + + step("Verify that OSPF is enabled with router id previously configured.") + input_dict = { + "r1": { + "ospf": { + "neighbors": { + "r0": {"state": "Full", "role": "Backup"}, + "r2": {"state": "Full", "role": "DROther"}, + "r3": {"state": "Full", "role": "DROther"}, + } + } + } + } + dut = "r1" + result = verify_ospf_neighbor(tgen, topo, dut, input_dict, lan=True) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_ospf_lan_tc2_p0(request): + """ + OSPF IFSM -Verify state change events on DR / BDR / DR Other + + """ + 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 + step("Bring up the base config as per the topology") + reset_config_on_routers(tgen) + step( + "Verify that OSPF is subscribed to multi cast services " + "(All SPF, all DR Routers)." + ) + step("Verify that interface is enabled in ospf.") + dut = "r0" + input_dict = { + "r0": { + "links": { + "s1": { + "ospf": { + "priority": 98, + "timerDeadSecs": 4, + "area": "0.0.0.3", + "mcastMemberOspfDesignatedRouters": True, + "mcastMemberOspfAllRouters": True, + "ospfEnabled": True, + } + } + } + } + } + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Delete the ip address") + topo1 = { + "r0": { + "links": { + "r3": { + "ipv4": topo["routers"]["r0"]["links"]["s1"]["ipv4"], + "interface": topo["routers"]["r0"]["links"]["s1"]["interface"], + "delete": True, + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Change the ip on the R0 interface") + + topo_modify_change_ip = deepcopy(topo) + intf_ip = topo_modify_change_ip["routers"]["r0"]["links"]["s1"]["ipv4"] + topo_modify_change_ip["routers"]["r0"]["links"]["s1"]["ipv4"] = str( + IPv4Address(unicode(intf_ip.split("/")[0])) + 3 + ) + "/{}".format(intf_ip.split("/")[1]) + + build_config_from_json(tgen, topo_modify_change_ip, save_bkup=False) + step("Verify that interface is enabled in ospf.") + dut = "r0" + input_dict = { + "r0": { + "links": { + "s1": { + "ospf": { + "ipAddress": topo_modify_change_ip["routers"]["r0"]["links"][ + "s1" + ]["ipv4"].split("/")[0], + "ipAddressPrefixlen": int( + topo_modify_change_ip["routers"]["r0"]["links"]["s1"][ + "ipv4" + ].split("/")[1] + ), + } + } + } + } + } + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Modify the mask on the R0 interface") + ip_addr = topo_modify_change_ip["routers"]["r0"]["links"]["s1"]["ipv4"] + mask = topo_modify_change_ip["routers"]["r0"]["links"]["s1"]["ipv4"] + step("Delete the ip address") + topo1 = { + "r0": { + "links": { + "r3": { + "ipv4": ip_addr, + "interface": topo["routers"]["r0"]["links"]["s1"]["interface"], + "delete": True, + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Change the ip on the R0 interface") + + topo_modify_change_ip = deepcopy(topo) + intf_ip = topo_modify_change_ip["routers"]["r0"]["links"]["s1"]["ipv4"] + topo_modify_change_ip["routers"]["r0"]["links"]["s1"]["ipv4"] = str( + IPv4Address(unicode(intf_ip.split("/")[0])) + 3 + ) + "/{}".format(int(intf_ip.split("/")[1]) + 1) + + build_config_from_json(tgen, topo_modify_change_ip, save_bkup=False) + step("Verify that interface is enabled in ospf.") + dut = "r0" + input_dict = { + "r0": { + "links": { + "s1": { + "ospf": { + "ipAddress": topo_modify_change_ip["routers"]["r0"]["links"][ + "s1" + ]["ipv4"].split("/")[0], + "ipAddressPrefixlen": int( + topo_modify_change_ip["routers"]["r0"]["links"]["s1"][ + "ipv4" + ].split("/")[1] + ), + } + } + } + } + } + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Change the area id on the interface") + input_dict = { + "r0": { + "links": { + "s1": { + "interface": topo["routers"]["r0"]["links"]["s1"]["interface"], + "ospf": {"area": "0.0.0.3"}, + "delete": True, + } + } + } + } + + result = create_interfaces_cfg(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + input_dict = { + "r0": { + "links": { + "s1": { + "interface": topo["routers"]["r0"]["links"]["s1"]["interface"], + "ospf": {"area": "0.0.0.2"}, + } + } + } + } + + result = create_interfaces_cfg(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + step("Verify that interface is enabled in ospf.") + dut = "r0" + input_dict = { + "r0": {"links": {"s1": {"ospf": {"area": "0.0.0.2", "ospfEnabled": True}}}} + } + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) + 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/ospf_basic_functionality/test_ospf_nssa.py b/tests/topotests/ospf_basic_functionality/test_ospf_nssa.py new file mode 100644 index 0000000000..ff4399f19e --- /dev/null +++ b/tests/topotests/ospf_basic_functionality/test_ospf_nssa.py @@ -0,0 +1,336 @@ +#!/usr/bin/python + +# +# Copyright (c) 2020 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 ipaddress +from lib.ospf import ( + verify_ospf_neighbor, + config_ospf_interface, + clear_ospf, + verify_ospf_rib, + create_router_ospf, + verify_ospf_interface, +) +from lib.topojson import build_topo_from_json, build_config_from_json +from lib.topolog import logger +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 +) +from ipaddress import IPv4Address +from lib.topogen import Topogen, get_topogen +from mininet.topo import Topo +import os +import sys +import time +import pytest +import json + +# 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, "../lib/")) +# pylint: disable=C0413 +# Import topogen and topotest helpers + +# Global variables +topo = None +# Reading the data from JSON File for topology creation +jsonFile = "{}/ospf_nssa.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) +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", + ] +} +""" +TOPOOLOGY = + Please view in a fixed-width font such as Courier. + +---+ A1 +---+ + +R1 +------------+R2 | + +-+-+- +--++ + | -- -- | + | -- A0 -- | + A0| ---- | + | ---- | A2 + | -- -- | + | -- -- | + +-+-+- +-+-+ + +R0 +-------------+R3 | + +---+ A3 +---+ + + + +TESTCASES = +1. OSPF Learning - Verify OSPF can learn different types of LSA and + processes them.[Edge learning different types of LSAs] +2. Verify that ospf non back bone area can be configured as NSSA area +3. Verify that ospf NSSA area DUT is capable receiving & processing + Type7 N2 route. +""" + + +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 + 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) + # 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 + ) + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + 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() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +def red_static(dut, config=True): + """Local def for Redstribute static routes inside ospf.""" + global topo + tgen = get_topogen() + if config: + ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "static"}]}}} + else: + ospf_red = { + dut: {"ospf": {"redistribute": [{"redist_type": "static", "delete": True}]}} + } + result = create_router_ospf(tgen, topo, ospf_red) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + +def red_connected(dut, config=True): + """Local def for Redstribute connected routes inside ospf.""" + global topo + tgen = get_topogen() + if config: + ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "connected"}]}}} + else: + ospf_red = { + dut: { + "ospf": { + "redistribute": [{"redist_type": "connected", "del_action": True}] + } + } + } + result = create_router_ospf(tgen, topo, ospf_red) + assert result is True, "Testcase: Failed \n Error: {}".format(result) + + +# ################################## +# Test cases start here. +# ################################## + + +def test_ospf_learning_tc15_p0(request): + """Verify OSPF can learn different types of LSA and processes them. + + OSPF Learning : Edge learning different types of LSAs. + """ + 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 + step("Bring up the base config as per the topology") + step("Configure area 1 as NSSA Area") + + reset_config_on_routers(tgen) + + step("Verify that Type 3 summary LSA is originated for the same Area 0") + ip = topo["routers"]["r1"]["links"]["r3-link0"]["ipv4"] + ip_net = str(ipaddress.ip_interface(u"{}".format(ip)).network) + + dut = "r0" + input_dict = { + "r1": { + "static_routes": [{"network": ip_net, "no_of_ip": 1, "routeType": "N IA"}] + } + } + + dut = "r0" + result = verify_ospf_rib(tgen, dut, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + protocol = "ospf" + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + input_dict = { + "r2": { + "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("Redistribute static route in R2 ospf.") + dut = "r2" + red_static(dut) + + step("Verify that Type 5 LSA is originated by R2.") + dut = "r0" + protocol = "ospf" + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify that R0 receives Type 4 summary LSA.") + dut = "r0" + input_dict = { + "r1": { + "static_routes": [ + {"network": NETWORK["ipv4"][0], "no_of_ip": 1, "routeType": "N E2"} + ] + } + } + + dut = "r1" + 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) + + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("Change area 1 as non nssa area (on the fly changing area" " type on DUT).") + + for rtr in ["r1", "r2", "r3"]: + input_dict = { + rtr: {"ospf": {"area": [{"id": "0.0.0.2", "type": "nssa", "delete": True}]}} + } + result = create_router_ospf(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that OSPF neighbours are reset after changing area type.") + step("Verify that ABR R2 originates type 5 LSA in area 1.") + step("Verify that route is calculated and installed in R1.") + + input_dict = { + "r1": { + "static_routes": [ + {"network": NETWORK["ipv4"][0], "no_of_ip": 1, "routeType": "N E2"} + ] + } + } + + dut = "r1" + 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) + + 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_routemaps.py b/tests/topotests/ospf_basic_functionality/test_ospf_routemaps.py new file mode 100644 index 0000000000..6ebc74a013 --- /dev/null +++ b/tests/topotests/ospf_basic_functionality/test_ospf_routemaps.py @@ -0,0 +1,540 @@ +#!/usr/bin/python + +# +# Copyright (c) 2020 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 copy import deepcopy + +# 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, "../lib/")) + +# 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, + create_prefix_lists, + verify_rib, + create_static_routes, + check_address_types, + step, + create_route_maps, + verify_prefix_lists, + topo_daemons +) +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_rib, + create_router_ospf, + verify_ospf_database, +) + +# Global variables +topo = None +# Reading the data from JSON File for topology creation +jsonFile = "{}/ospf_routemaps.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +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", + ] +} +routerids = ["100.1.1.0", "100.1.1.1", "100.1.1.2", "100.1.1.3"] + +""" +TOPOOLOGY = + Please view in a fixed-width font such as Courier. + +---+ A1 +---+ + +R1 +------------+R2 | + +-+-+- +--++ + | -- -- | + | -- A0 -- | + A0| ---- | + | ---- | A2 + | -- -- | + | -- -- | + +-+-+- +-+-+ + +R0 +-------------+R3 | + +---+ A3 +---+ + +TESTCASES = +1. OSPF Route map - Verify OSPF route map support functionality. +2. Verify OSPF route map support functionality when route map is not + configured at system level but configured in OSPF +3. Verify OSPF route map support functionality with set/match clauses + /call/continue/goto in a route-map to see if it takes immediate effect. +4. Verify OSPF route map support functionality + when route map actions are toggled. +5. Verify OSPF route map support functionality with multiple sequence + numbers in a single route-map for different match/set clauses. +6. Verify OSPF route map support functionality when we add/remove route-maps + with multiple set clauses and without any match statement.(Set only) +7. Verify OSPF route map support functionality when we + add/remove route-maps with multiple match clauses and without + any set statement.(Match only) + """ + + +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 + 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) + # 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 + ) + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + 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() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +# ################################## +# Test cases start here. +# ################################## + + +def test_ospf_routemaps_functionality_tc20_p0(request): + """ + OSPF route map support functionality. + + Verify OSPF route map support functionality when route map is not + configured at system level but configured in OSPF + + """ + 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 and 10.0.20.2/32) in R0") + # 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("Redistribute to ospf using route map ( non existent route map)") + ospf_red_r1 = { + "r0": { + "ospf": { + "redistribute": [{"redist_type": "static", "route_map": "rmap_ipv4"}] + } + } + } + result = create_router_ospf(tgen, topo, ospf_red_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that routes are not allowed in OSPF even tough no " + "matching routing map is configured." + ) + + dut = "r1" + protocol = "ospf" + result = verify_ospf_rib(tgen, dut, input_dict, attempts=2, 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, attempts=2, expected=False + ) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "configure the route map with the same name that is used " + "in the ospf with deny rule." + ) + + # Create route map + routemaps = {"r0": {"route_maps": {"rmap_ipv4": [{"action": "deny"}]}}} + result = create_route_maps(tgen, routemaps) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("verify that now route map is activated & routes are denied in OSPF.") + 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 + ) + + # Create route map + routemaps = {"r0": {"route_maps": {"rmap_ipv4": [{"action": "deny"}]}}} + result = create_route_maps(tgen, routemaps) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("verify that now route map is activated & routes are denied in OSPF.") + 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 + ) + + step("Delete the route map.") + # Create route map + routemaps = { + "r0": {"route_maps": {"rmap_ipv4": [{"action": "deny", "delete": True}]}} + } + result = create_route_maps(tgen, routemaps) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that routes are allowed in OSPF even tough " + "no matching routing map is configured." + ) + 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_tc24_p0(request): + """ + OSPF Route map - Multiple set clauses. + + Verify OSPF route map support functionality when we + add/remove route-maps with multiple match clauses and without + any set statement.(Match only) + + """ + 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": 1, "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) + + # Create ip prefix list + pfx_list = { + "r0": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + {"seqid": 10, "network": "any", "action": "permit"} + ] + } + } + } + } + result = create_prefix_lists(tgen, pfx_list) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("verify that prefix-list is created in R0.") + result = verify_prefix_lists(tgen, pfx_list) + assert result is not True, ( + "Testcase {} : Failed \n Prefix list not " + "present. Error: {}".format(tc_name, result) + ) + + # Create route map + routemaps = { + "r0": { + "route_maps": { + "rmap_ipv4": [ + { + "action": "permit", + "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}}, + } + ] + } + } + } + result = create_route_maps(tgen, routemaps) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify that metric falls back to original metric for ospf routes.") + 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( + "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"][1], + "no_of_ip": 1, + "next_hop": "Null0", + "tag": 1000, + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create ip prefix list + pfx_list = { + "r0": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + {"seqid": 10, "network": "any", "action": "permit"} + ] + } + } + } + } + result = create_prefix_lists(tgen, pfx_list) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("verify that prefix-list is created in R0.") + result = verify_prefix_lists(tgen, pfx_list) + assert result is not True, ( + "Testcase {} : Failed \n Prefix list not " + "present. Error: {}".format(tc_name, result) + ) + + # Create route map + routemaps = { + "r0": { + "route_maps": { + "rmap_ipv4": [{"action": "permit", "match": {"ipv4": {"tag": "1000"}}}] + } + } + } + result = create_route_maps(tgen, routemaps) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify that metric falls back to original metric for ospf routes.") + 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("Delete the match clause with tag in route map") + # Create route map + routemaps = { + "r0": { + "route_maps": { + "rmap_ipv4": [ + { + "action": "permit", + "match": {"ipv4": {"tag": "1000", "delete": True}}, + } + ] + } + } + } + result = create_route_maps(tgen, routemaps) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify that metric falls back to original metric for ospf routes.") + 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("Delete the match clause with metric in route map.") + + # Create route map + routemaps = { + "r0": { + "route_maps": { + "rmap_ipv4": [ + { + "action": "permit", + "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}}, + } + ] + } + } + } + result = create_route_maps(tgen, routemaps) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + 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) + + 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_rte_calc.py b/tests/topotests/ospf_basic_functionality/test_ospf_rte_calc.py new file mode 100644 index 0000000000..2c6bcf0162 --- /dev/null +++ b/tests/topotests/ospf_basic_functionality/test_ospf_rte_calc.py @@ -0,0 +1,612 @@ +#!/usr/bin/python + +# +# Copyright (c) 2020 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 ipaddress +import json + +# 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, "../lib/")) + +# 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, + create_interfaces_cfg, + write_test_footer, + reset_config_on_routers, + verify_rib, + create_static_routes, + step, + shutdown_bringup_interface, + topo_daemons +) +from lib.bgp import verify_bgp_convergence, create_router_bgp +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_rib, + create_router_ospf, +) + +# Global variables +topo = None + +# number of retries. +nretry = 5 + +# Reading the data from JSON File for topology creation +jsonFile = "{}/ospf_rte_calc.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +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", + ] +} +TOPOOLOGY = """ + Please view in a fixed-width font such as Courier. + +---+ A1 +---+ + +R1 +------------+R2 | + +-+-+- +--++ + | -- -- | + | -- A0 -- | + A0| ---- | + | ---- | A2 + | -- -- | + | -- -- | + +-+-+- +-+-+ + +R0 +-------------+R3 | + +---+ A3 +---+ +""" + +TESTCASES = """ +1. Test OSPF intra area route calculations. +2. Test OSPF inter area route calculations. +3. Test OSPF redistribution of connected routes. +""" + + +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 + 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) + # 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 + ) + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + 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() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +def red_static(dut, config=True): + """Local def for Redstribute static routes inside ospf.""" + global topo + tgen = get_topogen() + if config: + ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "static"}]}}} + else: + ospf_red = { + dut: { + "ospf": { + "redistribute": [{"redist_type": "static", "del_action": True}] + } + } + } + result = create_router_ospf(tgen, topo, ospf_red) + assert result is True, "Testcase : Failed \n Error: {}".format(result) + + +def red_connected(dut, config=True): + """Local def for Redstribute connected routes inside ospf.""" + global topo + tgen = get_topogen() + if config: + ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "connected"}]}}} + else: + ospf_red = { + dut: { + "ospf": { + "redistribute": [{"redist_type": "connected", "del_action": True}] + } + } + } + result = create_router_ospf(tgen, topo, ospf_red) + assert result is True, "Testcase: Failed \n Error: {}".format(result) + + +# ################################## +# Test cases start here. +# ################################## + + +def test_ospf_redistribution_tc5_p0(request): + """Test OSPF intra area route calculations.""" + 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 + step("Bring up the base config.") + reset_config_on_routers(tgen) + + step("Verify that OSPF neighbors are FULL.") + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("verify intra area route is calculated for r0-r3 interface ip in R1") + ip = topo["routers"]["r0"]["links"]["r3"]["ipv4"] + ip_net = str(ipaddress.ip_interface(u"{}".format(ip)).network) + nh = topo["routers"]["r0"]["links"]["r1"]["ipv4"].split("/")[0] + input_dict = { + "r1": {"static_routes": [{"network": ip_net, "no_of_ip": 1, "routeType": "N"}]} + } + + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + protocol = "ospf" + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Delete the ip address on newly configured interface of R0") + topo1 = { + "r0": { + "links": { + "r3": { + "ipv4": topo["routers"]["r0"]["links"]["r3"]["ipv4"], + "interface": topo["routers"]["r0"]["links"]["r3"]["interface"], + "delete": True, + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + dut = "r1" + for num in range(0, nretry): + result = verify_ospf_rib( + tgen, dut, input_dict, next_hop=nh, expected=False) + if result is not True: + break + + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + protocol = "ospf" + result = verify_rib( + tgen, + "ipv4", + dut, + input_dict, + protocol=protocol, + next_hop=nh, + attempts=5, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Add back the deleted ip address on newly configured interface of R0") + topo1 = { + "r0": { + "links": { + "r3": { + "ipv4": topo["routers"]["r0"]["links"]["r3"]["ipv4"], + "interface": topo["routers"]["r0"]["links"]["r3"]["interface"], + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + protocol = "ospf" + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Shut no shut interface on R0") + dut = "r0" + intf = topo["routers"]["r0"]["links"]["r3"]["interface"] + shutdown_bringup_interface(tgen, dut, intf, False) + + step("un shut the OSPF interface on R0") + dut = "r0" + shutdown_bringup_interface(tgen, dut, intf, True) + + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + protocol = "ospf" + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_ospf_redistribution_tc6_p0(request): + """Test OSPF inter area route calculations.""" + 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 + step("Bring up the base config.") + reset_config_on_routers(tgen) + + step("Verify that OSPF neighbors are FULL.") + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("verify intra area route is calculated for r0-r3 interface ip in R1") + ip = topo["routers"]["r0"]["links"]["r3"]["ipv4"] + ip_net = str(ipaddress.ip_interface(u"{}".format(ip)).network) + nh = topo["routers"]["r0"]["links"]["r1"]["ipv4"].split("/")[0] + input_dict = { + "r1": {"static_routes": [{"network": ip_net, "no_of_ip": 1, "routeType": "N"}]} + } + + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + protocol = "ospf" + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Delete the ip address on newly configured loopback of R0") + topo1 = { + "r0": { + "links": { + "r3": { + "ipv4": topo["routers"]["r0"]["links"]["r3"]["ipv4"], + "interface": topo["routers"]["r0"]["links"]["r3"]["interface"], + "delete": True, + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + dut = "r1" + for num in range(0, nretry): + result = verify_ospf_rib( + tgen, dut, input_dict, next_hop=nh, expected=False) + if result is not True: + break + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + protocol = "ospf" + result = verify_rib( + tgen, + "ipv4", + dut, + input_dict, + protocol=protocol, + next_hop=nh, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Add back the deleted ip address on newly configured interface of R0") + topo1 = { + "r0": { + "links": { + "r3": { + "ipv4": topo["routers"]["r0"]["links"]["r3"]["ipv4"], + "interface": topo["routers"]["r0"]["links"]["r3"]["interface"], + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + protocol = "ospf" + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Shut no shut interface on R0") + dut = "r0" + intf = topo["routers"]["r0"]["links"]["r3"]["interface"] + shutdown_bringup_interface(tgen, dut, intf, False) + + step("un shut the OSPF interface on R0") + dut = "r0" + shutdown_bringup_interface(tgen, dut, intf, True) + + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + protocol = "ospf" + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_ospf_redistribution_tc8_p1(request): + """ + Test OSPF redistribution of connected routes. + + Verify OSPF redistribution of connected routes when bgp multi hop + neighbor is configured using ospf routes + + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + global topo + step("Bring up the base config.") + step( + "Configure loopback interface on all routers, and redistribut" + "e connected routes into ospf" + ) + reset_config_on_routers(tgen) + + step( + "verify that connected routes -loopback is found in all routers" + "advertised/exchaged via ospf" + ) + for rtr in topo["routers"]: + red_static(rtr) + red_connected(rtr) + for node in topo["routers"]: + input_dict = { + "r0": { + "static_routes": [ + { + "network": topo["routers"][node]["links"]["lo"]["ipv4"], + "no_of_ip": 1, + } + ] + } + } + for rtr in topo["routers"]: + result = verify_rib(tgen, "ipv4", rtr, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure E BGP multi hop using the loopback addresses.") + as_num = 100 + for node in topo["routers"]: + as_num += 1 + topo["routers"][node].update( + { + "bgp": { + "local_as": as_num, + "address_family": {"ipv4": {"unicast": {"neighbor": {}}}}, + } + } + ) + for node in topo["routers"]: + for rtr in topo["routers"]: + if node is not rtr: + topo["routers"][node]["bgp"]["address_family"]["ipv4"]["unicast"][ + "neighbor" + ].update( + { + rtr: { + "dest_link": { + "lo": {"source_link": "lo", "ebgp_multihop": 2} + } + } + } + ) + + result = create_router_bgp(tgen, topo, topo["routers"]) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify that BGP neighbor is ESTABLISHED") + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + step( + "Configure couple of static routes in R0 and " + "Redistribute static routes in R1 bgp." + ) + + for rtr in topo["routers"]: + ospf_red = { + rtr: {"ospf": {"redistribute": [{"redist_type": "static", "delete": True}]}} + } + result = create_router_ospf(tgen, topo, ospf_red) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + 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) + + configure_bgp_on_r0 = { + "r0": { + "bgp": { + "address_family": { + "ipv4": {"unicast": {"redistribute": [{"redist_type": "static"}]}} + } + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp_on_r0) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + protocol = "bgp" + for rtr in ["r1", "r2", "r3"]: + result = verify_rib(tgen, "ipv4", rtr, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Clear ospf neighbours in R0") + for rtr in topo["routers"]: + clear_ospf(tgen, rtr) + + step("Verify that OSPF neighbours are reset and forms new adjacencies.") + # 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 BGP neighbours are reset and forms new adjacencies.") + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + protocol = "bgp" + for rtr in ["r1", "r2", "r3"]: + result = verify_rib(tgen, "ipv4", rtr, input_dict, 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/ospf_basic_functionality/test_ospf_single_area.py b/tests/topotests/ospf_basic_functionality/test_ospf_single_area.py new file mode 100644 index 0000000000..5a141224f1 --- /dev/null +++ b/tests/topotests/ospf_basic_functionality/test_ospf_single_area.py @@ -0,0 +1,781 @@ +#!/usr/bin/python + +# +# Copyright (c) 2020 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 copy import deepcopy +from ipaddress import IPv4Address + +# 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, "../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from mininet.topo import Topo +from lib.topogen import Topogen, get_topogen +import ipaddress + +# 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 +) +from lib.topolog import logger +from lib.topojson import build_topo_from_json, build_config_from_json + +from lib.ospf import ( + verify_ospf_neighbor, + config_ospf_interface, + clear_ospf, + verify_ospf_rib, + create_router_ospf, + verify_ospf_interface, + verify_ospf_database, +) + +# Global variables +topo = None + +# Reading the data from JSON File for topology creation +jsonFile = "{}/ospf_single_area.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +""" +TOPOOLOGY = + Please view in a fixed-width font such as Courier. + +---+ A1 +---+ + +R1 +------------+R2 | + +-+-+- +--++ + | -- -- | + | -- A0 -- | + A0| ---- | + | ---- | A2 + | -- -- | + | -- -- | + +-+-+- +-+-+ + +R0 +-------------+R3 | + +---+ A3 +---+ + +TESTCASES = +1. OSPF IFSM -Verify state change events on p2p network. +2. OSPF Timers - Verify OSPF interface timer hello interval functionality +3. OSPF Timers - Verify OSPF interface timer dead interval functionality +4. Verify ospf show commands with json output. + """ + + +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 + 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) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + 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() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +# ################################## +# Test cases start here. +# ################################## + + +def test_ospf_p2p_tc3_p0(request): + """OSPF IFSM -Verify state change events on p2p network.""" + 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 + step("Bring up the base config as per the topology") + reset_config_on_routers(tgen) + step( + "Verify that OSPF is subscribed to multi cast services " + "(All SPF, all DR Routers)." + ) + step("Verify that interface is enabled in ospf.") + step("Verify that config is successful.") + dut = "r0" + input_dict = { + "r0": { + "links": { + "r3": {"ospf": {"mcastMemberOspfAllRouters": True, "ospfEnabled": True}} + } + } + } + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Delete the ip address") + topo1 = { + "r0": { + "links": { + "r3": { + "ipv4": topo["routers"]["r0"]["links"]["r3"]["ipv4"], + "interface": topo["routers"]["r0"]["links"]["r3"]["interface"], + "delete": True, + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Change the ip on the R0 interface") + + topo_modify_change_ip = deepcopy(topo) + intf_ip = topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv4"] + topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv4"] = str( + IPv4Address(unicode(intf_ip.split("/")[0])) + 3 + ) + "/{}".format(intf_ip.split("/")[1]) + + build_config_from_json(tgen, topo_modify_change_ip, save_bkup=False) + step("Verify that interface is enabled in ospf.") + dut = "r0" + input_dict = { + "r0": { + "links": { + "r3": { + "ospf": { + "ipAddress": topo_modify_change_ip["routers"]["r0"]["links"][ + "r3" + ]["ipv4"].split("/")[0], + "ipAddressPrefixlen": int( + topo_modify_change_ip["routers"]["r0"]["links"]["r3"][ + "ipv4" + ].split("/")[1] + ), + } + } + } + } + } + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Modify the mask on the R0 interface") + ip_addr = topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv4"] + mask = topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv4"] + step("Delete the ip address") + topo1 = { + "r0": { + "links": { + "r3": { + "ipv4": ip_addr, + "interface": topo["routers"]["r0"]["links"]["r3"]["interface"], + "delete": True, + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Change the ip on the R0 interface") + + topo_modify_change_ip = deepcopy(topo) + intf_ip = topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv4"] + topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv4"] = str( + IPv4Address(unicode(intf_ip.split("/")[0])) + 3 + ) + "/{}".format(int(intf_ip.split("/")[1]) + 1) + + build_config_from_json(tgen, topo_modify_change_ip, save_bkup=False) + step("Verify that interface is enabled in ospf.") + dut = "r0" + input_dict = { + "r0": { + "links": { + "r3": { + "ospf": { + "ipAddress": topo_modify_change_ip["routers"]["r0"]["links"][ + "r3" + ]["ipv4"].split("/")[0], + "ipAddressPrefixlen": int( + topo_modify_change_ip["routers"]["r0"]["links"]["r3"][ + "ipv4" + ].split("/")[1] + ), + } + } + } + } + } + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + topo1 = { + "r0": { + "links": { + "r3": { + "ipv4": topo_modify_change_ip["routers"]["r0"]["links"]["r3"][ + "ipv4" + ], + "interface": topo_modify_change_ip["routers"]["r0"]["links"]["r3"][ + "interface" + ], + "delete": True, + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + build_config_from_json(tgen, topo, save_bkup=False) + + step("Change the area id on the interface") + input_dict = { + "r0": { + "links": { + "r3": { + "interface": topo["routers"]["r0"]["links"]["r3"]["interface"], + "ospf": {"area": "0.0.0.0"}, + "delete": True, + } + } + } + } + + result = create_interfaces_cfg(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + input_dict = { + "r0": { + "links": { + "r3": { + "interface": topo["routers"]["r0"]["links"]["r3"]["interface"], + "ospf": {"area": "0.0.0.1"}, + } + } + } + } + + result = create_interfaces_cfg(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + step("Verify that interface is enabled in ospf.") + dut = "r0" + input_dict = { + "r0": {"links": {"r3": {"ospf": {"area": "0.0.0.1", "ospfEnabled": True}}}} + } + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + input_dict = { + "r0": { + "links": { + "r3": { + "interface": topo["routers"]["r0"]["links"]["r3"]["interface"], + "ospf": {"area": "0.0.0.1"}, + "delete": True, + } + } + } + } + + result = create_interfaces_cfg(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + input_dict = { + "r0": { + "links": { + "r3": { + "interface": topo["routers"]["r0"]["links"]["r3"]["interface"], + "ospf": {"area": "0.0.0.0"}, + } + } + } + } + + result = create_interfaces_cfg(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + # Api call verify whether BGP is converged + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + write_test_footer(tc_name) + + +def test_ospf_hello_tc10_p0(request): + """ + OSPF timers. + + Verify OSPF interface timer hello interval functionality + """ + 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 + step("Bring up the base config as per the topology") + reset_config_on_routers(tgen) + + step("modify hello timer from default value to some other value on r1") + + topo1 = { + "r1": { + "links": { + "r0": { + "interface": topo["routers"]["r1"]["links"]["r0"]["interface"], + "ospf": {"hello_interval": 11, "dead_interval": 12}, + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "verify that new timer value is configured and applied using " + "the show ip ospf interface command." + ) + dut = "r1" + input_dict = { + "r1": { + "links": {"r0": {"ospf": {"timerMsecs": 11 * 1000, "timerDeadSecs": 12}}} + } + } + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("modify hello timer from default value to r1 hello timer on r2") + + topo1 = { + "r0": { + "links": { + "r1": { + "interface": topo["routers"]["r0"]["links"]["r1"]["interface"], + "ospf": {"hello_interval": 11, "dead_interval": 12}, + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("verify that new timer value is configured.") + input_dict = { + "r0": { + "links": {"r1": {"ospf": {"timerMsecs": 11 * 1000, "timerDeadSecs": 12}}} + } + } + dut = "r0" + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("verify that ospf neighbours are full") + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("reconfigure the default hello timer value to default on r1 and r2") + + topo1 = { + "r0": { + "links": { + "r1": { + "interface": topo["routers"]["r0"]["links"]["r1"]["interface"], + "ospf": {"hello_interval": 10, "dead_interval": 40}, + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + topo1 = { + "r1": { + "links": { + "r0": { + "interface": topo["routers"]["r1"]["links"]["r0"]["interface"], + "ospf": {"hello_interval": 10, "dead_interval": 40}, + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("verify that new timer value is configured.") + input_dict = { + "r0": { + "links": {"r1": {"ospf": {"timerMsecs": 10 * 1000, "timerDeadSecs": 40}}} + } + } + dut = "r0" + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("verify that ospf neighbours are full") + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("reconfigure the default hello timer value to default on r1 and r2") + + topo1 = { + "r0": { + "links": { + "r1": { + "interface": topo["routers"]["r0"]["links"]["r1"]["interface"], + "ospf": {"hello_interval": 10, "dead_interval": 40}, + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + topo1 = { + "r1": { + "links": { + "r0": { + "interface": topo["routers"]["r1"]["links"]["r0"]["interface"], + "ospf": {"hello_interval": 10, "dead_interval": 40}, + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("verify that new timer value is configured.") + input_dict = { + "r0": { + "links": {"r1": {"ospf": {"timerMsecs": 10 * 1000, "timerDeadSecs": 40}}} + } + } + dut = "r0" + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("verify that ospf neighbours are full") + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step("configure hello timer = 1 on r1 and r2") + topo1 = { + "r0": { + "links": { + "r1": { + "interface": topo["routers"]["r0"]["links"]["r1"]["interface"], + "ospf": {"hello_interval": 1, "dead_interval": 4}, + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + topo1 = { + "r1": { + "links": { + "r0": { + "interface": topo["routers"]["r1"]["links"]["r0"]["interface"], + "ospf": {"hello_interval": 1, "dead_interval": 4}, + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("verify that new timer value is configured.") + input_dict = { + "r0": {"links": {"r1": {"ospf": {"timerMsecs": 1 * 1000, "timerDeadSecs": 4}}}} + } + dut = "r0" + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("verify that ospf neighbours are full") + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step(" Configure hello timer = 65535") + topo1 = { + "r0": { + "links": { + "r1": { + "interface": topo["routers"]["r0"]["links"]["r1"]["interface"], + "ospf": {"hello_interval": 65535, "dead_interval": 4}, + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + topo1 = { + "r1": { + "links": { + "r0": { + "interface": topo["routers"]["r1"]["links"]["r0"]["interface"], + "ospf": {"hello_interval": 65535, "dead_interval": 4}, + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("verify that new timer value is configured.") + input_dict = { + "r0": { + "links": {"r1": {"ospf": {"timerMsecs": 65535 * 1000, "timerDeadSecs": 4}}} + } + } + dut = "r0" + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("verify that ospf neighbours are full") + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + + step(" Try configuring timer values outside range for example 65536") + topo1 = { + "r0": { + "links": { + "r1": { + "interface": topo["routers"]["r0"]["links"]["r1"]["interface"], + "ospf": {"hello_interval": 65536, "dead_interval": 4}, + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Unconfigure the hello timer from the interface from r1 and r2.") + + topo1 = { + "r1": { + "links": { + "r0": { + "interface": topo["routers"]["r1"]["links"]["r0"]["interface"], + "ospf": {"hello_interval": 65535}, + "delete": True, + } + } + } + } + + result = create_interfaces_cfg(tgen, topo1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that timer value is deleted from intf & " "set to default value 40 sec." + ) + input_dict = {"r1": {"links": {"r0": {"ospf": {"timerMsecs": 10 * 1000}}}}} + dut = "r1" + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_ospf_show_p1(request): + """Verify ospf show commands with json output.""" + 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 + step(" Bring up the base config as per the topology") + reset_config_on_routers(tgen) + + ospf_covergence = verify_ospf_neighbor(tgen, topo) + assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf_covergence + ) + dut = "r1" + input_dict = { + "r1": { + "links": { + "r0": { + "ospf": { + "ifUp": True, + "ifFlags": "<UP,BROADCAST,RUNNING,MULTICAST>", + "ospfEnabled": True, + "ipAddressPrefixlen": 24, + "ospfIfType": "Broadcast", + "area": "0.0.0.0", + "networkType": "BROADCAST", + "cost": 10, + "transmitDelaySecs": 1, + "state": "DR", + "priority": 1, + "mcastMemberOspfAllRouters": True, + "timerMsecs": 1000, + "timerDeadSecs": 4, + "timerWaitSecs": 4, + "timerRetransmitSecs": 5, + "nbrCount": 1, + "nbrAdjacentCount": 1, + } + } + } + } + } + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # show ip ospf route + ip = topo["routers"]["r0"]["links"]["r3"]["ipv4"] + ip_net = str(ipaddress.ip_interface(u"{}".format(ip)).network) + nh = topo["routers"]["r0"]["links"]["r1"]["ipv4"].split("/")[0] + input_dict = { + "r1": {"static_routes": [{"network": ip_net, "no_of_ip": 1, "routeType": "N"}]} + } + + dut = "r1" + result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh) + 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/pbr-topo1/test_pbr_topo1.py b/tests/topotests/pbr-topo1/test_pbr_topo1.py index ffac8e2889..9ae4cce360 100755..100644 --- a/tests/topotests/pbr-topo1/test_pbr_topo1.py +++ b/tests/topotests/pbr-topo1/test_pbr_topo1.py @@ -92,7 +92,7 @@ def setup_module(module): pytest.skip(tgen.errors) router_list = tgen.routers() - for rname, router in router_list.iteritems(): + for rname, router in router_list.items(): # Install vrf into the kernel and slave eth3 router.run("ip link add vrf-chiyoda type vrf table 1000") router.run("ip link set dev {}-eth3 master vrf-chiyoda".format(rname)) diff --git a/tests/topotests/pim-basic/r1/bgpd.conf b/tests/topotests/pim-basic/r1/bgpd.conf index 1ca643f758..84d9598bc6 100644 --- a/tests/topotests/pim-basic/r1/bgpd.conf +++ b/tests/topotests/pim-basic/r1/bgpd.conf @@ -1,4 +1,5 @@ router bgp 65001 no bgp ebgp-requires-policy neighbor 10.0.30.3 remote-as external + neighbor 10.0.30.3 timers 3 10 redistribute connected diff --git a/tests/topotests/pim-basic/rp/bgpd.conf b/tests/topotests/pim-basic/rp/bgpd.conf index 451799288a..1bfae6059b 100644 --- a/tests/topotests/pim-basic/rp/bgpd.conf +++ b/tests/topotests/pim-basic/rp/bgpd.conf @@ -1,4 +1,5 @@ router bgp 65003 no bgp ebgp-requires-policy neighbor 10.0.30.1 remote-as external + neighbor 10.0.30.1 timers 3 10 redistribute connected diff --git a/tests/topotests/pim-basic/test_pim.py b/tests/topotests/pim-basic/test_pim.py index 2abee39176..e8a9f72b48 100644 --- a/tests/topotests/pim-basic/test_pim.py +++ b/tests/topotests/pim-basic/test_pim.py @@ -87,7 +87,7 @@ def setup_module(mod): tgen.start_topology() # For all registered routers, load the zebra configuration file - for rname, router in tgen.routers().iteritems(): + for rname, router in tgen.routers().items(): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ) diff --git a/tests/topotests/route-scale/test_route_scale.py b/tests/topotests/route-scale/test_route_scale.py index 508d1746b3..0bfae3b830 100755..100644 --- a/tests/topotests/route-scale/test_route_scale.py +++ b/tests/topotests/route-scale/test_route_scale.py @@ -86,7 +86,7 @@ def setup_module(module): tgen.start_topology() router_list = tgen.routers() - for rname, router in router_list.iteritems(): + for rname, router in router_list.items(): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ) diff --git a/tests/topotests/zebra_netlink/test_zebra_netlink.py b/tests/topotests/zebra_netlink/test_zebra_netlink.py index 7b692c75ab..3b3c74d502 100755..100644 --- a/tests/topotests/zebra_netlink/test_zebra_netlink.py +++ b/tests/topotests/zebra_netlink/test_zebra_netlink.py @@ -81,7 +81,7 @@ def setup_module(mod): tgen.start_topology() router_list = tgen.routers() - for rname, router in router_list.iteritems(): + for rname, router in router_list.items(): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ) diff --git a/tests/topotests/zebra_rib/test_zebra_rib.py b/tests/topotests/zebra_rib/test_zebra_rib.py index 17eb736cab..13965c63ae 100755..100644 --- a/tests/topotests/zebra_rib/test_zebra_rib.py +++ b/tests/topotests/zebra_rib/test_zebra_rib.py @@ -73,7 +73,7 @@ def setup_module(mod): tgen.start_topology() router_list = tgen.routers() - for rname, router in router_list.iteritems(): + for rname, router in router_list.items(): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ) diff --git a/tools/frr-reload.py b/tools/frr-reload.py index 8ffc313c04..88873da904 100755 --- a/tools/frr-reload.py +++ b/tools/frr-reload.py @@ -128,17 +128,13 @@ class Vtysh(object): % (child.returncode)) def mark_file(self, filename, stdin=None): - kwargs = {} - if stdin is not None: - kwargs['stdin'] = stdin - child = self._call(['-m', '-f', filename], - stdout=subprocess.PIPE, **kwargs) + stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE) try: stdout, stderr = child.communicate() except subprocess.TimeoutExpired: child.kill() - stdout, stderr = proc.communicate() + stdout, stderr = child.communicate() raise VtyshException('vtysh call timed out!') if child.wait() != 0: @@ -1313,8 +1309,12 @@ if __name__ == '__main__': # Create a Config object from the config generated by newconf newconf = Config(vtysh) - newconf.load_from_file(args.filename) - reload_ok = True + try: + newconf.load_from_file(args.filename) + reload_ok = True + except VtyshException as ve: + log.error("vtysh failed to process new configuration: {}".format(ve)) + reload_ok = False if args.test: diff --git a/yang/frr-interface.yang b/yang/frr-interface.yang index dade9f97fe..46c03a1d1f 100644 --- a/yang/frr-interface.yang +++ b/yang/frr-interface.yang @@ -300,11 +300,7 @@ module frr-interface { } leaf vrf { - type string { - length "1..16"; - } - /* yang version 0.16 having issue accessing leafref. */ - /* type frr-vrf:vrf-ref;*/ + type frr-vrf:vrf-ref; description "VRF this interface is associated with."; } diff --git a/yang/frr-isisd.yang b/yang/frr-isisd.yang index 00296516ef..cc959bd9fe 100644 --- a/yang/frr-isisd.yang +++ b/yang/frr-isisd.yang @@ -612,6 +612,26 @@ module frr-isisd { "IPv6 destination-source topology."; } } + + container mpls { + description + "Configuration of MPLS parameters"; + leaf ldp-sync { + type boolean; + default "true"; + description + "Enable MPLS LDP-Sync functionality on this circuit."; + } + leaf holddown { + type uint16 { + range "0..10000"; + } + units "seconds"; + description + "Time to wait for LDP-Sync to occur before restoring interface metric."; + } + } + } grouping adjacency-state { @@ -1324,6 +1344,26 @@ module frr-isisd { } } } + + container mpls { + description + "Configuration of MPLS parameters"; + container ldp-sync { + presence "Present if MPLS LDP-Sync is enabled."; + description + "Enable MPLS LDP-Sync functionality."; + leaf holddown { + type uint16 { + range "0..10000"; + } + units "seconds"; + default "0"; + description + "Time to wait for LDP-Sync to occur before restoring interface metric."; + } + } + } + } } diff --git a/yang/frr-ripd.yang b/yang/frr-ripd.yang index 4e5795b8f7..f5775ab968 100644 --- a/yang/frr-ripd.yang +++ b/yang/frr-ripd.yang @@ -143,7 +143,7 @@ module frr-ripd { "Enable RIP on the specified IP network."; } leaf-list interface { - type string; + type frr-interface:interface-ref; description "Enable RIP on the specified interface."; } @@ -204,14 +204,14 @@ module frr-ripd { } leaf-list passive-interface { when "../passive-default = 'false'"; - type string; + type frr-interface:interface-ref; description "A list of interfaces where the sending of RIP packets is disabled."; } leaf-list non-passive-interface { when "../passive-default = 'true'"; - type string; + type frr-interface:interface-ref; description "A list of interfaces where the sending of RIP packets is enabled."; diff --git a/yang/frr-ripngd.yang b/yang/frr-ripngd.yang index 47ae67b238..52e208b2c9 100644 --- a/yang/frr-ripngd.yang +++ b/yang/frr-ripngd.yang @@ -101,7 +101,7 @@ module frr-ripngd { "Enable RIPng on the specified IPv6 network."; } leaf-list interface { - type string; + type frr-interface:interface-ref; description "Enable RIPng on the specified interface."; } diff --git a/yang/frr-route-map.yang b/yang/frr-route-map.yang index 70a6f4470c..b22a96a740 100644 --- a/yang/frr-route-map.yang +++ b/yang/frr-route-map.yang @@ -249,7 +249,7 @@ module frr-route-map { case interface { when "./condition = 'interface'"; leaf interface { - type string; + type frr-interface:interface-ref; } } diff --git a/yang/frr-zebra.yang b/yang/frr-zebra.yang index 7762c75d68..2efc45c146 100644 --- a/yang/frr-zebra.yang +++ b/yang/frr-zebra.yang @@ -393,9 +393,7 @@ module frr-zebra { } leaf vrf { - type string { - length "1..36"; - } + type frr-vrf:vrf-ref; description "The tenant VRF."; } @@ -715,9 +713,7 @@ module frr-zebra { choice vrf-choice { case single { leaf vrf { - type string { - length "1..36"; - } + type frr-vrf:vrf-ref; description "Retrieve routes in a non-default vrf."; } @@ -815,9 +811,7 @@ module frr-zebra { choice vrf-choice { case single { leaf vrf { - type string { - length "1..36"; - } + type frr-vrf:vrf-ref; description "Retrieve routes in a non-default vrf."; } @@ -853,9 +847,7 @@ module frr-zebra { output { list vrf-list { leaf name { - type string { - length "1..36"; - } + type frr-vrf:vrf-ref; description "The VRF name"; } @@ -910,9 +902,7 @@ module frr-zebra { output { list vrf-vni-list { leaf vrf-name { - type string { - length "1..36"; - } + type frr-vrf:vrf-ref; description "The VRF name."; } diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index 5cd3e69299..90a08bbd6c 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -798,8 +798,10 @@ static int netlink_interface(struct nlmsghdr *h, ns_id_t ns_id, int startup) if (tb[IFLA_LINK]) link_ifindex = *(ifindex_t *)RTA_DATA(tb[IFLA_LINK]); - if (tb[IFLA_LINK_NETNSID]) + if (tb[IFLA_LINK_NETNSID]) { link_nsid = *(ns_id_t *)RTA_DATA(tb[IFLA_LINK_NETNSID]); + link_nsid = ns_id_get_absolute(ns_id, link_nsid); + } /* Add interface. * We add by index first because in some cases such as the master @@ -848,7 +850,7 @@ static int netlink_interface(struct nlmsghdr *h, ns_id_t ns_id, int startup) netlink_interface_update_l2info(ifp, linkinfo[IFLA_INFO_DATA], 1, link_nsid); if (IS_ZEBRA_IF_BRIDGE_SLAVE(ifp)) - zebra_l2if_update_bridge_slave(ifp, bridge_ifindex); + zebra_l2if_update_bridge_slave(ifp, bridge_ifindex, ns_id); else if (IS_ZEBRA_IF_BOND_SLAVE(ifp)) zebra_l2if_update_bond_slave(ifp, bond_ifindex); @@ -1349,9 +1351,10 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) if (tb[IFLA_LINK]) link_ifindex = *(ifindex_t *)RTA_DATA(tb[IFLA_LINK]); - if (tb[IFLA_LINK_NETNSID]) + if (tb[IFLA_LINK_NETNSID]) { link_nsid = *(ns_id_t *)RTA_DATA(tb[IFLA_LINK_NETNSID]); - + link_nsid = ns_id_get_absolute(ns_id, link_nsid); + } if (tb[IFLA_IFALIAS]) { desc = (char *)RTA_DATA(tb[IFLA_IFALIAS]); } @@ -1439,7 +1442,8 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) 1, link_nsid); if (IS_ZEBRA_IF_BRIDGE_SLAVE(ifp)) zebra_l2if_update_bridge_slave(ifp, - bridge_ifindex); + bridge_ifindex, + ns_id); else if (IS_ZEBRA_IF_BOND_SLAVE(ifp)) zebra_l2if_update_bond_slave(ifp, bond_ifindex); } else if (ifp->vrf_id != vrf_id) { @@ -1540,7 +1544,8 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) 0, link_nsid); if (IS_ZEBRA_IF_BRIDGE_SLAVE(ifp) || was_bridge_slave) zebra_l2if_update_bridge_slave(ifp, - bridge_ifindex); + bridge_ifindex, + ns_id); else if (IS_ZEBRA_IF_BOND_SLAVE(ifp) || was_bond_slave) zebra_l2if_update_bond_slave(ifp, bond_ifindex); } diff --git a/zebra/interface.c b/zebra/interface.c index b824e313ec..d29f61450e 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -1242,6 +1242,23 @@ static void nbr_connected_dump_vty(struct vty *vty, vty_out(vty, "\n"); } +static const char *zebra_zifslavetype_2str(zebra_slave_iftype_t zif_slave_type) +{ + switch (zif_slave_type) { + case ZEBRA_IF_SLAVE_BRIDGE: + return "Bridge"; + case ZEBRA_IF_SLAVE_VRF: + return "Vrf"; + case ZEBRA_IF_SLAVE_BOND: + return "Bond"; + case ZEBRA_IF_SLAVE_OTHER: + return "Other"; + case ZEBRA_IF_SLAVE_NONE: + return "None"; + } + return "None"; +} + static const char *zebra_ziftype_2str(zebra_iftype_t zif_type) { switch (zif_type) { @@ -1469,6 +1486,9 @@ static void if_dump_vty(struct vty *vty, struct interface *ifp) vty_out(vty, " Interface Type %s\n", zebra_ziftype_2str(zebra_if->zif_type)); + vty_out(vty, " Interface Slave Type %s\n", + zebra_zifslavetype_2str(zebra_if->zif_slave_type)); + if (IS_ZEBRA_IF_BRIDGE(ifp)) { struct zebra_l2info_bridge *bridge_info; diff --git a/zebra/main.c b/zebra/main.c index cfc45567d7..2afef46bb2 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -183,7 +183,7 @@ static void sigint(void) vrf_terminate(); rtadv_terminate(); - ns_walk_func(zebra_ns_early_shutdown); + ns_walk_func(zebra_ns_early_shutdown, NULL, NULL); zebra_ns_notify_close(); access_list_reset(); @@ -214,7 +214,7 @@ int zebra_finalize(struct thread *dummy) zlog_info("Zebra final shutdown"); /* Final shutdown of ns resources */ - ns_walk_func(zebra_ns_final_shutdown); + ns_walk_func(zebra_ns_final_shutdown, NULL, NULL); /* Stop dplane thread and finish any cleanup */ zebra_dplane_shutdown(); diff --git a/zebra/rule_netlink.c b/zebra/rule_netlink.c index 3a3baab4ca..d6a34327a6 100644 --- a/zebra/rule_netlink.c +++ b/zebra/rule_netlink.c @@ -74,7 +74,7 @@ netlink_rule_msg_encode(int cmd, const struct zebra_dplane_ctx *ctx, char buf[]; } *req = buf; - const char *ifname = dplane_ctx_get_ifname(ctx); + const char *ifname = dplane_ctx_rule_get_ifname(ctx); char buf1[PREFIX_STRLEN]; char buf2[PREFIX_STRLEN]; @@ -141,9 +141,9 @@ netlink_rule_msg_encode(int cmd, const struct zebra_dplane_ctx *ctx, if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug( - "Tx %s family %s IF %s(%u) Pref %u Fwmark %u Src %s Dst %s Table %u", + "Tx %s family %s IF %s Pref %u Fwmark %u Src %s Dst %s Table %u", nl_msg_type_to_str(cmd), nl_family_to_str(family), - ifname, dplane_ctx_get_ifindex(ctx), priority, fwmark, + ifname, priority, fwmark, prefix2str(src_ip, buf1, sizeof(buf1)), prefix2str(dst_ip, buf2, sizeof(buf2)), table); @@ -324,13 +324,13 @@ int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) ret = dplane_pbr_rule_delete(&rule); zlog_debug( - "%s: %s leftover rule: family %s IF %s(%u) Pref %u Src %s Dst %s Table %u", + "%s: %s leftover rule: family %s IF %s Pref %u Src %s Dst %s Table %u", __func__, ((ret == ZEBRA_DPLANE_REQUEST_FAILURE) ? "Failed to remove" : "Removed"), nl_family_to_str(frh->family), rule.ifname, - rule.rule.ifindex, rule.rule.priority, + rule.rule.priority, prefix2str(&rule.rule.filter.src_ip, buf1, sizeof(buf1)), prefix2str(&rule.rule.filter.dst_ip, buf2, @@ -350,10 +350,10 @@ int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug( - "Rx %s family %s IF %s(%u) Pref %u Src %s Dst %s Table %u", + "Rx %s family %s IF %s Pref %u Src %s Dst %s Table %u", nl_msg_type_to_str(h->nlmsg_type), nl_family_to_str(frh->family), rule.ifname, - rule.rule.ifindex, rule.rule.priority, + rule.rule.priority, prefix2str(&rule.rule.filter.src_ip, buf1, sizeof(buf1)), prefix2str(&rule.rule.filter.dst_ip, buf2, diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index 4b31b46bce..e436e5a288 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -815,7 +815,7 @@ void zsend_rule_notify_owner(const struct zebra_dplane_ctx *ctx, stream_putl(s, dplane_ctx_rule_get_seq(ctx)); stream_putl(s, dplane_ctx_rule_get_priority(ctx)); stream_putl(s, dplane_ctx_rule_get_unique(ctx)); - stream_putl(s, dplane_ctx_get_ifindex(ctx)); + stream_put(s, dplane_ctx_rule_get_ifname(ctx), INTERFACE_NAMSIZ); stream_putw_at(s, 0, stream_get_endp(s)); @@ -1349,6 +1349,20 @@ static void zread_interface_add(ZAPI_HANDLER_ARGS) struct vrf *vrf; struct interface *ifp; + vrf_id_t vrf_id = zvrf_id(zvrf); + if (vrf_id != VRF_DEFAULT && vrf_id != VRF_UNKNOWN) { + FOR_ALL_INTERFACES (zvrf->vrf, ifp) { + /* Skip pseudo interface. */ + if (!CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE)) + continue; + + zsend_interface_add(client, ifp); + zsend_interface_link_params(client, ifp); + zsend_interface_addresses(client, ifp); + } + return; + } + RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) { FOR_ALL_INTERFACES (vrf, ifp) { /* Skip pseudo interface. */ @@ -2737,6 +2751,7 @@ static inline void zread_rule(ZAPI_HANDLER_ARGS) struct zebra_pbr_rule zpr; struct stream *s; uint32_t total, i; + char ifname[INTERFACE_NAMSIZ + 1] = {}; s = msg; STREAM_GETL(s, total); @@ -2762,21 +2777,10 @@ static inline void zread_rule(ZAPI_HANDLER_ARGS) STREAM_GETC(s, zpr.rule.filter.dsfield); STREAM_GETL(s, zpr.rule.filter.fwmark); STREAM_GETL(s, zpr.rule.action.table); - STREAM_GETL(s, zpr.rule.ifindex); - - if (zpr.rule.ifindex) { - struct interface *ifp; + STREAM_GET(ifname, s, INTERFACE_NAMSIZ); - ifp = if_lookup_by_index_per_ns(zvrf->zns, - zpr.rule.ifindex); - if (!ifp) { - zlog_debug("Failed to lookup ifindex: %u", - zpr.rule.ifindex); - return; - } - - strlcpy(zpr.ifname, ifp->name, sizeof(zpr.ifname)); - } + strlcpy(zpr.ifname, ifname, sizeof(zpr.ifname)); + strlcpy(zpr.rule.ifname, ifname, sizeof(zpr.rule.ifname)); if (!is_default_prefix(&zpr.rule.filter.src_ip)) zpr.rule.filter.filter_bm |= PBR_FILTER_SRC_IP; diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index 9771235717..abd0adb64e 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -210,6 +210,7 @@ struct dplane_ctx_rule { uint8_t dsfield; struct prefix src_ip; struct prefix dst_ip; + char ifname[INTERFACE_NAMSIZ + 1]; }; struct dplane_rule_info { @@ -1632,6 +1633,13 @@ int dplane_ctx_rule_get_sock(const struct zebra_dplane_ctx *ctx) return ctx->u.rule.sock; } +const char *dplane_ctx_rule_get_ifname(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.rule.new.ifname; +} + int dplane_ctx_rule_get_unique(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); @@ -2191,6 +2199,7 @@ static void dplane_ctx_rule_init_single(struct dplane_ctx_rule *dplane_rule, dplane_rule->dsfield = rule->rule.filter.dsfield; prefix_copy(&(dplane_rule->dst_ip), &rule->rule.filter.dst_ip); prefix_copy(&(dplane_rule->src_ip), &rule->rule.filter.src_ip); + strlcpy(dplane_rule->ifname, rule->ifname, INTERFACE_NAMSIZ); } /** @@ -2212,10 +2221,9 @@ static int dplane_ctx_rule_init(struct zebra_dplane_ctx *ctx, char buf2[PREFIX_STRLEN]; zlog_debug( - "init dplane ctx %s: IF %s(%u) Prio %u Fwmark %u Src %s Dst %s Table %u", + "init dplane ctx %s: IF %s Prio %u Fwmark %u Src %s Dst %s Table %u", dplane_op2str(op), new_rule->ifname, - new_rule->rule.ifindex, new_rule->rule.priority, - new_rule->rule.filter.fwmark, + new_rule->rule.priority, new_rule->rule.filter.fwmark, prefix2str(&new_rule->rule.filter.src_ip, buf1, sizeof(buf1)), prefix2str(&new_rule->rule.filter.dst_ip, buf2, @@ -2232,7 +2240,6 @@ static int dplane_ctx_rule_init(struct zebra_dplane_ctx *ctx, ctx->zd_vrf_id = new_rule->vrf_id; memcpy(ctx->zd_ifname, new_rule->ifname, sizeof(new_rule->ifname)); - ctx->zd_ifindex = new_rule->rule.ifindex; ctx->u.rule.sock = new_rule->sock; ctx->u.rule.unique = new_rule->rule.unique; diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h index 5dd789a851..1d852b1bac 100644 --- a/zebra/zebra_dplane.h +++ b/zebra/zebra_dplane.h @@ -423,6 +423,7 @@ uint32_t dplane_ctx_neigh_get_update_flags(const struct zebra_dplane_ctx *ctx); int dplane_ctx_rule_get_sock(const struct zebra_dplane_ctx *ctx); int dplane_ctx_rule_get_unique(const struct zebra_dplane_ctx *ctx); int dplane_ctx_rule_get_seq(const struct zebra_dplane_ctx *ctx); +const char *dplane_ctx_rule_get_ifname(const struct zebra_dplane_ctx *ctx); uint32_t dplane_ctx_rule_get_priority(const struct zebra_dplane_ctx *ctx); uint32_t dplane_ctx_rule_get_old_priority(const struct zebra_dplane_ctx *ctx); uint32_t dplane_ctx_rule_get_table(const struct zebra_dplane_ctx *ctx); diff --git a/zebra/zebra_evpn.c b/zebra/zebra_evpn.c index 73df93258e..56699c4e83 100644 --- a/zebra/zebra_evpn.c +++ b/zebra/zebra_evpn.c @@ -622,6 +622,60 @@ void zebra_evpn_svi_macip_del_for_evpn_hash(struct hash_bucket *bucket, return; } +static int zebra_evpn_map_vlan_ns(struct ns *ns, + void *_in_param, + void **_p_zevpn) +{ + struct zebra_ns *zns = ns->info; + struct route_node *rn; + struct interface *br_if; + zebra_evpn_t **p_zevpn = (zebra_evpn_t **)_p_zevpn; + zebra_evpn_t *zevpn; + struct interface *tmp_if = NULL; + struct zebra_if *zif; + struct zebra_l2info_vxlan *vxl = NULL; + struct zebra_from_svi_param *in_param = + (struct zebra_from_svi_param *)_in_param; + int found = 0; + + if (!in_param) + return NS_WALK_STOP; + br_if = in_param->br_if; + zif = in_param->zif; + assert(zif); + assert(br_if); + + /* See if this interface (or interface plus VLAN Id) maps to a VxLAN */ + /* TODO: Optimize with a hash. */ + for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { + tmp_if = (struct interface *)rn->info; + if (!tmp_if) + continue; + zif = tmp_if->info; + if (!zif || zif->zif_type != ZEBRA_IF_VXLAN) + continue; + if (!if_is_operative(tmp_if)) + continue; + vxl = &zif->l2info.vxl; + + if (zif->brslave_info.br_if != br_if) + continue; + + if (!in_param->bridge_vlan_aware + || vxl->access_vlan == in_param->vid) { + found = 1; + break; + } + } + if (!found) + return NS_WALK_CONTINUE; + + zevpn = zebra_evpn_lookup(vxl->vni); + if (p_zevpn) + *p_zevpn = zevpn; + return NS_WALK_STOP; +} + /* * Map port or (port, VLAN) to an EVPN. This is invoked upon getting MAC * notifications, to see if they are of interest. @@ -629,25 +683,51 @@ void zebra_evpn_svi_macip_del_for_evpn_hash(struct hash_bucket *bucket, zebra_evpn_t *zebra_evpn_map_vlan(struct interface *ifp, struct interface *br_if, vlanid_t vid) { - struct zebra_ns *zns; - struct route_node *rn; - struct interface *tmp_if = NULL; struct zebra_if *zif; struct zebra_l2info_bridge *br; - struct zebra_l2info_vxlan *vxl = NULL; - uint8_t bridge_vlan_aware; - zebra_evpn_t *zevpn; - int found = 0; + zebra_evpn_t **p_zevpn; + zebra_evpn_t *zevpn = NULL; + struct zebra_from_svi_param in_param; /* Determine if bridge is VLAN-aware or not */ zif = br_if->info; assert(zif); br = &zif->l2info.br; - bridge_vlan_aware = br->vlan_aware; + in_param.bridge_vlan_aware = br->vlan_aware; + in_param.vid = vid; + in_param.br_if = br_if; + in_param.zif = zif; + p_zevpn = &zevpn; + + ns_walk_func(zebra_evpn_map_vlan_ns, + (void *)&in_param, + (void **)p_zevpn); + return zevpn; +} + +static int zebra_evpn_from_svi_ns(struct ns *ns, + void *_in_param, + void **_p_zevpn) +{ + struct zebra_ns *zns = ns->info; + struct route_node *rn; + struct interface *br_if; + zebra_evpn_t **p_zevpn = (zebra_evpn_t **)_p_zevpn; + zebra_evpn_t *zevpn; + struct interface *tmp_if = NULL; + struct zebra_if *zif; + struct zebra_l2info_vxlan *vxl = NULL; + struct zebra_from_svi_param *in_param = + (struct zebra_from_svi_param *)_in_param; + int found = 0; + + if (!in_param) + return NS_WALK_STOP; + br_if = in_param->br_if; + zif = in_param->zif; + assert(zif); - /* See if this interface (or interface plus VLAN Id) maps to a VxLAN */ /* TODO: Optimize with a hash. */ - zns = zebra_ns_lookup(NS_DEFAULT); for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { tmp_if = (struct interface *)rn->info; if (!tmp_if) @@ -662,17 +742,20 @@ zebra_evpn_t *zebra_evpn_map_vlan(struct interface *ifp, if (zif->brslave_info.br_if != br_if) continue; - if (!bridge_vlan_aware || vxl->access_vlan == vid) { + if (!in_param->bridge_vlan_aware + || vxl->access_vlan == in_param->vid) { found = 1; break; } } if (!found) - return NULL; + return NS_WALK_CONTINUE; zevpn = zebra_evpn_lookup(vxl->vni); - return zevpn; + if (p_zevpn) + *p_zevpn = zevpn; + return NS_WALK_STOP; } /* @@ -682,16 +765,11 @@ zebra_evpn_t *zebra_evpn_map_vlan(struct interface *ifp, zebra_evpn_t *zebra_evpn_from_svi(struct interface *ifp, struct interface *br_if) { - struct zebra_ns *zns; - struct route_node *rn; - struct interface *tmp_if = NULL; - struct zebra_if *zif; struct zebra_l2info_bridge *br; - struct zebra_l2info_vxlan *vxl = NULL; - uint8_t bridge_vlan_aware; - vlanid_t vid = 0; - zebra_evpn_t *zevpn; - int found = 0; + zebra_evpn_t *zevpn = NULL; + zebra_evpn_t **p_zevpn; + struct zebra_if *zif; + struct zebra_from_svi_param in_param; if (!br_if) return NULL; @@ -704,8 +782,10 @@ zebra_evpn_t *zebra_evpn_from_svi(struct interface *ifp, zif = br_if->info; assert(zif); br = &zif->l2info.br; - bridge_vlan_aware = br->vlan_aware; - if (bridge_vlan_aware) { + in_param.bridge_vlan_aware = br->vlan_aware; + in_param.vid = 0; + + if (in_param.bridge_vlan_aware) { struct zebra_l2info_vlan *vl; if (!IS_ZEBRA_IF_VLAN(ifp)) @@ -714,37 +794,52 @@ zebra_evpn_t *zebra_evpn_from_svi(struct interface *ifp, zif = ifp->info; assert(zif); vl = &zif->l2info.vl; - vid = vl->vid; + in_param.vid = vl->vid; } + in_param.br_if = br_if; + in_param.zif = zif; + p_zevpn = &zevpn; /* See if this interface (or interface plus VLAN Id) maps to a VxLAN */ - /* TODO: Optimize with a hash. */ - zns = zebra_ns_lookup(NS_DEFAULT); + ns_walk_func(zebra_evpn_from_svi_ns, (void *)&in_param, + (void **)p_zevpn); + return zevpn; +} + +static int zvni_map_to_macvlan_ns(struct ns *ns, + void *_in_param, + void **_p_ifp) +{ + struct zebra_ns *zns = ns->info; + struct zebra_from_svi_param *in_param = + (struct zebra_from_svi_param *)_in_param; + struct interface **p_ifp = (struct interface **)_p_ifp; + struct route_node *rn; + struct interface *tmp_if = NULL; + struct zebra_if *zif; + + if (!in_param) + return NS_WALK_STOP; + + /* Identify corresponding VLAN interface. */ for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { tmp_if = (struct interface *)rn->info; - if (!tmp_if) + /* Check oper status of the SVI. */ + if (!tmp_if || !if_is_operative(tmp_if)) continue; zif = tmp_if->info; - if (!zif || zif->zif_type != ZEBRA_IF_VXLAN) - continue; - if (!if_is_operative(tmp_if)) - continue; - vxl = &zif->l2info.vxl; - if (zif->brslave_info.br_if != br_if) + if (!zif || zif->zif_type != ZEBRA_IF_MACVLAN) continue; - if (!bridge_vlan_aware || vxl->access_vlan == vid) { - found = 1; - break; + if (zif->link == in_param->svi_if) { + if (p_ifp) + *p_ifp = tmp_if; + return NS_WALK_STOP; } } - if (!found) - return NULL; - - zevpn = zebra_evpn_lookup(vxl->vni); - return zevpn; + return NS_WALK_CONTINUE; } /* Map to MAC-VLAN interface corresponding to specified SVI interface. @@ -752,11 +847,10 @@ zebra_evpn_t *zebra_evpn_from_svi(struct interface *ifp, struct interface *zebra_evpn_map_to_macvlan(struct interface *br_if, struct interface *svi_if) { - struct zebra_ns *zns; - struct route_node *rn; struct interface *tmp_if = NULL; struct zebra_if *zif; - int found = 0; + struct interface **p_ifp; + struct zebra_from_svi_param in_param; /* Defensive check, caller expected to invoke only with valid bridge. */ if (!br_if) @@ -771,25 +865,17 @@ struct interface *zebra_evpn_map_to_macvlan(struct interface *br_if, zif = br_if->info; assert(zif); - /* Identify corresponding VLAN interface. */ - zns = zebra_ns_lookup(NS_DEFAULT); - for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { - tmp_if = (struct interface *)rn->info; - /* Check oper status of the SVI. */ - if (!tmp_if || !if_is_operative(tmp_if)) - continue; - zif = tmp_if->info; - - if (!zif || zif->zif_type != ZEBRA_IF_MACVLAN) - continue; + in_param.vid = 0; + in_param.br_if = br_if; + in_param.zif = NULL; + in_param.svi_if = svi_if; + p_ifp = &tmp_if; - if (zif->link == svi_if) { - found = 1; - break; - } - } - - return found ? tmp_if : NULL; + /* Identify corresponding VLAN interface. */ + ns_walk_func(zvni_map_to_macvlan_ns, + (void *)&in_param, + (void **)p_ifp); + return tmp_if; } /* @@ -812,6 +898,7 @@ void zebra_evpn_install_mac_hash(struct hash_bucket *bucket, void *ctxt) void zebra_evpn_read_mac_neigh(zebra_evpn_t *zevpn, struct interface *ifp) { struct zebra_ns *zns; + struct zebra_vrf *zvrf; struct zebra_if *zif; struct interface *vlan_if; struct zebra_l2info_vxlan *vxl; @@ -819,7 +906,10 @@ void zebra_evpn_read_mac_neigh(zebra_evpn_t *zevpn, struct interface *ifp) zif = ifp->info; vxl = &zif->l2info.vxl; - zns = zebra_ns_lookup(NS_DEFAULT); + zvrf = zebra_vrf_lookup_by_id(zevpn->vrf_id); + if (!zvrf || !zvrf->zns) + return; + zns = zvrf->zns; if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( @@ -921,7 +1011,7 @@ zebra_evpn_t *zebra_evpn_add(vni_t vni) zevpn = hash_get(zvrf->evpn_table, &tmp_zevpn, zebra_evpn_alloc); assert(zevpn); - zebra_evpn_evpn_es_init(zevpn); + zebra_evpn_es_evi_init(zevpn); /* Create hash table for MAC */ zevpn->mac_table = zebra_mac_db_create("Zebra EVPN MAC Table"); @@ -951,7 +1041,7 @@ int zebra_evpn_del(zebra_evpn_t *zevpn) hash_free(zevpn->mac_table); zevpn->mac_table = NULL; - zebra_evpn_evpn_es_cleanup(zevpn); + zebra_evpn_es_evi_cleanup(zevpn); /* Free the EVPN hash entry and allocated memory. */ tmp_zevpn = hash_release(zvrf->evpn_table, zevpn); @@ -1315,7 +1405,7 @@ void process_remote_macip_add(vni_t vni, struct ethaddr *macaddr, } } - zvrf = vrf_info_lookup(zevpn->vxlan_if->vrf_id); + zvrf = zebra_vrf_get_evpn(); if (!zvrf) return; diff --git a/zebra/zebra_evpn.h b/zebra/zebra_evpn.h index 3b6a5b21e8..27392ec85c 100644 --- a/zebra/zebra_evpn.h +++ b/zebra/zebra_evpn.h @@ -123,6 +123,15 @@ struct zebra_evpn_t_ { struct list *local_es_evi_list; }; +/* for parsing evpn and vni contexts */ +struct zebra_from_svi_param { + struct interface *br_if; + struct interface *svi_if; + struct zebra_if *zif; + uint8_t bridge_vlan_aware; + vlanid_t vid; +}; + struct interface *zvni_map_to_svi(vlanid_t vid, struct interface *br_if); static inline struct interface *zevpn_map_to_svi(zebra_evpn_t *zevpn) diff --git a/zebra/zebra_evpn_mac.c b/zebra/zebra_evpn_mac.c index b9cc02a276..75031ddba6 100644 --- a/zebra/zebra_evpn_mac.c +++ b/zebra/zebra_evpn_mac.c @@ -249,7 +249,7 @@ static void zebra_evpn_mac_get_access_info(zebra_mac_t *mac, struct zebra_ns *zns; *vid = mac->fwd_info.local.vid; - zns = zebra_ns_lookup(NS_DEFAULT); + zns = zebra_ns_lookup(mac->fwd_info.local.ns_id); *ifpP = if_lookup_by_index_per_ns(zns, mac->fwd_info.local.ifindex); } @@ -1610,6 +1610,12 @@ static bool zebra_evpn_local_mac_update_fwd_info(zebra_mac_t *mac, { struct zebra_if *zif = ifp->info; bool es_change; + ns_id_t local_ns_id = NS_DEFAULT; + struct zebra_vrf *zvrf; + + zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id); + if (zvrf && zvrf->zns) + local_ns_id = zvrf->zns->ns_id; memset(&mac->fwd_info, 0, sizeof(mac->fwd_info)); @@ -1618,6 +1624,7 @@ static bool zebra_evpn_local_mac_update_fwd_info(zebra_mac_t *mac, if (!mac->es) { /* if es is set fwd_info is not-relevant/taped-out */ mac->fwd_info.local.ifindex = ifp->ifindex; + mac->fwd_info.local.ns_id = local_ns_id; mac->fwd_info.local.vid = vid; } @@ -2060,6 +2067,7 @@ int zebra_evpn_add_update_local_mac(struct zebra_vrf *zvrf, zebra_evpn_t *zevpn, if (is_dup_detect) { inform_client = false; upd_neigh = false; + es_change = false; } } } @@ -2092,7 +2100,8 @@ int zebra_evpn_add_update_local_mac(struct zebra_vrf *zvrf, zebra_evpn_t *zevpn, mac->es ? mac->es->esi_str : "", mac->loc_seq, mac->flags, local_inactive ? " local-inactive" : ""); - inform_client = true; + if (!is_dup_detect) + inform_client = true; } if (es_change) { @@ -2204,6 +2213,12 @@ int zebra_evpn_mac_gw_macip_add(struct interface *ifp, zebra_evpn_t *zevpn, { char buf[ETHER_ADDR_STRLEN]; zebra_mac_t *mac; + ns_id_t local_ns_id = NS_DEFAULT; + struct zebra_vrf *zvrf; + + zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id); + if (zvrf && zvrf->zns) + local_ns_id = zvrf->zns->ns_id; mac = zebra_evpn_mac_lookup(zevpn, macaddr); if (!mac) { @@ -2223,6 +2238,7 @@ int zebra_evpn_mac_gw_macip_add(struct interface *ifp, zebra_evpn_t *zevpn, SET_FLAG(mac->flags, ZEBRA_MAC_DEF_GW); memset(&mac->fwd_info, 0, sizeof(mac->fwd_info)); mac->fwd_info.local.ifindex = ifp->ifindex; + mac->fwd_info.local.ns_id = local_ns_id; mac->fwd_info.local.vid = vlan_id; *macp = mac; diff --git a/zebra/zebra_evpn_mac.h b/zebra/zebra_evpn_mac.h index 39aaf1fb30..f9ca81445f 100644 --- a/zebra/zebra_evpn_mac.h +++ b/zebra/zebra_evpn_mac.h @@ -91,6 +91,7 @@ struct zebra_mac_t_ { union { struct { ifindex_t ifindex; + ns_id_t ns_id; vlanid_t vid; } local; diff --git a/zebra/zebra_evpn_mh.c b/zebra/zebra_evpn_mh.c index 3c99ce29c3..2567171c5e 100644 --- a/zebra/zebra_evpn_mh.c +++ b/zebra/zebra_evpn_mh.c @@ -363,7 +363,7 @@ void zebra_evpn_es_evi_show_vni(struct vty *vty, bool uj, vni_t vni, int detail) } /* Initialize the ES tables maintained per-L2_VNI */ -void zebra_evpn_evpn_es_init(zebra_evpn_t *zevpn) +void zebra_evpn_es_evi_init(zebra_evpn_t *zevpn) { /* Initialize the ES-EVI RB tree */ RB_INIT(zebra_es_evi_rb_head, &zevpn->es_evi_rb_tree); @@ -376,7 +376,7 @@ void zebra_evpn_evpn_es_init(zebra_evpn_t *zevpn) } /* Cleanup the ES info maintained per- EVPN */ -void zebra_evpn_evpn_es_cleanup(zebra_evpn_t *zevpn) +void zebra_evpn_es_evi_cleanup(zebra_evpn_t *zevpn) { struct zebra_evpn_es_evi *es_evi; struct zebra_evpn_es_evi *es_evi_next; diff --git a/zebra/zebra_evpn_mh.h b/zebra/zebra_evpn_mh.h index ed62677e3b..72b7f9b675 100644 --- a/zebra/zebra_evpn_mh.h +++ b/zebra/zebra_evpn_mh.h @@ -198,8 +198,8 @@ extern void zebra_evpn_mh_terminate(void); extern bool zebra_evpn_is_if_es_capable(struct zebra_if *zif); extern void zebra_evpn_if_init(struct zebra_if *zif); extern void zebra_evpn_if_cleanup(struct zebra_if *zif); -extern void zebra_evpn_evpn_es_init(zebra_evpn_t *zevpn); -extern void zebra_evpn_evpn_es_cleanup(zebra_evpn_t *zevpn); +extern void zebra_evpn_es_evi_init(zebra_evpn_t *zevpn); +extern void zebra_evpn_es_evi_cleanup(zebra_evpn_t *zevpn); extern void zebra_evpn_vxl_evpn_set(struct zebra_if *zif, zebra_evpn_t *zevpn, bool set); extern void zebra_evpn_es_set_base_evpn(zebra_evpn_t *zevpn); diff --git a/zebra/zebra_evpn_neigh.c b/zebra/zebra_evpn_neigh.c index 492052b1b2..661d1c7f81 100644 --- a/zebra/zebra_evpn_neigh.c +++ b/zebra/zebra_evpn_neigh.c @@ -2184,9 +2184,10 @@ void process_neigh_remote_macip_add(zebra_evpn_t *zevpn, struct zebra_vrf *zvrf, seq, n->flags); zebra_evpn_neigh_clear_sync_info(n); if (IS_ZEBRA_NEIGH_ACTIVE(n)) - zebra_evpn_mac_send_del_to_client( - zevpn->vni, &mac->macaddr, - mac->flags, false /*force*/); + zebra_evpn_neigh_send_del_to_client( + zevpn->vni, &n->ip, &n->emac, + n->flags, n->state, + false /*force*/); } if (memcmp(&n->emac, &mac->macaddr, sizeof(struct ethaddr)) diff --git a/zebra/zebra_l2.c b/zebra/zebra_l2.c index 1758c8f96a..417056ecb0 100644 --- a/zebra/zebra_l2.c +++ b/zebra/zebra_l2.c @@ -54,7 +54,13 @@ static void map_slaves_to_bridge(struct interface *br_if, int link) { struct vrf *vrf; struct interface *ifp; + struct zebra_vrf *zvrf; + struct zebra_ns *zns; + zvrf = zebra_vrf_lookup_by_id(br_if->vrf_id); + assert(zvrf); + zns = zvrf->zns; + assert(zns); RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { FOR_ALL_INTERFACES (vrf, ifp) { struct zebra_if *zif; @@ -73,7 +79,8 @@ static void map_slaves_to_bridge(struct interface *br_if, int link) br_slave = &zif->brslave_info; if (link) { - if (br_slave->bridge_ifindex == br_if->ifindex) + if (br_slave->bridge_ifindex == br_if->ifindex && + br_slave->ns_id == zns->ns_id) br_slave->br_if = br_if; } else { if (br_slave->br_if == br_if) @@ -84,12 +91,14 @@ static void map_slaves_to_bridge(struct interface *br_if, int link) } /* Public functions */ -void zebra_l2_map_slave_to_bridge(struct zebra_l2info_brslave *br_slave) +void zebra_l2_map_slave_to_bridge(struct zebra_l2info_brslave *br_slave, + struct zebra_ns *zns) { struct interface *br_if; /* TODO: Handle change of master */ - br_if = if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT), + assert(zns); + br_if = if_lookup_by_index_per_ns(zebra_ns_lookup(zns->ns_id), br_slave->bridge_ifindex); if (br_if) br_slave->br_if = br_if; @@ -248,23 +257,32 @@ void zebra_l2_vxlanif_del(struct interface *ifp) * from a bridge before it can be mapped to another bridge. */ void zebra_l2if_update_bridge_slave(struct interface *ifp, - ifindex_t bridge_ifindex) + ifindex_t bridge_ifindex, + ns_id_t ns_id) { struct zebra_if *zif; ifindex_t old_bridge_ifindex; + ns_id_t old_ns_id; + struct zebra_vrf *zvrf; zif = ifp->info; assert(zif); + zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id); + if (!zvrf) + return; + old_bridge_ifindex = zif->brslave_info.bridge_ifindex; - if (old_bridge_ifindex == bridge_ifindex) + old_ns_id = zif->brslave_info.ns_id; + if (old_bridge_ifindex == bridge_ifindex && + old_ns_id == zif->brslave_info.ns_id) return; + zif->brslave_info.ns_id = ns_id; zif->brslave_info.bridge_ifindex = bridge_ifindex; - /* Set up or remove link with master */ if (bridge_ifindex != IFINDEX_INTERNAL) { - zebra_l2_map_slave_to_bridge(&zif->brslave_info); + zebra_l2_map_slave_to_bridge(&zif->brslave_info, zvrf->zns); /* In the case of VxLAN, invoke the handler for EVPN. */ if (zif->zif_type == ZEBRA_IF_VXLAN) zebra_vxlan_if_update(ifp, ZEBRA_VXLIF_MASTER_CHANGE); diff --git a/zebra/zebra_l2.h b/zebra/zebra_l2.h index 2735d915ec..f3b15c7770 100644 --- a/zebra/zebra_l2.h +++ b/zebra/zebra_l2.h @@ -37,6 +37,7 @@ extern "C" { struct zebra_l2info_brslave { ifindex_t bridge_ifindex; /* Bridge Master */ struct interface *br_if; /* Pointer to master */ + ns_id_t ns_id; /* network namespace where bridge is */ }; /* zebra L2 interface information - bridge interface */ @@ -81,7 +82,8 @@ union zebra_l2if_info { #define IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(zif) ((zif)->l2info.br.vlan_aware == 1) -extern void zebra_l2_map_slave_to_bridge(struct zebra_l2info_brslave *br_slave); +extern void zebra_l2_map_slave_to_bridge(struct zebra_l2info_brslave *br_slave, + struct zebra_ns *zns); extern void zebra_l2_unmap_slave_from_bridge(struct zebra_l2info_brslave *br_slave); extern void @@ -101,7 +103,8 @@ extern void zebra_l2_vxlanif_update_access_vlan(struct interface *ifp, vlanid_t access_vlan); extern void zebra_l2_vxlanif_del(struct interface *ifp); extern void zebra_l2if_update_bridge_slave(struct interface *ifp, - ifindex_t bridge_ifindex); + ifindex_t bridge_ifindex, + ns_id_t ns_id); extern void zebra_l2if_update_bond_slave(struct interface *ifp, ifindex_t bond_ifindex); diff --git a/zebra/zebra_netns_id.c b/zebra/zebra_netns_id.c index 8de4daf439..79121bb086 100644 --- a/zebra/zebra_netns_id.c +++ b/zebra/zebra_netns_id.c @@ -159,27 +159,34 @@ static ns_id_t extract_nsid(struct nlmsghdr *nlh, char *buf) return ns_id; } -ns_id_t zebra_ns_id_get(const char *netnspath) +/* fd_param = -1 is ignored. + * netnspath set to null is ignored. + * one of the 2 params is mandatory. netnspath is looked in priority + */ +ns_id_t zebra_ns_id_get(const char *netnspath, int fd_param) { int ns_id = -1; struct sockaddr_nl snl; - int fd, sock, ret; + int fd = -1, sock, ret; unsigned int seq; ns_id_t return_nsid = NS_UNKNOWN; /* netns path check */ - if (!netnspath) - return NS_UNKNOWN; - fd = open(netnspath, O_RDONLY); - if (fd == -1) + if (!netnspath && fd_param == -1) return NS_UNKNOWN; - + if (netnspath) { + fd = open(netnspath, O_RDONLY); + if (fd == -1) + return NS_UNKNOWN; + } else if (fd_param != -1) + fd = fd_param; /* netlink socket */ sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (sock < 0) { flog_err_sys(EC_LIB_SOCKET, "netlink( %u) socket() error: %s", sock, safe_strerror(errno)); - close(fd); + if (fd_param == -1) + close(fd); return NS_UNKNOWN; } memset(&snl, 0, sizeof(snl)); @@ -192,7 +199,8 @@ ns_id_t zebra_ns_id_get(const char *netnspath) "netlink( %u) socket() bind error: %s", sock, safe_strerror(errno)); close(sock); - close(fd); + if (fd_param == -1) + close(fd); return NS_UNKNOWN; } @@ -214,7 +222,8 @@ ns_id_t zebra_ns_id_get(const char *netnspath) ret = send_receive(sock, nlh, seq, buf); if (ret < 0) { close(sock); - close(fd); + if (fd_param == -1) + close(fd); return NS_UNKNOWN; } nlh = (struct nlmsghdr *)buf; @@ -258,7 +267,8 @@ ns_id_t zebra_ns_id_get(const char *netnspath) "netlink( %u) recvfrom() error 2 when reading: %s", fd, safe_strerror(errno)); close(sock); - close(fd); + if (fd_param == -1) + close(fd); if (errno == ENOTSUP) { zlog_debug("NEWNSID locally generated"); return zebra_ns_id_get_fallback(netnspath); @@ -279,7 +289,8 @@ ns_id_t zebra_ns_id_get(const char *netnspath) ret = send_receive(sock, nlh, seq, buf); if (ret < 0) { close(sock); - close(fd); + if (fd_param == -1) + close(fd); return NS_UNKNOWN; } nlh = (struct nlmsghdr *)buf; @@ -310,16 +321,18 @@ ns_id_t zebra_ns_id_get(const char *netnspath) } while (len != 0 && ret == 0); } - close(fd); + if (fd_param == -1) + close(fd); close(sock); return return_nsid; } #else -ns_id_t zebra_ns_id_get(const char *netnspath) +ns_id_t zebra_ns_id_get(const char *netnspath, int fd __attribute__ ((unused))) { return zebra_ns_id_get_fallback(netnspath); } + #endif /* ! defined(HAVE_NETLINK) */ #ifdef HAVE_NETNS @@ -355,7 +368,7 @@ ns_id_t zebra_ns_id_get_default(void) return NS_DEFAULT_INTERNAL; } close(fd); - return zebra_ns_id_get((char *)NS_DEFAULT_NAME); + return zebra_ns_id_get((char *)NS_DEFAULT_NAME, -1); #else /* HAVE_NETNS */ return NS_DEFAULT_INTERNAL; #endif /* !HAVE_NETNS */ diff --git a/zebra/zebra_netns_id.h b/zebra/zebra_netns_id.h index 7a5f6851f4..dd9eab18e0 100644 --- a/zebra/zebra_netns_id.h +++ b/zebra/zebra_netns_id.h @@ -24,7 +24,7 @@ extern "C" { #endif -extern ns_id_t zebra_ns_id_get(const char *netnspath); +extern ns_id_t zebra_ns_id_get(const char *netnspath, int fd); extern ns_id_t zebra_ns_id_get_default(void); #ifdef __cplusplus diff --git a/zebra/zebra_netns_notify.c b/zebra/zebra_netns_notify.c index ec7681bf23..995fa6fb5a 100644 --- a/zebra/zebra_netns_notify.c +++ b/zebra/zebra_netns_notify.c @@ -72,13 +72,14 @@ static void zebra_ns_notify_create_context_from_entry_name(const char *name) char *netnspath = ns_netns_pathname(NULL, name); struct vrf *vrf; int ret; - ns_id_t ns_id, ns_id_external; + ns_id_t ns_id, ns_id_external, ns_id_relative = NS_UNKNOWN; + struct ns *default_ns; if (netnspath == NULL) return; frr_with_privs(&zserv_privs) { - ns_id = zebra_ns_id_get(netnspath); + ns_id = zebra_ns_id_get(netnspath, -1); } if (ns_id == NS_UNKNOWN) return; @@ -97,9 +98,21 @@ static void zebra_ns_notify_create_context_from_entry_name(const char *name) ns_map_nsid_with_external(ns_id, false); return; } + + default_ns = ns_get_default(); + + /* force kernel ns_id creation in that new vrf */ + frr_with_privs(&zserv_privs) { + ns_switch_to_netns(netnspath); + ns_id_relative = zebra_ns_id_get(NULL, default_ns->fd); + ns_switchback_to_initial(); + } + frr_with_privs(&zserv_privs) { ret = vrf_netns_handler_create(NULL, vrf, netnspath, - ns_id_external, ns_id); + ns_id_external, + ns_id, + ns_id_relative); } if (ret != CMD_SUCCESS) { flog_warn(EC_ZEBRA_NS_VRF_CREATION_FAILED, diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c index 4e51437337..6462daf687 100644 --- a/zebra/zebra_ns.c +++ b/zebra/zebra_ns.c @@ -153,20 +153,25 @@ static int zebra_ns_disable_internal(struct zebra_ns *zns, bool complete) /* During zebra shutdown, do partial cleanup while the async dataplane * is still running. */ -int zebra_ns_early_shutdown(struct ns *ns) +int zebra_ns_early_shutdown(struct ns *ns, + void *param_in __attribute__((unused)), + void **param_out __attribute__((unused))) { struct zebra_ns *zns = ns->info; if (zns == NULL) return 0; - return zebra_ns_disable_internal(zns, false); + zebra_ns_disable_internal(zns, false); + return NS_WALK_CONTINUE; } /* During zebra shutdown, do final cleanup * after all dataplane work is complete. */ -int zebra_ns_final_shutdown(struct ns *ns) +int zebra_ns_final_shutdown(struct ns *ns, + void *param_in __attribute__((unused)), + void **param_out __attribute__((unused))) { struct zebra_ns *zns = ns->info; @@ -175,7 +180,7 @@ int zebra_ns_final_shutdown(struct ns *ns) kernel_terminate(zns, true); - return 0; + return NS_WALK_CONTINUE; } int zebra_ns_init(const char *optional_default_name) @@ -183,12 +188,16 @@ int zebra_ns_init(const char *optional_default_name) struct ns *default_ns; ns_id_t ns_id; ns_id_t ns_id_external; + struct ns *ns; frr_with_privs(&zserv_privs) { ns_id = zebra_ns_id_get_default(); } ns_id_external = ns_map_nsid_with_external(ns_id, true); ns_init_management(ns_id_external, ns_id); + ns = ns_get_default(); + if (ns) + ns->relative_default_ns = ns_id; default_ns = ns_lookup(ns_get_default_id()); if (!default_ns) { diff --git a/zebra/zebra_ns.h b/zebra/zebra_ns.h index dc79a83db0..f7d1f40782 100644 --- a/zebra/zebra_ns.h +++ b/zebra/zebra_ns.h @@ -67,9 +67,12 @@ struct zebra_ns *zebra_ns_lookup(ns_id_t ns_id); int zebra_ns_init(const char *optional_default_name); int zebra_ns_enable(ns_id_t ns_id, void **info); int zebra_ns_disabled(struct ns *ns); -int zebra_ns_early_shutdown(struct ns *ns); -int zebra_ns_final_shutdown(struct ns *ns); - +int zebra_ns_early_shutdown(struct ns *ns, + void *param_in __attribute__((unused)), + void **param_out __attribute__((unused))); +int zebra_ns_final_shutdown(struct ns *ns, + void *param_in __attribute__((unused)), + void **param_out __attribute__((unused))); int zebra_ns_config_write(struct vty *vty, struct ns *ns); #ifdef __cplusplus diff --git a/zebra/zebra_pbr.c b/zebra/zebra_pbr.c index 95d241c59f..c244d2a955 100644 --- a/zebra/zebra_pbr.c +++ b/zebra/zebra_pbr.c @@ -167,10 +167,11 @@ uint32_t zebra_pbr_rules_hash_key(const void *arg) prefix_hash_key(&rule->rule.filter.src_ip)); if (rule->rule.filter.fwmark) - key = jhash_3words(rule->rule.filter.fwmark, rule->vrf_id, - rule->rule.ifindex, key); + key = jhash_2words(rule->rule.filter.fwmark, rule->vrf_id, key); else - key = jhash_2words(rule->vrf_id, rule->rule.ifindex, key); + key = jhash_1word(rule->vrf_id, key); + + key = jhash(rule->ifname, strlen(rule->ifname), key); return jhash_3words(rule->rule.filter.src_port, rule->rule.filter.dst_port, @@ -212,7 +213,7 @@ bool zebra_pbr_rules_hash_equal(const void *arg1, const void *arg2) if (!prefix_same(&r1->rule.filter.dst_ip, &r2->rule.filter.dst_ip)) return false; - if (r1->rule.ifindex != r2->rule.ifindex) + if (strcmp(r1->rule.ifname, r2->rule.ifname) != 0) return false; if (r1->vrf_id != r2->vrf_id) @@ -224,7 +225,7 @@ bool zebra_pbr_rules_hash_equal(const void *arg1, const void *arg2) struct pbr_rule_unique_lookup { struct zebra_pbr_rule *rule; uint32_t unique; - ifindex_t ifindex; + char ifname[INTERFACE_NAMSIZ + 1]; vrf_id_t vrf_id; }; @@ -234,7 +235,7 @@ static int pbr_rule_lookup_unique_walker(struct hash_bucket *b, void *data) struct zebra_pbr_rule *rule = b->data; if (pul->unique == rule->rule.unique - && pul->ifindex == rule->rule.ifindex + && strncmp(pul->ifname, rule->rule.ifname, INTERFACE_NAMSIZ) == 0 && pul->vrf_id == rule->vrf_id) { pul->rule = rule; return HASHWALK_ABORT; @@ -249,7 +250,7 @@ pbr_rule_lookup_unique(struct zebra_pbr_rule *zrule) struct pbr_rule_unique_lookup pul; pul.unique = zrule->rule.unique; - pul.ifindex = zrule->rule.ifindex; + strlcpy(pul.ifname, zrule->rule.ifname, INTERFACE_NAMSIZ); pul.rule = NULL; pul.vrf_id = zrule->vrf_id; hash_walk(zrouter.rules_hash, &pbr_rule_lookup_unique_walker, &pul); diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index 13a58bc34a..61498973e9 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -782,6 +782,43 @@ static void zl3vni_print_hash_detail(struct hash_bucket *bucket, void *data) vty_out(vty, "\n"); } +static int zvni_map_to_svi_ns(struct ns *ns, + void *_in_param, + void **_p_ifp) +{ + struct zebra_ns *zns = ns->info; + struct route_node *rn; + struct zebra_from_svi_param *in_param = + (struct zebra_from_svi_param *)_in_param; + struct zebra_l2info_vlan *vl; + struct interface *tmp_if = NULL; + struct interface **p_ifp = (struct interface **)_p_ifp; + struct zebra_if *zif; + + if (!in_param) + return NS_WALK_STOP; + + /* TODO: Optimize with a hash. */ + for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { + tmp_if = (struct interface *)rn->info; + /* Check oper status of the SVI. */ + if (!tmp_if || !if_is_operative(tmp_if)) + continue; + zif = tmp_if->info; + if (!zif || zif->zif_type != ZEBRA_IF_VLAN + || zif->link != in_param->br_if) + continue; + vl = (struct zebra_l2info_vlan *)&zif->l2info.vl; + + if (vl->vid == in_param->vid) { + if (p_ifp) + *p_ifp = tmp_if; + return NS_WALK_STOP; + } + } + return NS_WALK_CONTINUE; +} + /* Map to SVI on bridge corresponding to specified VLAN. This can be one * of two cases: * (a) In the case of a VLAN-aware bridge, the SVI is a L3 VLAN interface @@ -791,15 +828,11 @@ static void zl3vni_print_hash_detail(struct hash_bucket *bucket, void *data) */ struct interface *zvni_map_to_svi(vlanid_t vid, struct interface *br_if) { - struct zebra_ns *zns; - struct route_node *rn; struct interface *tmp_if = NULL; struct zebra_if *zif; struct zebra_l2info_bridge *br; - struct zebra_l2info_vlan *vl; - uint8_t bridge_vlan_aware; - int found = 0; - + struct zebra_from_svi_param in_param; + struct interface **p_ifp; /* Defensive check, caller expected to invoke only with valid bridge. */ if (!br_if) return NULL; @@ -808,33 +841,19 @@ struct interface *zvni_map_to_svi(vlanid_t vid, struct interface *br_if) zif = br_if->info; assert(zif); br = &zif->l2info.br; - bridge_vlan_aware = br->vlan_aware; - + in_param.bridge_vlan_aware = br->vlan_aware; /* Check oper status of the SVI. */ - if (!bridge_vlan_aware) + if (!in_param.bridge_vlan_aware) return if_is_operative(br_if) ? br_if : NULL; + in_param.vid = vid; + in_param.br_if = br_if; + in_param.zif = NULL; + p_ifp = &tmp_if; /* Identify corresponding VLAN interface. */ - /* TODO: Optimize with a hash. */ - zns = zebra_ns_lookup(NS_DEFAULT); - for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { - tmp_if = (struct interface *)rn->info; - /* Check oper status of the SVI. */ - if (!tmp_if || !if_is_operative(tmp_if)) - continue; - zif = tmp_if->info; - if (!zif || zif->zif_type != ZEBRA_IF_VLAN - || zif->link != br_if) - continue; - vl = &zif->l2info.vl; - - if (vl->vid == vid) { - found = 1; - break; - } - } - - return found ? tmp_if : NULL; + ns_walk_func(zvni_map_to_svi_ns, (void *)&in_param, + (void **)p_ifp); + return tmp_if; } static int zebra_evpn_vxlan_del(zebra_evpn_t *zevpn) @@ -846,18 +865,22 @@ static int zebra_evpn_vxlan_del(zebra_evpn_t *zevpn) return zebra_evpn_del(zevpn); } -/* - * Build the VNI hash table by going over the VxLAN interfaces. This - * is called when EVPN (advertise-all-vni) is enabled. - */ -static void zevpn_build_hash_table(void) + +static int zevpn_build_hash_table_zns(struct ns *ns, + void *param_in __attribute__((unused)), + void **param_out __attribute__((unused))) { - struct zebra_ns *zns; + struct zebra_ns *zns = ns->info; struct route_node *rn; struct interface *ifp; + struct zebra_vrf *zvrf; + + zvrf = zebra_vrf_get_evpn(); + + if (!zvrf) + return NS_WALK_STOP; /* Walk VxLAN interfaces and create EVPN hash. */ - zns = zebra_ns_lookup(NS_DEFAULT); for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { vni_t vni; zebra_evpn_t *zevpn = NULL; @@ -874,7 +897,15 @@ static void zevpn_build_hash_table(void) vxl = &zif->l2info.vxl; vni = vxl->vni; - + /* link of VXLAN interface should be in zebra_evpn_vrf */ + if (zvrf->zns->ns_id != vxl->link_nsid) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Intf %s(%u) VNI %u, link not in same " + "namespace than BGP EVPN core instance ", + ifp->name, ifp->ifindex, vni); + continue; + } /* L3-VNI and L2-VNI are handled seperately */ zl3vni = zl3vni_lookup(vni); if (zl3vni) { @@ -943,7 +974,7 @@ static void zevpn_build_hash_table(void) zlog_debug( "Failed to add EVPN hash, IF %s(%u) L2-VNI %u", ifp->name, ifp->ifindex, vni); - return; + return NS_WALK_CONTINUE; } if (zevpn->local_vtep_ip.s_addr != @@ -985,6 +1016,19 @@ static void zevpn_build_hash_table(void) } } } + return NS_WALK_CONTINUE; +} + +/* + * Build the VNI hash table by going over the VxLAN interfaces. This + * is called when EVPN (advertise-all-vni) is enabled. + */ + +static void zevpn_build_hash_table(void) +{ + ns_walk_func(zevpn_build_hash_table_zns, + (void *)NULL, + (void **)NULL); } /* @@ -1617,14 +1661,22 @@ static int zl3vni_del(zebra_l3vni_t *zl3vni) return 0; } -struct interface *zl3vni_map_to_vxlan_if(zebra_l3vni_t *zl3vni) +static int zl3vni_map_to_vxlan_if_ns(struct ns *ns, + void *_zl3vni, + void **_pifp) { - struct zebra_ns *zns = NULL; + struct zebra_ns *zns = ns->info; + zebra_l3vni_t *zl3vni = (zebra_l3vni_t *)_zl3vni; struct route_node *rn = NULL; struct interface *ifp = NULL; + struct zebra_vrf *zvrf; + + zvrf = zebra_vrf_get_evpn(); + + if (!zvrf) + return NS_WALK_STOP; /* loop through all vxlan-interface */ - zns = zebra_ns_lookup(NS_DEFAULT); for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { struct zebra_if *zif = NULL; @@ -1639,13 +1691,39 @@ struct interface *zl3vni_map_to_vxlan_if(zebra_l3vni_t *zl3vni) continue; vxl = &zif->l2info.vxl; - if (vxl->vni == zl3vni->vni) { - zl3vni->local_vtep_ip = vxl->vtep_ip; - return ifp; + if (vxl->vni != zl3vni->vni) + continue; + + /* link of VXLAN interface should be in zebra_evpn_vrf */ + if (zvrf->zns->ns_id != vxl->link_nsid) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Intf %s(%u) VNI %u, link not in same " + "namespace than BGP EVPN core instance ", + ifp->name, ifp->ifindex, vxl->vni); + continue; } + + + zl3vni->local_vtep_ip = vxl->vtep_ip; + if (_pifp) + *_pifp = (void *)ifp; + return NS_WALK_STOP; } - return NULL; + return NS_WALK_CONTINUE; +} + +struct interface *zl3vni_map_to_vxlan_if(zebra_l3vni_t *zl3vni) +{ + struct interface **p_ifp; + struct interface *ifp = NULL; + + p_ifp = &ifp; + + ns_walk_func(zl3vni_map_to_vxlan_if_ns, + (void *)zl3vni, (void **)p_ifp); + return ifp; } struct interface *zl3vni_map_to_svi_if(zebra_l3vni_t *zl3vni) @@ -3987,11 +4065,10 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp, return -1; } - zvrf = vrf_info_lookup(zevpn->vxlan_if->vrf_id); + zvrf = zebra_vrf_get_evpn(); if (!zvrf) { if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug(" No Vrf found for vrf_id: %d", - zevpn->vxlan_if->vrf_id); + zlog_debug(" No Evpn Global Vrf found"); return -1; } @@ -5447,6 +5524,25 @@ stream_failure: return; } +static int macfdb_read_ns(struct ns *ns, + void *_in_param __attribute__((unused)), + void **out_param __attribute__((unused))) +{ + struct zebra_ns *zns = ns->info; + + macfdb_read(zns); + return NS_WALK_CONTINUE; +} + +static int neigh_read_ns(struct ns *ns, + void *_in_param __attribute__((unused)), + void **out_param __attribute__((unused))) +{ + struct zebra_ns *zns = ns->info; + + neigh_read(zns); + return NS_WALK_CONTINUE; +} /* * Handle message from client to learn (or stop learning) about VNIs and MACs. @@ -5499,10 +5595,10 @@ void zebra_vxlan_advertise_all_vni(ZAPI_HANDLER_ARGS) zebra_evpn_gw_macip_add_for_evpn_hash, NULL); /* Read the MAC FDB */ - macfdb_read(zvrf->zns); + ns_walk_func(macfdb_read_ns, NULL, NULL); /* Read neighbors */ - neigh_read(zvrf->zns); + ns_walk_func(neigh_read_ns, NULL, NULL); } else { /* Cleanup VTEPs for all EVPNs - uninstall from * kernel and free entries. |
