diff options
153 files changed, 4916 insertions, 1765 deletions
diff --git a/babeld/babeld.c b/babeld/babeld.c index 4d4dd2e194..b9623b64b5 100644 --- a/babeld/babeld.c +++ b/babeld/babeld.c @@ -819,6 +819,8 @@ babeld_quagga_init(void) install_element(BABEL_NODE, &babel_ipv6_distribute_list_cmd); install_element(BABEL_NODE, &babel_no_ipv6_distribute_list_cmd); + vrf_cmd_init(NULL, &babeld_privs); + babel_if_init(); /* Access list install. */ diff --git a/bgpd/bgp_advertise.c b/bgpd/bgp_advertise.c index 1ebe4e5b53..9da97d110f 100644 --- a/bgpd/bgp_advertise.c +++ b/bgpd/bgp_advertise.c @@ -205,6 +205,7 @@ void bgp_adj_in_remove(struct bgp_dest *dest, struct bgp_adj_in *bai) { bgp_attr_unintern(&bai->attr); BGP_ADJ_IN_DEL(dest, bai); + bgp_dest_unlock_node(dest); peer_unlock(bai->peer); /* adj_in peer reference */ XFREE(MTYPE_BGP_ADJ_IN, bai); } @@ -223,10 +224,8 @@ bool bgp_adj_in_unset(struct bgp_dest *dest, struct peer *peer, while (adj) { adj_next = adj->next; - if (adj->peer == peer && adj->addpath_rx_id == addpath_id) { + if (adj->peer == peer && adj->addpath_rx_id == addpath_id) bgp_adj_in_remove(dest, adj); - bgp_dest_unlock_node(dest); - } adj = adj_next; } diff --git a/bgpd/bgp_aspath.c b/bgpd/bgp_aspath.c index 5cf3c60fa2..25109e030b 100644 --- a/bgpd/bgp_aspath.c +++ b/bgpd/bgp_aspath.c @@ -910,77 +910,70 @@ size_t aspath_put(struct stream *s, struct aspath *as, int use32bit) if (!seg || seg->length == 0) return 0; - if (seg) { - /* - * Hey, what do we do when we have > STREAM_WRITABLE(s) here? - * At the moment, we would write out a partial aspath, and our - * peer - * will complain and drop the session :-/ - * - * The general assumption here is that many things tested will - * never happen. And, in real live, up to now, they have not. - */ - while (seg && (ASSEGMENT_LEN(seg, use32bit) - <= STREAM_WRITEABLE(s))) { - struct assegment *next = seg->next; - int written = 0; - int asns_packed = 0; - size_t lenp; - - /* Overlength segments have to be split up */ - while ((seg->length - written) > AS_SEGMENT_MAX) { - assegment_header_put(s, seg->type, - AS_SEGMENT_MAX); - assegment_data_put(s, (seg->as + written), AS_SEGMENT_MAX, - use32bit); - written += AS_SEGMENT_MAX; - bytes += ASSEGMENT_SIZE(AS_SEGMENT_MAX, - use32bit); - } - - /* write the final segment, probably is also the first - */ - lenp = assegment_header_put(s, seg->type, - seg->length - written); + /* + * Hey, what do we do when we have > STREAM_WRITABLE(s) here? + * At the moment, we would write out a partial aspath, and our + * peer + * will complain and drop the session :-/ + * + * The general assumption here is that many things tested will + * never happen. And, in real live, up to now, they have not. + */ + while (seg && (ASSEGMENT_LEN(seg, use32bit) <= STREAM_WRITEABLE(s))) { + struct assegment *next = seg->next; + int written = 0; + int asns_packed = 0; + size_t lenp; + + /* Overlength segments have to be split up */ + while ((seg->length - written) > AS_SEGMENT_MAX) { + assegment_header_put(s, seg->type, AS_SEGMENT_MAX); assegment_data_put(s, (seg->as + written), - seg->length - written, use32bit); + AS_SEGMENT_MAX, use32bit); + written += AS_SEGMENT_MAX; + bytes += ASSEGMENT_SIZE(AS_SEGMENT_MAX, use32bit); + } - /* Sequence-type segments can be 'packed' together - * Case of a segment which was overlength and split up - * will be missed here, but that doesn't matter. + /* write the final segment, probably is also the first + */ + lenp = assegment_header_put(s, seg->type, + seg->length - written); + assegment_data_put(s, (seg->as + written), + seg->length - written, use32bit); + + /* Sequence-type segments can be 'packed' together + * Case of a segment which was overlength and split up + * will be missed here, but that doesn't matter. + */ + while (next && ASSEGMENTS_PACKABLE(seg, next)) { + /* NB: We should never normally get here given + * we + * normalise aspath data when parse them. + * However, better + * safe than sorry. We potentially could call + * assegment_normalise here instead, but it's + * cheaper and + * easier to do it on the fly here rather than + * go through + * the segment list twice every time we write + * out + * aspath's. */ - while (next && ASSEGMENTS_PACKABLE(seg, next)) { - /* NB: We should never normally get here given - * we - * normalise aspath data when parse them. - * However, better - * safe than sorry. We potentially could call - * assegment_normalise here instead, but it's - * cheaper and - * easier to do it on the fly here rather than - * go through - * the segment list twice every time we write - * out - * aspath's. - */ - - /* Next segment's data can fit in this one */ - assegment_data_put(s, next->as, next->length, - use32bit); - - /* update the length of the segment header */ - stream_putc_at(s, lenp, - seg->length - written - + next->length); - asns_packed += next->length; - - next = next->next; - } - bytes += ASSEGMENT_SIZE( - seg->length - written + asns_packed, use32bit); - seg = next; + /* Next segment's data can fit in this one */ + assegment_data_put(s, next->as, next->length, use32bit); + + /* update the length of the segment header */ + stream_putc_at(s, lenp, + seg->length - written + next->length); + asns_packed += next->length; + + next = next->next; } + + bytes += ASSEGMENT_SIZE(seg->length - written + asns_packed, + use32bit); + seg = next; } return bytes; } diff --git a/bgpd/bgp_damp.c b/bgpd/bgp_damp.c index 3db142b8cf..2a372c0ba4 100644 --- a/bgpd/bgp_damp.c +++ b/bgpd/bgp_damp.c @@ -245,7 +245,6 @@ static int bgp_reuse_timer(struct thread *t) * list head entry. */ assert(bdc->reuse_offset < bdc->reuse_list_size); plist = bdc->reuse_list[bdc->reuse_offset]; - node = SLIST_FIRST(&plist); SLIST_INIT(&bdc->reuse_list[bdc->reuse_offset]); /* 2. set offset = modulo reuse-list-size ( offset + 1 ), thereby @@ -788,7 +787,7 @@ const char *bgp_damp_reuse_time_vty(struct vty *vty, struct bgp_path_info *path, /* If dampening is not enabled or there is no dampening information, return immediately. */ - if (!bdc || !bdi) + if (!bdi) return NULL; /* Calculate new penalty. */ @@ -822,7 +821,7 @@ static int bgp_print_dampening_parameters(struct bgp *bgp, struct vty *vty, } int bgp_show_dampening_parameters(struct vty *vty, afi_t afi, safi_t safi, - uint8_t show_flags) + uint16_t show_flags) { struct bgp *bgp; diff --git a/bgpd/bgp_damp.h b/bgpd/bgp_damp.h index 3c8f138d6a..c03a0cc5c9 100644 --- a/bgpd/bgp_damp.h +++ b/bgpd/bgp_damp.h @@ -166,7 +166,7 @@ extern const char *bgp_damp_reuse_time_vty(struct vty *vty, safi_t safi, bool use_json, json_object *json); extern int bgp_show_dampening_parameters(struct vty *vty, afi_t, safi_t, - uint8_t); + uint16_t); extern void bgp_peer_damp_enable(struct peer *peer, afi_t afi, safi_t safi, time_t half, unsigned int reuse, unsigned int suppress, time_t max); diff --git a/bgpd/bgp_evpn_mh.c b/bgpd/bgp_evpn_mh.c index 59bced6f93..b191840f63 100644 --- a/bgpd/bgp_evpn_mh.c +++ b/bgpd/bgp_evpn_mh.c @@ -2070,9 +2070,8 @@ int bgp_evpn_local_es_del(struct bgp *bgp, esi_t *esi) /* Lookup ESI hash - should exist. */ es = bgp_evpn_es_find(esi); if (!es) { - flog_warn(EC_BGP_EVPN_ESI, - "%u: ES %s missing at local ES DEL", - bgp->vrf_id, es->esi_str); + flog_warn(EC_BGP_EVPN_ESI, "%u: ES missing at local ES DEL", + bgp->vrf_id); return -1; } @@ -3317,9 +3316,6 @@ bgp_evpn_es_evi_local_info_clear(struct bgp_evpn_es_evi *es_evi) { struct bgpevpn *vpn = es_evi->vpn; - if (!CHECK_FLAG(es_evi->flags, BGP_EVPNES_EVI_LOCAL)) - return es_evi; - UNSET_FLAG(es_evi->flags, BGP_EVPNES_EVI_LOCAL); list_delete_node(vpn->local_es_evi_list, &es_evi->l2vni_listnode); diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index fb3ba2c0ec..2a7c2ec853 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -768,13 +768,13 @@ static void bgp_evpn_show_routes_mac_ip_es(struct vty *vty, esi_t *esi, static void bgp_evpn_show_routes_mac_ip_evi_es(struct vty *vty, esi_t *esi, json_object *json, int detail) { - return bgp_evpn_show_routes_mac_ip_es(vty, esi, json, detail, false); + bgp_evpn_show_routes_mac_ip_es(vty, esi, json, detail, false); } static void bgp_evpn_show_routes_mac_ip_global_es(struct vty *vty, esi_t *esi, json_object *json, int detail) { - return bgp_evpn_show_routes_mac_ip_es(vty, esi, json, detail, true); + bgp_evpn_show_routes_mac_ip_es(vty, esi, json, detail, true); } static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn, int type, @@ -4310,7 +4310,7 @@ DEFPY(show_bgp_l2vpn_evpn_nh, * Display EVPN neighbor summary. */ DEFUN(show_bgp_l2vpn_evpn_summary, show_bgp_l2vpn_evpn_summary_cmd, - "show bgp [vrf VRFNAME] l2vpn evpn summary [established|failed] [<neighbor <A.B.C.D|X:X::X:X|WORD>|remote-as <(1-4294967295)|internal|external>>] [wide] [json]", + "show bgp [vrf VRFNAME] l2vpn evpn summary [established|failed] [<neighbor <A.B.C.D|X:X::X:X|WORD>|remote-as <(1-4294967295)|internal|external>>] [terse] [wide] [json]", SHOW_STR BGP_STR "bgp vrf\n" "vrf name\n" L2VPN_HELP_STR EVPN_HELP_STR @@ -4325,6 +4325,7 @@ DEFUN(show_bgp_l2vpn_evpn_summary, show_bgp_l2vpn_evpn_summary_cmd, "AS number\n" "Internal (iBGP) AS sessions\n" "External (eBGP) AS sessions\n" + "Shorten the information on BGP instances\n" "Increase table width for longer output\n" JSON_STR) { int idx_vrf = 0; @@ -4333,7 +4334,7 @@ DEFUN(show_bgp_l2vpn_evpn_summary, show_bgp_l2vpn_evpn_summary_cmd, char *neighbor = NULL; as_t as = 0; /* 0 means AS filter not set */ int as_type = AS_UNSPECIFIED; - uint8_t show_flags = 0; + uint16_t show_flags = 0; if (argv_find(argv, argc, "vrf", &idx_vrf)) vrf = argv[++idx_vrf]->arg; @@ -4357,6 +4358,9 @@ DEFUN(show_bgp_l2vpn_evpn_summary, show_bgp_l2vpn_evpn_summary_cmd, as = (as_t)atoi(argv[idx + 1]->arg); } + if (argv_find(argv, argc, "terse", &idx)) + SET_FLAG(show_flags, BGP_SHOW_OPT_TERSE); + if (argv_find(argv, argc, "wide", &idx)) SET_FLAG(show_flags, BGP_SHOW_OPT_WIDE); diff --git a/bgpd/bgp_flowspec_util.c b/bgpd/bgp_flowspec_util.c index b244c87258..23baa0184e 100644 --- a/bgpd/bgp_flowspec_util.c +++ b/bgpd/bgp_flowspec_util.c @@ -641,13 +641,12 @@ int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len, __func__, type); } } - if (bpem->match_packet_length_num || bpem->match_fragment_num || - bpem->match_tcpflags_num || bpem->match_dscp_num || - bpem->match_packet_length_num || bpem->match_icmp_code_num || - bpem->match_icmp_type_num || bpem->match_port_num || - bpem->match_src_port_num || bpem->match_dst_port_num || - bpem->match_protocol_num || bpem->match_bitmask || - bpem->match_flowlabel_num) + if (bpem->match_packet_length_num || bpem->match_fragment_num + || bpem->match_tcpflags_num || bpem->match_dscp_num + || bpem->match_icmp_code_num || bpem->match_icmp_type_num + || bpem->match_port_num || bpem->match_src_port_num + || bpem->match_dst_port_num || bpem->match_protocol_num + || bpem->match_bitmask || bpem->match_flowlabel_num) bpem->type = BGP_PBR_IPSET; else if ((bpem->match_bitmask_iprule & PREFIX_SRC_PRESENT) || (bpem->match_bitmask_iprule & PREFIX_DST_PRESENT)) diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 8438621f68..cf651185ab 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -125,6 +125,7 @@ static const struct message bgp_pmsi_tnltype_str[] = { }; #define VRFID_NONE_STR "-" +#define SOFT_RECONFIG_TASK_MAX_PREFIX 25000 DEFINE_HOOK(bgp_process, (struct bgp * bgp, afi_t afi, safi_t safi, struct bgp_dest *bn, @@ -3141,6 +3142,14 @@ void bgp_process(struct bgp *bgp, struct bgp_dest *dest, afi_t afi, safi_t safi) return; } + if (CHECK_FLAG(dest->flags, BGP_NODE_SOFT_RECONFIG)) { + if (BGP_DEBUG(update, UPDATE_OUT)) + zlog_debug( + "Soft reconfigure table in progress for route %p", + dest); + return; + } + if (wq == NULL) return; @@ -4591,6 +4600,60 @@ void bgp_announce_route_all(struct peer *peer) bgp_announce_route(peer, afi, safi); } +/* Flag or unflag bgp_dest to determine whether it should be treated by + * bgp_soft_reconfig_table_task. + * Flag if flag is true. Unflag if flag is false. + */ +static void bgp_soft_reconfig_table_flag(struct bgp_table *table, bool flag) +{ + struct bgp_dest *dest; + struct bgp_adj_in *ain; + + if (!table) + return; + + for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) { + for (ain = dest->adj_in; ain; ain = ain->next) { + if (ain->peer != NULL) + break; + } + if (flag && ain != NULL && ain->peer != NULL) + SET_FLAG(dest->flags, BGP_NODE_SOFT_RECONFIG); + else + UNSET_FLAG(dest->flags, BGP_NODE_SOFT_RECONFIG); + } +} + +static int bgp_soft_reconfig_table_update(struct peer *peer, + struct bgp_dest *dest, + struct bgp_adj_in *ain, afi_t afi, + safi_t safi, struct prefix_rd *prd) +{ + struct bgp_path_info *pi; + uint32_t num_labels = 0; + mpls_label_t *label_pnt = NULL; + struct bgp_route_evpn evpn; + + for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) + if (pi->peer == peer) + break; + + if (pi && pi->extra) + num_labels = pi->extra->num_labels; + if (num_labels) + label_pnt = &pi->extra->label[0]; + if (pi) + memcpy(&evpn, bgp_attr_get_evpn_overlay(pi->attr), + sizeof(evpn)); + else + memset(&evpn, 0, sizeof(evpn)); + + return bgp_update(peer, bgp_dest_get_prefix(dest), ain->addpath_rx_id, + ain->attr, afi, safi, ZEBRA_ROUTE_BGP, + BGP_ROUTE_NORMAL, prd, label_pnt, num_labels, 1, + &evpn); +} + static void bgp_soft_reconfig_table(struct peer *peer, afi_t afi, safi_t safi, struct bgp_table *table, struct prefix_rd *prd) @@ -4607,32 +4670,8 @@ static void bgp_soft_reconfig_table(struct peer *peer, afi_t afi, safi_t safi, if (ain->peer != peer) continue; - struct bgp_path_info *pi; - uint32_t num_labels = 0; - mpls_label_t *label_pnt = NULL; - struct bgp_route_evpn evpn; - - for (pi = bgp_dest_get_bgp_path_info(dest); pi; - pi = pi->next) - if (pi->peer == peer) - break; - - if (pi && pi->extra) - num_labels = pi->extra->num_labels; - if (num_labels) - label_pnt = &pi->extra->label[0]; - if (pi) - memcpy(&evpn, - bgp_attr_get_evpn_overlay(pi->attr), - sizeof(evpn)); - else - memset(&evpn, 0, sizeof(evpn)); - - ret = bgp_update(peer, bgp_dest_get_prefix(dest), - ain->addpath_rx_id, ain->attr, afi, - safi, ZEBRA_ROUTE_BGP, - BGP_ROUTE_NORMAL, prd, label_pnt, - num_labels, 1, &evpn); + ret = bgp_soft_reconfig_table_update(peer, dest, ain, + afi, safi, prd); if (ret < 0) { bgp_dest_unlock_node(dest); @@ -4641,18 +4680,201 @@ static void bgp_soft_reconfig_table(struct peer *peer, afi_t afi, safi_t safi, } } +/* Do soft reconfig table per bgp table. + * Walk on SOFT_RECONFIG_TASK_MAX_PREFIX bgp_dest, + * when BGP_NODE_SOFT_RECONFIG is set, + * reconfig bgp_dest for list of table->soft_reconfig_peers peers. + * Schedule a new thread to continue the job. + * Without splitting the full job into several part, + * vtysh waits for the job to finish before responding to a BGP command + */ +static int bgp_soft_reconfig_table_task(struct thread *thread) +{ + uint32_t iter, max_iter; + int ret; + struct bgp_dest *dest; + struct bgp_adj_in *ain; + struct peer *peer; + struct bgp_table *table; + struct prefix_rd *prd; + struct listnode *node, *nnode; + + table = THREAD_ARG(thread); + prd = NULL; + + max_iter = SOFT_RECONFIG_TASK_MAX_PREFIX; + if (table->soft_reconfig_init) { + /* first call of the function with a new srta structure. + * Don't do any treatment this time on nodes + * in order vtysh to respond quickly + */ + max_iter = 0; + } + + for (iter = 0, dest = bgp_table_top(table); (dest && iter < max_iter); + dest = bgp_route_next(dest)) { + if (!CHECK_FLAG(dest->flags, BGP_NODE_SOFT_RECONFIG)) + continue; + + UNSET_FLAG(dest->flags, BGP_NODE_SOFT_RECONFIG); + + for (ain = dest->adj_in; ain; ain = ain->next) { + for (ALL_LIST_ELEMENTS(table->soft_reconfig_peers, node, + nnode, peer)) { + if (ain->peer != peer) + continue; + + ret = bgp_soft_reconfig_table_update( + peer, dest, ain, table->afi, + table->safi, prd); + iter++; + + if (ret < 0) { + bgp_dest_unlock_node(dest); + listnode_delete( + table->soft_reconfig_peers, + peer); + bgp_announce_route(peer, table->afi, + table->safi); + if (list_isempty( + table->soft_reconfig_peers)) { + list_delete( + &table->soft_reconfig_peers); + bgp_soft_reconfig_table_flag( + table, false); + return 0; + } + } + } + } + } + + /* we're either starting the initial iteration, + * or we're going to continue an ongoing iteration + */ + if (dest || table->soft_reconfig_init) { + table->soft_reconfig_init = false; + thread_add_event(bm->master, bgp_soft_reconfig_table_task, + table, 0, &table->soft_reconfig_thread); + return 0; + } + /* we're done, clean up the background iteration context info and + schedule route annoucement + */ + for (ALL_LIST_ELEMENTS(table->soft_reconfig_peers, node, nnode, peer)) { + listnode_delete(table->soft_reconfig_peers, peer); + bgp_announce_route(peer, table->afi, table->safi); + } + + list_delete(&table->soft_reconfig_peers); + + return 0; +} + + +/* Cancel soft_reconfig_table task matching bgp instance, bgp_table + * and peer. + * - bgp cannot be NULL + * - if table and peer are NULL, cancel all threads within the bgp instance + * - if table is NULL and peer is not, + * remove peer in all threads within the bgp instance + * - if peer is NULL, cancel all threads matching table within the bgp instance + */ +void bgp_soft_reconfig_table_task_cancel(const struct bgp *bgp, + const struct bgp_table *table, + const struct peer *peer) +{ + struct peer *npeer; + struct listnode *node, *nnode; + int afi, safi; + struct bgp_table *ntable; + + if (!bgp) + return; + + FOREACH_AFI_SAFI (afi, safi) { + ntable = bgp->rib[afi][safi]; + if (!ntable) + continue; + if (table && table != ntable) + continue; + + for (ALL_LIST_ELEMENTS(ntable->soft_reconfig_peers, node, nnode, + npeer)) { + if (peer && peer != npeer) + continue; + listnode_delete(ntable->soft_reconfig_peers, npeer); + } + + if (!ntable->soft_reconfig_peers + || !list_isempty(ntable->soft_reconfig_peers)) + continue; + + list_delete(&ntable->soft_reconfig_peers); + bgp_soft_reconfig_table_flag(ntable, false); + BGP_TIMER_OFF(ntable->soft_reconfig_thread); + } +} + void bgp_soft_reconfig_in(struct peer *peer, afi_t afi, safi_t safi) { struct bgp_dest *dest; struct bgp_table *table; + struct listnode *node, *nnode; + struct peer *npeer; + struct peer_af *paf; if (!peer_established(peer)) return; if ((safi != SAFI_MPLS_VPN) && (safi != SAFI_ENCAP) - && (safi != SAFI_EVPN)) - bgp_soft_reconfig_table(peer, afi, safi, NULL, NULL); - else + && (safi != SAFI_EVPN)) { + table = peer->bgp->rib[afi][safi]; + if (!table) + return; + + table->soft_reconfig_init = true; + + if (!table->soft_reconfig_peers) + table->soft_reconfig_peers = list_new(); + npeer = NULL; + /* add peer to the table soft_reconfig_peers if not already + * there + */ + for (ALL_LIST_ELEMENTS(table->soft_reconfig_peers, node, nnode, + npeer)) { + if (peer == npeer) + break; + } + if (peer != npeer) + listnode_add(table->soft_reconfig_peers, peer); + + /* (re)flag all bgp_dest in table. Existing soft_reconfig_in job + * on table would start back at the beginning. + */ + bgp_soft_reconfig_table_flag(table, true); + + if (!table->soft_reconfig_thread) + thread_add_event(bm->master, + bgp_soft_reconfig_table_task, table, 0, + &table->soft_reconfig_thread); + /* Cancel bgp_announce_route_timer_expired threads. + * bgp_announce_route_timer_expired threads have been scheduled + * to announce routes as soon as the soft_reconfigure process + * finishes. + * In this case, soft_reconfigure is also scheduled by using + * a thread but is planned after the + * bgp_announce_route_timer_expired threads. It means that, + * without cancelling the threads, the route announcement task + * would run before the soft reconfiguration one. That would + * useless and would block vtysh during several seconds. Route + * announcements are rescheduled as soon as the soft_reconfigure + * process finishes. + */ + paf = peer_af_find(peer, afi, safi); + if (paf) + bgp_stop_announce_route_timer(paf); + } else for (dest = bgp_table_top(peer->bgp->rib[afi][safi]); dest; dest = bgp_route_next(dest)) { table = bgp_dest_get_bgp_table_info(dest); @@ -4826,10 +5048,8 @@ static void bgp_clear_route_table(struct peer *peer, afi_t afi, safi_t safi, while (ain) { ain_next = ain->next; - if (ain->peer == peer) { + if (ain->peer == peer) bgp_adj_in_remove(dest, ain); - bgp_dest_unlock_node(dest); - } ain = ain_next; } @@ -4935,10 +5155,8 @@ void bgp_clear_adj_in(struct peer *peer, afi_t afi, safi_t safi) while (ain) { ain_next = ain->next; - if (ain->peer == peer) { + if (ain->peer == peer) bgp_adj_in_remove(dest, ain); - bgp_dest_unlock_node(dest); - } ain = ain_next; } @@ -5964,6 +6182,7 @@ void bgp_static_add(struct bgp *bgp) struct bgp_table *table; struct bgp_static *bgp_static; + SET_FLAG(bgp->flags, BGP_FLAG_FORCE_STATIC_PROCESS); FOREACH_AFI_SAFI (afi, safi) for (dest = bgp_table_top(bgp->route[afi][safi]); dest; dest = bgp_route_next(dest)) { @@ -5990,6 +6209,7 @@ void bgp_static_add(struct bgp *bgp) safi); } } + UNSET_FLAG(bgp->flags, BGP_FLAG_FORCE_STATIC_PROCESS); } /* Called from bgp_delete(). Delete all static routes from the BGP @@ -10569,13 +10789,13 @@ 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, uint8_t show_flags); + safi_t safi, uint16_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, char *rd, int is_last, unsigned long *output_cum, unsigned long *total_cum, - unsigned long *json_header_depth, uint8_t show_flags, + unsigned long *json_header_depth, uint16_t show_flags, enum rpki_states rpki_target_state) { struct bgp_path_info *pi; @@ -10994,7 +11214,7 @@ 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; + uint16_t show_flags = 0; show_msg = (!use_json && type == bgp_show_type_normal); @@ -11036,7 +11256,7 @@ int bgp_show_table_rd(struct vty *vty, struct bgp *bgp, safi_t safi, } static int bgp_show(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, enum bgp_show_type type, void *output_arg, - uint8_t show_flags, enum rpki_states rpki_target_state) + uint16_t show_flags, enum rpki_states rpki_target_state) { struct bgp_table *table; unsigned long json_header_depth = 0; @@ -11076,7 +11296,7 @@ static int bgp_show(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, } static void bgp_show_all_instances_routes_vty(struct vty *vty, afi_t afi, - safi_t safi, uint8_t show_flags) + safi_t safi, uint16_t show_flags) { struct listnode *node, *nnode; struct bgp *bgp; @@ -11582,7 +11802,7 @@ 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; + uint16_t show_flags = 0; int ret; if (uj) @@ -11625,7 +11845,7 @@ 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; + uint16_t show_flags = 0; if (uj) SET_FLAG(show_flags, BGP_SHOW_OPT_JSON); @@ -11705,7 +11925,7 @@ 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; + uint16_t show_flags = 0; if (uj) { argc--; @@ -11909,7 +12129,7 @@ DEFPY(show_ip_bgp, show_ip_bgp_cmd, int exact_match = 0; struct bgp *bgp = NULL; int idx = 0; - uint8_t show_flags = 0; + uint16_t show_flags = 0; /* [<ipv4|ipv6> [all]] */ if (all) { @@ -12022,7 +12242,7 @@ DEFPY(show_ip_bgp_json, show_ip_bgp_json_cmd, char *prefix_version = NULL; char *bgp_community_alias = NULL; bool first = true; - uint8_t show_flags = 0; + uint16_t show_flags = 0; enum rpki_states rpki_target_state = RPKI_NOT_BEING_USED; if (uj) { @@ -12343,7 +12563,7 @@ DEFPY (show_ip_bgp_instance_all, safi_t safi = SAFI_UNICAST; struct bgp *bgp = NULL; int idx = 0; - uint8_t show_flags = 0; + uint16_t show_flags = 0; if (uj) { argc--; @@ -12368,7 +12588,7 @@ static int bgp_show_regexp(struct vty *vty, struct bgp *bgp, const char *regstr, { regex_t *regex; int rc; - uint8_t show_flags = 0; + uint16_t show_flags = 0; if (use_json) SET_FLAG(show_flags, BGP_SHOW_OPT_JSON); @@ -12396,7 +12616,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; + uint16_t show_flags = 0; plist = prefix_list_lookup(afi, prefix_list_str); if (plist == NULL) { @@ -12414,7 +12634,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; + uint16_t show_flags = 0; as_list = as_list_lookup(filter); if (as_list == NULL) { @@ -12432,7 +12652,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; + uint16_t show_flags = 0; rmap = route_map_lookup_by_name(rmap_str); if (!rmap) { @@ -12446,7 +12666,7 @@ static int bgp_show_route_map(struct vty *vty, struct bgp *bgp, static int bgp_show_community(struct vty *vty, struct bgp *bgp, const char *comstr, int exact, afi_t afi, - safi_t safi, uint8_t show_flags) + safi_t safi, uint16_t show_flags) { struct community *com; int ret = 0; @@ -12471,7 +12691,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; + uint16_t show_flags = 0; list = community_list_lookup(bgp_clist, com, 0, COMMUNITY_LIST_MASTER); if (list == NULL) { @@ -12491,7 +12711,7 @@ static int bgp_show_prefix_longer(struct vty *vty, struct bgp *bgp, { int ret; struct prefix *p; - uint8_t show_flags = 0; + uint16_t show_flags = 0; p = prefix_new(); @@ -13284,7 +13504,7 @@ show_adj_route(struct vty *vty, struct peer *peer, struct bgp_table *table, afi_t afi, safi_t safi, enum bgp_show_adj_route_type type, const char *rmap_name, json_object *json, json_object *json_ar, json_object *json_scode, json_object *json_ocode, - uint8_t show_flags, int *header1, int *header2, char *rd_str, + uint16_t show_flags, int *header1, int *header2, char *rd_str, unsigned long *output_count, unsigned long *filtered_count) { struct bgp_adj_in *ain; @@ -13494,7 +13714,7 @@ show_adj_route(struct vty *vty, struct peer *peer, struct bgp_table *table, 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, uint8_t show_flags) + const char *rmap_name, uint16_t show_flags) { struct bgp *bgp; struct bgp_table *table; @@ -13685,7 +13905,7 @@ 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; + uint16_t show_flags = 0; if (uj) SET_FLAG(show_flags, BGP_SHOW_OPT_JSON); @@ -13741,7 +13961,7 @@ DEFPY (show_ip_bgp_instance_neighbor_advertised_route, enum bgp_show_adj_route_type type = bgp_show_adj_route_advertised; int idx = 0; bool first = true; - uint8_t show_flags = 0; + uint16_t show_flags = 0; if (uj) { argc--; @@ -13923,7 +14143,7 @@ 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; + uint16_t show_flags = 0; if (use_json) SET_FLAG(show_flags, BGP_SHOW_OPT_JSON); @@ -13968,7 +14188,7 @@ 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; + uint16_t show_flags = 0; if (uj) { argc--; diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index 6d6008ff55..3e3b018e83 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -591,6 +591,7 @@ DECLARE_HOOK(bgp_process, #define BGP_SHOW_OPT_ESTABLISHED (1 << 5) #define BGP_SHOW_OPT_FAILED (1 << 6) #define BGP_SHOW_OPT_DETAIL (1 << 7) +#define BGP_SHOW_OPT_TERSE (1 << 8) /* Prototypes. */ extern void bgp_rib_remove(struct bgp_dest *dest, struct bgp_path_info *pi, @@ -603,6 +604,9 @@ extern void bgp_announce_route(struct peer *, afi_t, safi_t); extern void bgp_stop_announce_route_timer(struct peer_af *paf); extern void bgp_announce_route_all(struct peer *); extern void bgp_default_originate(struct peer *, afi_t, safi_t, int); +extern void bgp_soft_reconfig_table_task_cancel(const struct bgp *bgp, + const struct bgp_table *table, + const struct peer *peer); extern void bgp_soft_reconfig_in(struct peer *, afi_t, safi_t); extern void bgp_clear_route(struct peer *, afi_t, safi_t); extern void bgp_clear_route_all(struct peer *); diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index 921a3e0dd9..806a771cfb 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -3767,7 +3767,6 @@ static void bgp_route_map_process_update_cb(char *rmap_name) bgp_route_map_process_update(bgp, rmap_name, 1); #ifdef ENABLE_BGP_VNC - /* zlog_debug("%s: calling vnc_routemap_update", __func__); */ vnc_routemap_update(bgp, __func__); #endif } @@ -3807,12 +3806,14 @@ static void bgp_route_map_mark_update(const char *rmap_name) BGP_POLICY_ROUTE_MAP, rmap_name, 1, 1); } else { - for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) + for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) { bgp_route_map_process_update(bgp, rmap_name, 0); #ifdef ENABLE_BGP_VNC - zlog_debug("%s: calling vnc_routemap_update", __func__); - vnc_routemap_update(bgp, __func__); + vnc_routemap_update(bgp, __func__); #endif + } + + vpn_policy_routemap_event(rmap_name); } } diff --git a/bgpd/bgp_table.h b/bgpd/bgp_table.h index 68b460149c..8a5ed2442f 100644 --- a/bgpd/bgp_table.h +++ b/bgpd/bgp_table.h @@ -31,6 +31,7 @@ #include "linklist.h" #include "bgpd.h" #include "bgp_advertise.h" +#include "bgpd/bgp_trace.h" struct bgp_table { /* table belongs to this instance */ @@ -42,6 +43,13 @@ struct bgp_table { int lock; + /* soft_reconfig_table in progress */ + bool soft_reconfig_init; + struct thread *soft_reconfig_thread; + + /* list of peers on which soft_reconfig_table has to run */ + struct list *soft_reconfig_peers; + struct route_table *route_table; uint64_t version; }; @@ -96,7 +104,7 @@ struct bgp_node { mpls_label_t local_label; - uint8_t flags; + uint16_t flags; #define BGP_NODE_PROCESS_SCHEDULED (1 << 0) #define BGP_NODE_USER_CLEAR (1 << 1) #define BGP_NODE_LABEL_CHANGED (1 << 2) @@ -105,6 +113,7 @@ struct bgp_node { #define BGP_NODE_FIB_INSTALL_PENDING (1 << 5) #define BGP_NODE_FIB_INSTALLED (1 << 6) #define BGP_NODE_LABEL_REQUESTED (1 << 7) +#define BGP_NODE_SOFT_RECONFIG (1 << 8) struct bgp_addpath_node_data tx_addpath; @@ -175,6 +184,7 @@ static inline struct bgp_dest *bgp_dest_parent_nolock(struct bgp_dest *dest) */ static inline void bgp_dest_unlock_node(struct bgp_dest *dest) { + frrtrace(1, frr_bgp, bgp_dest_unlock, dest); bgp_delete_listnode(dest); route_unlock_node(bgp_dest_to_rnode(dest)); } @@ -248,6 +258,7 @@ bgp_node_lookup(const struct bgp_table *const table, const struct prefix *p) */ static inline struct bgp_dest *bgp_dest_lock_node(struct bgp_dest *dest) { + frrtrace(1, frr_bgp, bgp_dest_lock, dest); struct route_node *rn = route_lock_node(bgp_dest_to_rnode(dest)); return bgp_dest_from_rnode(rn); diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index a78afdd871..de4f5a59b6 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -10901,7 +10901,7 @@ static bool bgp_show_summary_is_peer_filtered(struct peer *peer, /* Show BGP peer's summary information. */ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, struct peer *fpeer, int as_type, as_t as, - uint8_t show_flags) + uint16_t show_flags) { struct peer *peer; struct listnode *node, *nnode; @@ -10910,6 +10910,7 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, char neighbor_buf[VTY_BUFSIZ]; int neighbor_col_default_width = 16; int len, failed_count = 0; + unsigned int filtered_count = 0; int max_neighbor_width = 0; int pfx_rcd_safi; json_object *json = NULL; @@ -10922,6 +10923,7 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, bool show_established = CHECK_FLAG(show_flags, BGP_SHOW_OPT_ESTABLISHED); bool show_wide = CHECK_FLAG(show_flags, BGP_SHOW_OPT_WIDE); + bool show_terse = CHECK_FLAG(show_flags, BGP_SHOW_OPT_TERSE); /* labeled-unicast routes are installed in the unicast table so in order * to @@ -10939,6 +10941,7 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { if (bgp_show_summary_is_peer_filtered(peer, fpeer, as_type, as)) { + filtered_count++; count++; continue; } @@ -10964,6 +10967,7 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { if (bgp_show_summary_is_peer_filtered(peer, fpeer, as_type, as)) { + filtered_count++; count++; continue; } @@ -11018,12 +11022,12 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, json_object_free(json); } else { vty_out(vty, "%% No failed BGP neighbors found\n"); - vty_out(vty, "\nTotal number of neighbors %d\n", count); } return CMD_SUCCESS; } count = 0; /* Reset the value as its used again */ + filtered_count = 0; for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { if (!CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE)) continue; @@ -11180,63 +11184,75 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, json_object_boolean_true_add( json, "dampeningEnabled"); } else { - if (bgp_maxmed_onstartup_configured(bgp) - && bgp->maxmed_active) - vty_out(vty, - "Max-med on-startup active\n"); - if (bgp->v_maxmed_admin) - vty_out(vty, - "Max-med administrative active\n"); + if (!show_terse) { + if (bgp_maxmed_onstartup_configured(bgp) + && bgp->maxmed_active) + vty_out(vty, + "Max-med on-startup active\n"); + if (bgp->v_maxmed_admin) + vty_out(vty, + "Max-med administrative active\n"); - vty_out(vty, "BGP table version %" PRIu64 "\n", - bgp_table_version(bgp->rib[afi][safi])); + vty_out(vty, + "BGP table version %" PRIu64 + "\n", + bgp_table_version( + bgp->rib[afi][safi])); - ents = bgp_table_count(bgp->rib[afi][safi]); - vty_out(vty, - "RIB entries %ld, using %s of memory\n", - ents, - mtype_memstr( - memstrbuf, sizeof(memstrbuf), - ents - * sizeof(struct - bgp_dest))); - - /* Peer related usage */ - ents = bgp->af_peer_count[afi][safi]; - vty_out(vty, "Peers %ld, using %s of memory\n", - ents, - mtype_memstr( - memstrbuf, sizeof(memstrbuf), - ents * sizeof(struct peer))); + ents = bgp_table_count( + bgp->rib[afi][safi]); + vty_out(vty, + "RIB entries %ld, using %s of memory\n", + ents, + mtype_memstr( + memstrbuf, + sizeof(memstrbuf), + ents + * sizeof( + struct + bgp_dest))); - if ((ents = listcount(bgp->group))) + /* Peer related usage */ + ents = bgp->af_peer_count[afi][safi]; vty_out(vty, - "Peer groups %ld, using %s of memory\n", + "Peers %ld, using %s of memory\n", ents, mtype_memstr( memstrbuf, sizeof(memstrbuf), - ents * sizeof(struct - peer_group))); + ents + * sizeof( + struct + peer))); - if (CHECK_FLAG(bgp->af_flags[afi][safi], - BGP_CONFIG_DAMPENING)) - vty_out(vty, "Dampening enabled.\n"); - vty_out(vty, "\n"); + if ((ents = listcount(bgp->group))) + vty_out(vty, + "Peer groups %ld, using %s of memory\n", + ents, + mtype_memstr( + memstrbuf, + sizeof(memstrbuf), + ents + * sizeof( + struct + peer_group))); + + if (CHECK_FLAG(bgp->af_flags[afi][safi], + BGP_CONFIG_DAMPENING)) + vty_out(vty, + "Dampening enabled.\n"); + } + if (show_failed) { + vty_out(vty, "\n"); - /* Subtract 8 here because 'Neighbor' is - * 8 characters */ - vty_out(vty, "Neighbor"); - vty_out(vty, "%*s", max_neighbor_width - 8, - " "); - if (show_failed) + /* Subtract 8 here because 'Neighbor' is + * 8 characters */ + vty_out(vty, "Neighbor"); + vty_out(vty, "%*s", + max_neighbor_width - 8, " "); vty_out(vty, BGP_SHOW_SUMMARY_HEADER_FAILED); - else - vty_out(vty, - show_wide - ? BGP_SHOW_SUMMARY_HEADER_ALL_WIDE - : BGP_SHOW_SUMMARY_HEADER_ALL); + } } } @@ -11250,11 +11266,11 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, if (use_json) { json_peer = NULL; - if (bgp_show_summary_is_peer_filtered(peer, fpeer, - as_type, as)) + as_type, as)) { + filtered_count++; continue; - + } if (show_failed && bgp_has_peer_failed(peer, afi, safi)) { json_peer = json_object_new_object(); @@ -11262,8 +11278,10 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, json_peer, 0, use_json); } else if (!show_failed) { if (show_established - && bgp_has_peer_failed(peer, afi, safi)) + && bgp_has_peer_failed(peer, afi, safi)) { + filtered_count++; continue; + } json_peer = json_object_new_object(); if (peer_dynamic_neighbor(peer)) { @@ -11405,8 +11423,10 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, json_peer); } else { if (bgp_show_summary_is_peer_filtered(peer, fpeer, - as_type, as)) + as_type, as)) { + filtered_count++; continue; + } if (show_failed && bgp_has_peer_failed(peer, afi, safi)) { bgp_show_failed_summary(vty, bgp, peer, NULL, @@ -11414,8 +11434,27 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, use_json); } else if (!show_failed) { if (show_established - && bgp_has_peer_failed(peer, afi, safi)) + && bgp_has_peer_failed(peer, afi, safi)) { + filtered_count++; continue; + } + + if ((count - filtered_count) == 1) { + /* display headline before the first + * neighbor line */ + vty_out(vty, "\n"); + + /* Subtract 8 here because 'Neighbor' is + * 8 characters */ + vty_out(vty, "Neighbor"); + vty_out(vty, "%*s", + max_neighbor_width - 8, " "); + vty_out(vty, + show_wide + ? BGP_SHOW_SUMMARY_HEADER_ALL_WIDE + : BGP_SHOW_SUMMARY_HEADER_ALL); + } + memset(dn_flag, '\0', sizeof(dn_flag)); if (peer_dynamic_neighbor(peer)) { dn_flag[0] = '*'; @@ -11540,6 +11579,8 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, if (use_json) { json_object_object_add(json, "peers", json_peers); json_object_int_add(json, "failedPeers", failed_count); + json_object_int_add(json, "displayedPeers", + count - filtered_count); json_object_int_add(json, "totalPeers", count); json_object_int_add(json, "dynamicPeers", dn_count); @@ -11550,9 +11591,22 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, json, JSON_C_TO_STRING_PRETTY)); json_object_free(json); } else { - if (count) - vty_out(vty, "\nTotal number of neighbors %d\n", count); - else { + if (count) { + if (filtered_count == count) + vty_out(vty, "\n%% No matching neighbor\n"); + else { + if (show_failed) + vty_out(vty, "\nDisplayed neighbors %d", + failed_count); + else if (as_type != AS_UNSPECIFIED || as + || fpeer || show_established) + vty_out(vty, "\nDisplayed neighbors %d", + count - filtered_count); + + vty_out(vty, "\nTotal number of neighbors %d\n", + count); + } + } else { vty_out(vty, "No %s neighbor is configured\n", get_afi_safi_str(afi, safi, false)); } @@ -11569,7 +11623,7 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, static void bgp_show_summary_afi_safi(struct vty *vty, struct bgp *bgp, int afi, int safi, struct peer *fpeer, int as_type, - as_t as, uint8_t show_flags) + as_t as, uint16_t show_flags) { int is_first = 1; int afi_wildcard = (afi == AFI_MAX); @@ -11607,10 +11661,12 @@ static void bgp_show_summary_afi_safi(struct vty *vty, struct bgp *bgp, int afi, safi, true)); } else { - vty_out(vty, "\n%s Summary:\n", + vty_out(vty, + "\n%s Summary (%s):\n", get_afi_safi_str(afi, safi, - false)); + false), + bgp->name_pretty); } } bgp_show_summary(vty, bgp, afi, safi, fpeer, @@ -11631,7 +11687,8 @@ static void bgp_show_summary_afi_safi(struct vty *vty, struct bgp *bgp, int afi, if (use_json) vty_out(vty, "{}\n"); else - vty_out(vty, "%% No BGP neighbors found\n"); + vty_out(vty, "%% No BGP neighbors found in %s\n", + bgp->name_pretty); } } @@ -11639,7 +11696,7 @@ static void bgp_show_all_instances_summary_vty(struct vty *vty, afi_t afi, safi_t safi, const char *neighbor, int as_type, as_t as, - uint8_t show_flags) + uint16_t show_flags) { struct listnode *node, *nnode; struct bgp *bgp; @@ -11663,11 +11720,6 @@ static void bgp_show_all_instances_summary_vty(struct vty *vty, afi_t afi, (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT) ? VRF_DEFAULT_NAME : bgp->name); - } else { - vty_out(vty, "\nInstance %s:\n", - (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT) - ? VRF_DEFAULT_NAME - : bgp->name); } if (neighbor) { fpeer = peer_lookup_in_view(vty, bgp, neighbor, @@ -11687,7 +11739,7 @@ static void bgp_show_all_instances_summary_vty(struct vty *vty, afi_t afi, int bgp_show_summary_vty(struct vty *vty, const char *name, afi_t afi, safi_t safi, const char *neighbor, int as_type, - as_t as, uint8_t show_flags) + as_t as, uint16_t show_flags) { struct bgp *bgp; bool use_json = CHECK_FLAG(show_flags, BGP_SHOW_OPT_JSON); @@ -11749,7 +11801,7 @@ int bgp_show_summary_vty(struct vty *vty, const char *name, afi_t afi, 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 - "]] [all$all] summary [established|failed] [<neighbor <A.B.C.D|X:X::X:X|WORD>|remote-as <(1-4294967295)|internal|external>>] [wide] [json$uj]", + "]] [all$all] summary [established|failed] [<neighbor <A.B.C.D|X:X::X:X|WORD>|remote-as <(1-4294967295)|internal|external>>] [terse] [wide] [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" @@ -11764,6 +11816,7 @@ DEFPY(show_ip_bgp_summary, show_ip_bgp_summary_cmd, "AS number\n" "Internal (iBGP) AS sessions\n" "External (eBGP) AS sessions\n" + "Shorten the information on BGP instances\n" "Increase table width for longer output\n" JSON_STR) { char *vrf = NULL; @@ -11771,7 +11824,7 @@ DEFPY(show_ip_bgp_summary, show_ip_bgp_summary_cmd, safi_t safi = SAFI_MAX; as_t as = 0; /* 0 means AS filter not set */ int as_type = AS_UNSPECIFIED; - uint8_t show_flags = 0; + uint16_t show_flags = 0; int idx = 0; @@ -11806,6 +11859,9 @@ DEFPY(show_ip_bgp_summary, show_ip_bgp_summary_cmd, as = (as_t)atoi(argv[idx + 1]->arg); } + if (argv_find(argv, argc, "terse", &idx)) + SET_FLAG(show_flags, BGP_SHOW_OPT_TERSE); + if (argv_find(argv, argc, "wide", &idx)) SET_FLAG(show_flags, BGP_SHOW_OPT_WIDE); diff --git a/bgpd/bgp_vty.h b/bgpd/bgp_vty.h index 2531488d0d..04a47f6f62 100644 --- a/bgpd/bgp_vty.h +++ b/bgpd/bgp_vty.h @@ -186,7 +186,7 @@ int bgp_vty_find_and_parse_bgp(struct vty *vty, struct cmd_token **argv, int argc, struct bgp **bgp, bool use_json); extern int bgp_show_summary_vty(struct vty *vty, const char *name, afi_t afi, safi_t safi, const char *neighbor, int as_type, - as_t as, uint8_t show_flags); + as_t as, uint16_t show_flags); extern int bgp_clear_star_soft_in(const char *name, char *errmsg, size_t errmsg_len); extern int bgp_clear_star_soft_out(const char *name, char *errmsg, diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 81375e37ed..49562e5874 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -892,6 +892,8 @@ int peer_af_delete(struct peer *peer, afi_t afi, safi_t safi) return -1; bgp = peer->bgp; + bgp_soft_reconfig_table_task_cancel(bgp, bgp->rib[afi][safi], peer); + bgp_stop_announce_route_timer(af); if (PAF_SUBGRP(af)) { @@ -2406,6 +2408,8 @@ int peer_delete(struct peer *peer) bgp = peer->bgp; accept_peer = CHECK_FLAG(peer->sflags, PEER_STATUS_ACCEPT_PEER); + bgp_soft_reconfig_table_task_cancel(bgp, NULL, peer); + bgp_keepalives_off(peer); bgp_reads_off(peer); bgp_writes_off(peer); @@ -3572,6 +3576,8 @@ int bgp_delete(struct bgp *bgp) assert(bgp); + bgp_soft_reconfig_table_task_cancel(bgp, NULL, NULL); + /* make sure we withdraw any exported routes */ vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, AFI_IP, bgp_get_default(), bgp); @@ -7885,7 +7891,8 @@ struct peer *peer_lookup_in_view(struct vty *vty, struct bgp *bgp, json_no, JSON_C_TO_STRING_PRETTY)); json_object_free(json_no); } else - vty_out(vty, "No such neighbor in this view/vrf\n"); + vty_out(vty, "No such neighbor in %s\n", + bgp->name_pretty); return NULL; } diff --git a/bgpd/rfapi/rfapi.c b/bgpd/rfapi/rfapi.c index 8c455c6ea5..f89ef7b0d2 100644 --- a/bgpd/rfapi/rfapi.c +++ b/bgpd/rfapi/rfapi.c @@ -2179,7 +2179,7 @@ int rfapi_close(void *handle) vnc_zlog_debug_verbose("%s administrative close rfd=%p", __func__, rfd); - if (h && h->rfp_methods.close_cb) { + if (h->rfp_methods.close_cb) { vnc_zlog_debug_verbose( "%s calling close callback rfd=%p", __func__, rfd); diff --git a/bgpd/rfapi/rfapi_import.c b/bgpd/rfapi/rfapi_import.c index b2732a40b4..51e051d688 100644 --- a/bgpd/rfapi/rfapi_import.c +++ b/bgpd/rfapi/rfapi_import.c @@ -2592,10 +2592,8 @@ static void rfapiCopyUnEncap2VPN(struct bgp_path_info *encap_bpi, * instrumentation to debug segfault of 091127 */ vnc_zlog_debug_verbose("%s: vpn_bpi=%p", __func__, vpn_bpi); - if (vpn_bpi) { - vnc_zlog_debug_verbose("%s: vpn_bpi->extra=%p", - __func__, vpn_bpi->extra); - } + vnc_zlog_debug_verbose("%s: vpn_bpi->extra=%p", __func__, + vpn_bpi->extra); vpn_bpi->extra->vnc.import.un_family = AF_INET; vpn_bpi->extra->vnc.import.un.addr4 = diff --git a/doc/developer/topotests.rst b/doc/developer/topotests.rst index 8885dcfce3..ba03aa9045 100644 --- a/doc/developer/topotests.rst +++ b/doc/developer/topotests.rst @@ -312,6 +312,20 @@ Here's an example of launching ``zebra`` and ``bgpd`` inside ``gdb`` on router --gdb-breakpoints=nb_config_diff \ all-protocol-startup +Detecting Memleaks with Valgrind +"""""""""""""""""""""""""""""""" + +Topotest can automatically launch all daemons with ``valgrind`` to check for +memleaks. This is enabled by specifying 1 or 2 CLI arguments. +``--valgrind-memleaks`` will enable general memleak detection, and +``--valgrind-extra`` enables extra functionality including generating a +suppression file. The suppression file ``tools/valgrind.supp`` is used when +memleak detection is enabled. + +.. code:: shell + + pytest --valgrind-memleaks all-protocol-startup + .. _topotests_docker: Running Tests with Docker diff --git a/doc/user/basic.rst b/doc/user/basic.rst index 3e85a08179..452794e3a8 100644 --- a/doc/user/basic.rst +++ b/doc/user/basic.rst @@ -66,6 +66,10 @@ Basic Config Commands Set domainname of the router. It is only for current ``vtysh``, it will not be saved to any configuration file even with ``write file``. +.. clicmd:: domainname DOMAINNAME + + Set domainname of the router. + .. clicmd:: password PASSWORD Set password for vty interface. The ``no`` form of the command deletes the diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index 0e01b8c3e4..4e78900e8d 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -442,7 +442,7 @@ Require policy on EBGP exit1# show bgp summary - IPv4 Unicast Summary: + IPv4 Unicast Summary (VRF default): BGP router identifier 10.10.10.1, local AS number 65001 vrf-id 0 BGP table version 4 RIB entries 7, using 1344 bytes of memory @@ -3258,7 +3258,7 @@ structure is extended with :clicmd:`show bgp [afi] [safi]`. exit1# show ip bgp summary wide - IPv4 Unicast Summary: + IPv4 Unicast Summary (VRF default): BGP router identifier 192.168.100.1, local AS number 65534 vrf-id 0 BGP table version 3 RIB entries 5, using 920 bytes of memory @@ -3310,6 +3310,13 @@ structure is extended with :clicmd:`show bgp [afi] [safi]`. address-family. The remote-as filter can be used in combination with the failed, established filters. +.. clicmd:: show bgp [afi] [safi] [all] summary terse [json] + + Shorten the output. Do not show the following information about the BGP + instances: the number of RIB entries, the table version and the used memory. + The ``terse`` option can be used in combination with the remote-as, neighbor, + failed and established filters, and with the ``wide`` option as well. + .. clicmd:: show bgp [afi] [safi] [neighbor [PEER] [routes|advertised-routes|received-routes] [json] This command shows information on a specific BGP peer of the relevant diff --git a/doc/user/ospf6d.rst b/doc/user/ospf6d.rst index 59a3af7645..d26062e2b9 100644 --- a/doc/user/ospf6d.rst +++ b/doc/user/ospf6d.rst @@ -18,13 +18,6 @@ OSPF6 router Set router's Router-ID. -.. clicmd:: interface IFNAME area (0-4294967295) - -.. clicmd:: interface IFNAME area A.B.C.D - - Bind interface to specified area, and start sending OSPF packets. `area` can - be specified as 0. - .. clicmd:: timers throttle spf (0-600000) (0-600000) (0-600000) This command sets the initial `delay`, the `initial-holdtime` @@ -108,6 +101,10 @@ The following functionalities are implemented as per RFC 3101: OSPF6 interface =============== +.. clicmd:: ipv6 ospf6 area <A.B.C.D|(0-4294967295)> + + Enable OSPFv3 on the interface and add it to the specified area. + .. clicmd:: ipv6 ospf6 cost COST Sets interface's output cost. Default value depends on the interface @@ -265,12 +262,12 @@ Example of ospf6d configured on one interface and area: .. code-block:: frr interface eth0 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 instance-id 0 ! router ospf6 ospf6 router-id 212.17.55.53 area 0.0.0.0 range 2001:770:105:2::/64 - interface eth0 area 0.0.0.0 ! @@ -282,6 +279,7 @@ Larger example with policy and various options set: debug ospf6 neighbor state ! interface fxp0 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 cost 1 ipv6 ospf6 hello-interval 10 ipv6 ospf6 dead-interval 40 @@ -302,7 +300,6 @@ Larger example with policy and various options set: router ospf6 router-id 255.1.1.1 redistribute static route-map static-ospf6 - interface fxp0 area 0.0.0.0 ! access-list access4 permit 127.0.0.1/32 ! diff --git a/docker/alpine/Dockerfile b/docker/alpine/Dockerfile index cb2b3eb69e..8fc36c0e5f 100644 --- a/docker/alpine/Dockerfile +++ b/docker/alpine/Dockerfile @@ -55,5 +55,14 @@ RUN apk add \ --no-cache \ --allow-untrusted /pkgs/apk/*/*.apk \ && rm -rf /pkgs + +# Own the config / PID files +RUN mkdir -p /var/run/frr +RUN chown -R frr:frr /etc/frr /var/run/frr + +# Simple init manager for reaping processes and forwarding signals +ENTRYPOINT ["/sbin/tini", "--"] + +# Default CMD starts watchfrr COPY docker/alpine/docker-start /usr/lib/frr/docker-start -CMD [ "/sbin/tini", "--", "/usr/lib/frr/docker-start" ] +CMD ["/usr/lib/frr/docker-start"] diff --git a/docker/alpine/docker-start b/docker/alpine/docker-start index 3f7737d3bf..c20df42e8e 100755 --- a/docker/alpine/docker-start +++ b/docker/alpine/docker-start @@ -1,12 +1,4 @@ -#!/bin/sh +#!/bin/ash -set -e - -## -# For volume mounts... -## -chown -R frr:frr /etc/frr || true -/usr/lib/frr/frrinit.sh start - -# Sleep forever -exec tail -f /dev/null +source /usr/lib/frr/frrcommon.sh +/usr/lib/frr/watchfrr $(daemon_list) diff --git a/docker/centos-7/Dockerfile b/docker/centos-7/Dockerfile index 748b5345a1..303a33fe4a 100644 --- a/docker/centos-7/Dockerfile +++ b/docker/centos-7/Dockerfile @@ -39,5 +39,19 @@ COPY --from=centos-7-builder /rpmbuild/RPMS/ /pkgs/rpm/ RUN yum install -y /pkgs/rpm/*/*.rpm \ && rm -rf /pkgs + +# Own the config / PID files +RUN mkdir -p /var/run/frr +RUN chown -R frr:frr /etc/frr /var/run/frr + +# Add tini because no CentOS7 package +ENV TINI_VERSION v0.19.0 +ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /sbin/tini +RUN chmod +x /sbin/tini + +# Simple init manager for reaping processes and forwarding signals +ENTRYPOINT ["/sbin/tini", "--"] + +# Default CMD starts watchfrr COPY docker/centos-7/docker-start /usr/lib/frr/docker-start -CMD [ "/usr/lib/frr/docker-start" ] +CMD ["/usr/lib/frr/docker-start"] diff --git a/docker/centos-7/docker-start b/docker/centos-7/docker-start index a3913245b6..d954142ab9 100755 --- a/docker/centos-7/docker-start +++ b/docker/centos-7/docker-start @@ -1,12 +1,4 @@ -#!/bin/sh +#!/bin/bash -set -e - -## -# Change owner for docker volume mount -## -chown -R frr:frr /etc/frr -/usr/lib/frr/frrinit.sh start - -# Sleep forever -exec tail -f /dev/null +source /usr/lib/frr/frrcommon.sh +/usr/lib/frr/watchfrr $(daemon_list) diff --git a/docker/centos-8/Dockerfile b/docker/centos-8/Dockerfile index e273be055b..8a0c28e13b 100644 --- a/docker/centos-8/Dockerfile +++ b/docker/centos-8/Dockerfile @@ -40,5 +40,19 @@ COPY --from=centos-8-builder /rpmbuild/RPMS/ /pkgs/rpm/ RUN yum install -y /pkgs/rpm/*/*.rpm \ && rm -rf /pkgs + +# Own the config / PID files +RUN mkdir -p /var/run/frr +RUN chown -R frr:frr /etc/frr /var/run/frr + +# Add tini because no CentOS8 package +ENV TINI_VERSION v0.19.0 +ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /sbin/tini +RUN chmod +x /sbin/tini + +# Simple init manager for reaping processes and forwarding signals +ENTRYPOINT ["/sbin/tini", "--"] + +# Default CMD starts watchfrr COPY docker/centos-8/docker-start /usr/lib/frr/docker-start -CMD [ "/usr/lib/frr/docker-start" ] +CMD ["/usr/lib/frr/docker-start"] diff --git a/docker/centos-8/docker-start b/docker/centos-8/docker-start index 935b22209e..d954142ab9 100755 --- a/docker/centos-8/docker-start +++ b/docker/centos-8/docker-start @@ -1,9 +1,4 @@ -#!/bin/sh +#!/bin/bash -set -e - -chown -R frr:frr /etc/frr -/usr/lib/frr/frrinit.sh start - -# Sleep forever -exec tail -f /dev/null +source /usr/lib/frr/frrcommon.sh +/usr/lib/frr/watchfrr $(daemon_list) diff --git a/docker/debian/Dockerfile b/docker/debian/Dockerfile index cc9217f103..7476e5fe3e 100644 --- a/docker/debian/Dockerfile +++ b/docker/debian/Dockerfile @@ -6,8 +6,8 @@ ENV APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=DontWarn RUN apt-get update && \ apt-get install -y libpcre3-dev apt-transport-https ca-certificates curl wget logrotate \ - libc-ares2 libjson-c3 vim procps libreadline7 gnupg2 lsb-release apt-utils && \ - rm -rf /var/lib/apt/lists/* + libc-ares2 libjson-c3 vim procps libreadline7 gnupg2 lsb-release apt-utils \ + tini && rm -rf /var/lib/apt/lists/* RUN curl -s https://deb.frrouting.org/frr/keys.asc | apt-key add - RUN echo deb https://deb.frrouting.org/frr $(lsb_release -s -c) frr-stable | tee -a /etc/apt/sources.list.d/frr.list @@ -16,5 +16,13 @@ RUN apt-get update && \ apt-get install -y frr frr-pythontools && \ rm -rf /var/lib/apt/lists/* -ADD docker-start /usr/sbin/docker-start -CMD ["/usr/sbin/docker-start"] +# Own the config / PID files +RUN mkdir -p /var/run/frr +RUN chown -R frr:frr /etc/frr /var/run/frr + +# Simple init manager for reaping processes and forwarding signals +ENTRYPOINT ["/usr/bin/tini", "--"] + +# Default CMD starts watchfrr +COPY docker-start /usr/lib/frr/docker-start +CMD ["/usr/lib/frr/docker-start"] diff --git a/docker/debian/docker-start b/docker/debian/docker-start index a0f31f5ac5..d954142ab9 100755 --- a/docker/debian/docker-start +++ b/docker/debian/docker-start @@ -1,12 +1,4 @@ -#!/bin/sh +#!/bin/bash -set -e - -## -# For volume mounts... -## -chown -R frr:frr /etc/frr -/etc/init.d/frr start - -# Sleep forever -exec tail -f /dev/null +source /usr/lib/frr/frrcommon.sh +/usr/lib/frr/watchfrr $(daemon_list) diff --git a/eigrpd/eigrp_cli.c b/eigrpd/eigrp_cli.c index 3a978cae33..47de929fc3 100644 --- a/eigrpd/eigrp_cli.c +++ b/eigrpd/eigrp_cli.c @@ -919,6 +919,8 @@ eigrp_cli_init(void) install_element(EIGRP_NODE, &eigrp_neighbor_cmd); install_element(EIGRP_NODE, &eigrp_redistribute_source_metric_cmd); + vrf_cmd_init(NULL, &eigrpd_privs); + install_node(&eigrp_interface_node); if_cmd_init(); diff --git a/isisd/isis_adjacency.c b/isisd/isis_adjacency.c index c1f5e49eca..ffda0f8643 100644 --- a/isisd/isis_adjacency.c +++ b/isisd/isis_adjacency.c @@ -270,7 +270,7 @@ const char *isis_adj_name(const struct isis_adjacency *adj) struct isis_dynhn *dyn; - dyn = dynhn_find_by_id(adj->sysid); + dyn = dynhn_find_by_id(adj->circuit->isis, adj->sysid); if (dyn) return dyn->hostname; else @@ -401,7 +401,7 @@ void isis_adj_print(struct isis_adjacency *adj) if (!adj) return; - dyn = dynhn_find_by_id(adj->sysid); + dyn = dynhn_find_by_id(adj->circuit->isis, adj->sysid); if (dyn) zlog_debug("%s", dyn->hostname); @@ -537,7 +537,7 @@ void isis_adj_print_vty(struct isis_adjacency *adj, struct vty *vty, vty_out(vty, " SNPA: %s", snpa_print(adj->snpa)); if (adj->circuit && (adj->circuit->circ_type == CIRCUIT_T_BROADCAST)) { - dyn = dynhn_find_by_id(adj->lanid); + dyn = dynhn_find_by_id(adj->circuit->isis, adj->lanid); if (dyn) vty_out(vty, ", LAN id: %s.%02x", dyn->hostname, adj->lanid[ISIS_SYS_ID_LEN]); diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c index 4fa28a4ad9..bccb9065f4 100644 --- a/isisd/isis_circuit.c +++ b/isisd/isis_circuit.c @@ -78,12 +78,14 @@ DEFINE_HOOK(isis_circuit_del_hook, (struct isis_circuit *circuit), (circuit)); static void isis_circuit_enable(struct isis_circuit *circuit) { - struct isis_area *area; + struct isis_area *area = circuit->area; struct interface *ifp = circuit->interface; - area = isis_area_lookup(circuit->tag, ifp->vrf_id); - if (area) - isis_area_add_circuit(area, circuit); + if (!area) { + area = isis_area_lookup(circuit->tag, ifp->vrf_id); + if (area) + isis_area_add_circuit(area, circuit); + } if (if_is_operative(ifp)) isis_csm_state_change(IF_UP_FROM_Z, circuit, ifp); @@ -1074,10 +1076,8 @@ static int isis_interface_config_write(struct vty *vty) isis = isis_lookup_by_vrfid(vrf->vrf_id); - if (isis == NULL) { - vty_out(vty, "ISIS routing instance not found"); + if (isis == NULL) return 0; - } FOR_ALL_INTERFACES (vrf, ifp) { /* IF name */ diff --git a/isisd/isis_dynhn.c b/isisd/isis_dynhn.c index decd3e8922..ade6e82220 100644 --- a/isisd/isis_dynhn.c +++ b/isisd/isis_dynhn.c @@ -42,30 +42,29 @@ DEFINE_MTYPE_STATIC(ISISD, ISIS_DYNHN, "ISIS dyn hostname"); -extern struct host host; - -struct list *dyn_cache = NULL; static int dyn_cache_cleanup(struct thread *); void dyn_cache_init(struct isis *isis) { - if (dyn_cache == NULL) - dyn_cache = list_new(); + isis->dyn_cache = list_new(); if (!CHECK_FLAG(im->options, F_ISIS_UNIT_TEST)) thread_add_timer(master, dyn_cache_cleanup, isis, 120, &isis->t_dync_clean); - return; } -void dyn_cache_cleanup_all(void) +void dyn_cache_finish(struct isis *isis) { struct listnode *node, *nnode; struct isis_dynhn *dyn; - for (ALL_LIST_ELEMENTS(dyn_cache, node, nnode, dyn)) { - list_delete_node(dyn_cache, node); + thread_cancel(&isis->t_dync_clean); + + for (ALL_LIST_ELEMENTS(isis->dyn_cache, node, nnode, dyn)) { + list_delete_node(isis->dyn_cache, node); XFREE(MTYPE_ISIS_DYNHN, dyn); } + + list_delete(&isis->dyn_cache); } static int dyn_cache_cleanup(struct thread *thread) @@ -79,10 +78,10 @@ static int dyn_cache_cleanup(struct thread *thread) isis->t_dync_clean = NULL; - for (ALL_LIST_ELEMENTS(dyn_cache, node, nnode, dyn)) { + for (ALL_LIST_ELEMENTS(isis->dyn_cache, node, nnode, dyn)) { if ((now - dyn->refresh) < MAX_LSP_LIFETIME) continue; - list_delete_node(dyn_cache, node); + list_delete_node(isis->dyn_cache, node); XFREE(MTYPE_ISIS_DYNHN, dyn); } @@ -92,54 +91,55 @@ static int dyn_cache_cleanup(struct thread *thread) return ISIS_OK; } -struct isis_dynhn *dynhn_find_by_id(const uint8_t *id) +struct isis_dynhn *dynhn_find_by_id(struct isis *isis, const uint8_t *id) { struct listnode *node = NULL; struct isis_dynhn *dyn = NULL; - for (ALL_LIST_ELEMENTS_RO(dyn_cache, node, dyn)) + for (ALL_LIST_ELEMENTS_RO(isis->dyn_cache, node, dyn)) if (memcmp(dyn->id, id, ISIS_SYS_ID_LEN) == 0) return dyn; return NULL; } -struct isis_dynhn *dynhn_find_by_name(const char *hostname) +struct isis_dynhn *dynhn_find_by_name(struct isis *isis, const char *hostname) { struct listnode *node = NULL; struct isis_dynhn *dyn = NULL; - for (ALL_LIST_ELEMENTS_RO(dyn_cache, node, dyn)) + for (ALL_LIST_ELEMENTS_RO(isis->dyn_cache, node, dyn)) if (strncmp(dyn->hostname, hostname, 255) == 0) return dyn; return NULL; } -void isis_dynhn_insert(const uint8_t *id, const char *hostname, int level) +void isis_dynhn_insert(struct isis *isis, const uint8_t *id, + const char *hostname, int level) { struct isis_dynhn *dyn; - dyn = dynhn_find_by_id(id); + dyn = dynhn_find_by_id(isis, id); if (!dyn) { dyn = XCALLOC(MTYPE_ISIS_DYNHN, sizeof(struct isis_dynhn)); memcpy(dyn->id, id, ISIS_SYS_ID_LEN); dyn->level = level; - listnode_add(dyn_cache, dyn); + listnode_add(isis->dyn_cache, dyn); } snprintf(dyn->hostname, sizeof(dyn->hostname), "%s", hostname); dyn->refresh = time(NULL); } -void isis_dynhn_remove(const uint8_t *id) +void isis_dynhn_remove(struct isis *isis, const uint8_t *id) { struct isis_dynhn *dyn; - dyn = dynhn_find_by_id(id); + dyn = dynhn_find_by_id(isis, id); if (!dyn) return; - listnode_delete(dyn_cache, dyn); + listnode_delete(isis->dyn_cache, dyn); XFREE(MTYPE_ISIS_DYNHN, dyn); } @@ -158,7 +158,7 @@ void dynhn_print_all(struct vty *vty, struct isis *isis) if (!isis->sysid_set) return; vty_out(vty, "Level System ID Dynamic Hostname\n"); - for (ALL_LIST_ELEMENTS_RO(dyn_cache, node, dyn)) { + for (ALL_LIST_ELEMENTS_RO(isis->dyn_cache, node, dyn)) { vty_out(vty, "%-7d", dyn->level); vty_out(vty, "%-15s%-15s\n", sysid_print(dyn->id), dyn->hostname); @@ -169,14 +169,15 @@ void dynhn_print_all(struct vty *vty, struct isis *isis) return; } -struct isis_dynhn *dynhn_snmp_next(const uint8_t *id, int level) +struct isis_dynhn *dynhn_snmp_next(struct isis *isis, const uint8_t *id, + int level) { struct listnode *node = NULL; struct isis_dynhn *dyn = NULL; struct isis_dynhn *found_dyn = NULL; int res; - for (ALL_LIST_ELEMENTS_RO(dyn_cache, node, dyn)) { + for (ALL_LIST_ELEMENTS_RO(isis->dyn_cache, node, dyn)) { res = memcmp(dyn->id, id, ISIS_SYS_ID_LEN); if (res < 0) diff --git a/isisd/isis_dynhn.h b/isisd/isis_dynhn.h index 8d25582e49..afb8b51b1f 100644 --- a/isisd/isis_dynhn.h +++ b/isisd/isis_dynhn.h @@ -31,14 +31,16 @@ struct isis_dynhn { }; void dyn_cache_init(struct isis *isis); -void dyn_cache_cleanup_all(void); -void isis_dynhn_insert(const uint8_t *id, const char *hostname, int level); -void isis_dynhn_remove(const uint8_t *id); -struct isis_dynhn *dynhn_find_by_id(const uint8_t *id); -struct isis_dynhn *dynhn_find_by_name(const char *hostname); +void dyn_cache_finish(struct isis *isis); +void isis_dynhn_insert(struct isis *isis, const uint8_t *id, + const char *hostname, int level); +void isis_dynhn_remove(struct isis *isis, const uint8_t *id); +struct isis_dynhn *dynhn_find_by_id(struct isis *isis, const uint8_t *id); +struct isis_dynhn *dynhn_find_by_name(struct isis *isis, const char *hostname); void dynhn_print_all(struct vty *vty, struct isis *isis); /* Snmp support */ -struct isis_dynhn *dynhn_snmp_next(const uint8_t *id, int level); +struct isis_dynhn *dynhn_snmp_next(struct isis *isis, const uint8_t *id, + int level); #endif /* _ZEBRA_ISIS_DYNHN_H */ diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c index 056e29e8de..814ba8fc2a 100644 --- a/isisd/isis_lsp.c +++ b/isisd/isis_lsp.c @@ -533,11 +533,11 @@ static void lsp_update_data(struct isis_lsp *lsp, struct isis_lsp_hdr *hdr, if (area->dynhostname && lsp->tlvs->hostname && lsp->hdr.rem_lifetime) { - isis_dynhn_insert(lsp->hdr.lsp_id, lsp->tlvs->hostname, - (lsp->hdr.lsp_bits & LSPBIT_IST) - == IS_LEVEL_1_AND_2 - ? IS_LEVEL_2 - : IS_LEVEL_1); + isis_dynhn_insert( + area->isis, lsp->hdr.lsp_id, lsp->tlvs->hostname, + (lsp->hdr.lsp_bits & LSPBIT_IST) == IS_LEVEL_1_AND_2 + ? IS_LEVEL_2 + : IS_LEVEL_1); } return; @@ -700,7 +700,7 @@ void lspid_print(uint8_t *lsp_id, char *dest, size_t dest_len, char dynhost, char id[SYSID_STRLEN]; if (dynhost) - dyn = dynhn_find_by_id(lsp_id); + dyn = dynhn_find_by_id(isis, lsp_id); else dyn = NULL; diff --git a/isisd/isis_misc.c b/isisd/isis_misc.c index d3d081d376..d49ad8485e 100644 --- a/isisd/isis_misc.c +++ b/isisd/isis_misc.c @@ -458,6 +458,7 @@ const char *print_sys_hostname(const uint8_t *sysid) { struct isis_dynhn *dyn; struct isis *isis = NULL; + struct listnode *node; if (!sysid) return "nullsysid"; @@ -467,9 +468,11 @@ const char *print_sys_hostname(const uint8_t *sysid) if (isis && !CHECK_FLAG(im->options, F_ISIS_UNIT_TEST)) return cmd_hostname_get(); - dyn = dynhn_find_by_id(sysid); - if (dyn) - return dyn->hostname; + for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) { + dyn = dynhn_find_by_id(isis, sysid); + if (dyn) + return dyn->hostname; + } return sysid_print(sysid); } diff --git a/isisd/isis_nb_notifications.c b/isisd/isis_nb_notifications.c index 755378a9b7..f219632acf 100644 --- a/isisd/isis_nb_notifications.c +++ b/isisd/isis_nb_notifications.c @@ -315,7 +315,7 @@ void isis_notif_adj_state_change(const struct isis_adjacency *adj, struct yang_data *data; struct isis_circuit *circuit = adj->circuit; struct isis_area *area = circuit->area; - struct isis_dynhn *dyn = dynhn_find_by_id(adj->sysid); + struct isis_dynhn *dyn = dynhn_find_by_id(circuit->isis, adj->sysid); notif_prep_instance_hdr(xpath, area, "default", arguments); notif_prepr_iface_hdr(xpath, circuit, arguments); diff --git a/isisd/isis_snmp.c b/isisd/isis_snmp.c index fa2f9a7669..d530faa151 100644 --- a/isisd/isis_snmp.c +++ b/isisd/isis_snmp.c @@ -1654,6 +1654,10 @@ static uint8_t *isis_snmp_find_router(struct variable *v, oid *name, oid *oid_idx; size_t oid_idx_len; size_t off = 0; + struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); + + if (isis == NULL) + return NULL; *write_method = NULL; @@ -1687,7 +1691,7 @@ static uint8_t *isis_snmp_find_router(struct variable *v, oid *name, cmp_level = (int)oid_idx[ISIS_SYS_ID_LEN + 1]; - dyn = dynhn_find_by_id(cmp_buf); + dyn = dynhn_find_by_id(isis, cmp_buf); if (dyn == NULL || dyn->level != cmp_level) return NULL; @@ -1739,7 +1743,7 @@ static uint8_t *isis_snmp_find_router(struct variable *v, oid *name, */ cmp_level = (int)(IS_LEVEL_2 + 1); - dyn = dynhn_snmp_next(cmp_buf, cmp_level); + dyn = dynhn_snmp_next(isis, cmp_buf, cmp_level); if (dyn == NULL) return NULL; diff --git a/isisd/isisd.c b/isisd/isisd.c index 77b18f9cf7..7e78e0ce69 100644 --- a/isisd/isisd.c +++ b/isisd/isisd.c @@ -229,6 +229,7 @@ void isis_finish(struct isis *isis) isis_redist_free(isis); list_delete(&isis->area_list); + dyn_cache_finish(isis); XFREE(MTYPE_ISIS, isis); } @@ -402,7 +403,7 @@ struct isis_area *isis_area_create(const char *area_tag, const char *vrf_name) continue; circuit = ifp->info; - if (circuit) + if (circuit && strmatch(circuit->tag, area->area_tag)) isis_area_add_circuit(area, circuit); } } @@ -715,6 +716,8 @@ void isis_vrf_init(void) { vrf_init(isis_vrf_new, isis_vrf_enable, isis_vrf_disable, isis_vrf_delete, isis_vrf_enable); + + vrf_cmd_init(NULL, &isisd_privs); } void isis_terminate() @@ -1076,6 +1079,23 @@ DEFUN(show_isis_interface_arg, vrf_name, all_vrf); } +static int id_to_sysid(struct isis *isis, const char *id, uint8_t *sysid) +{ + struct isis_dynhn *dynhn; + + memset(sysid, 0, ISIS_SYS_ID_LEN); + if (id) { + if (sysid2buff(sysid, id) == 0) { + dynhn = dynhn_find_by_name(isis, id); + if (dynhn == NULL) + return -1; + memcpy(sysid, dynhn->id, ISIS_SYS_ID_LEN); + } + } + + return 0; +} + static void isis_neighbor_common(struct vty *vty, const char *id, char detail, struct isis *isis, uint8_t *sysid) { @@ -1131,7 +1151,6 @@ int show_isis_neighbor_common(struct vty *vty, const char *id, char detail, const char *vrf_name, bool all_vrf) { struct listnode *node; - struct isis_dynhn *dynhn; uint8_t sysid[ISIS_SYS_ID_LEN]; struct isis *isis; @@ -1140,29 +1159,27 @@ int show_isis_neighbor_common(struct vty *vty, const char *id, char detail, return CMD_SUCCESS; } - memset(sysid, 0, ISIS_SYS_ID_LEN); - if (id) { - if (sysid2buff(sysid, id) == 0) { - dynhn = dynhn_find_by_name(id); - if (dynhn == NULL) { - vty_out(vty, "Invalid system id %s\n", id); - return CMD_SUCCESS; - } - memcpy(sysid, dynhn->id, ISIS_SYS_ID_LEN); - } - } - if (vrf_name) { if (all_vrf) { for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) { + if (id_to_sysid(isis, id, sysid)) { + vty_out(vty, "Invalid system id %s\n", + id); + return CMD_SUCCESS; + } isis_neighbor_common(vty, id, detail, isis, sysid); } return CMD_SUCCESS; } isis = isis_lookup_by_vrfname(vrf_name); - if (isis != NULL) + if (isis != NULL) { + if (id_to_sysid(isis, id, sysid)) { + vty_out(vty, "Invalid system id %s\n", id); + return CMD_SUCCESS; + } isis_neighbor_common(vty, id, detail, isis, sysid); + } } return CMD_SUCCESS; @@ -1218,7 +1235,6 @@ int clear_isis_neighbor_common(struct vty *vty, const char *id, const char *vrf_ bool all_vrf) { struct listnode *node; - struct isis_dynhn *dynhn; uint8_t sysid[ISIS_SYS_ID_LEN]; struct isis *isis; @@ -1227,27 +1243,27 @@ int clear_isis_neighbor_common(struct vty *vty, const char *id, const char *vrf_ return CMD_SUCCESS; } - memset(sysid, 0, ISIS_SYS_ID_LEN); - if (id) { - if (sysid2buff(sysid, id) == 0) { - dynhn = dynhn_find_by_name(id); - if (dynhn == NULL) { - vty_out(vty, "Invalid system id %s\n", id); - return CMD_SUCCESS; - } - memcpy(sysid, dynhn->id, ISIS_SYS_ID_LEN); - } - } if (vrf_name) { if (all_vrf) { - for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) + for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) { + if (id_to_sysid(isis, id, sysid)) { + vty_out(vty, "Invalid system id %s\n", + id); + return CMD_SUCCESS; + } isis_neighbor_common_clear(vty, id, sysid, isis); + } return CMD_SUCCESS; } isis = isis_lookup_by_vrfname(vrf_name); - if (isis != NULL) + if (isis != NULL) { + if (id_to_sysid(isis, id, sysid)) { + vty_out(vty, "Invalid system id %s\n", id); + return CMD_SUCCESS; + } isis_neighbor_common_clear(vty, id, sysid, isis); + } } return CMD_SUCCESS; @@ -2204,7 +2220,7 @@ struct isis_lsp *lsp_for_arg(struct lspdb_head *head, const char *argv, */ if (sysid2buff(lspid, sysid)) { lsp = lsp_search(head, lspid); - } else if ((dynhn = dynhn_find_by_name(sysid))) { + } else if ((dynhn = dynhn_find_by_name(isis, sysid))) { memcpy(lspid, dynhn->id, ISIS_SYS_ID_LEN); lsp = lsp_search(head, lspid); } else if (strncmp(cmd_hostname_get(), sysid, 15) == 0) { diff --git a/isisd/isisd.h b/isisd/isisd.h index 9d0b57e9f6..b2c9af55b7 100644 --- a/isisd/isisd.h +++ b/isisd/isisd.h @@ -95,6 +95,7 @@ struct isis { struct thread *t_dync_clean; /* dynamic hostname cache cleanup thread */ uint32_t circuit_ids_used[8]; /* 256 bits to track circuit ids 1 through 255 */ int snmp_notifications; + struct list *dyn_cache; struct route_table *ext_info[REDIST_PROTOCOL_COUNT]; }; diff --git a/lib/bitfield.h b/lib/bitfield.h index 244938933b..a3f361ed9d 100644 --- a/lib/bitfield.h +++ b/lib/bitfield.h @@ -60,6 +60,8 @@ typedef unsigned int word_t; */ typedef struct {word_t *data; size_t n, m; } bitfield_t; +DECLARE_MTYPE(BITFIELD); + /** * Initialize the bits. * @v: an instance of bitfield_t struct. @@ -70,7 +72,7 @@ typedef struct {word_t *data; size_t n, m; } bitfield_t; do { \ (v).n = 0; \ (v).m = ((N) / WORD_SIZE + 1); \ - (v).data = calloc(1, ((v).m * sizeof(word_t))); \ + (v).data = XCALLOC(MTYPE_BITFIELD, ((v).m * sizeof(word_t))); \ } while (0) /** @@ -193,7 +195,7 @@ static inline unsigned int bf_find_next_set_bit(bitfield_t v, */ #define bf_free(v) \ do { \ - free((v).data); \ + XFREE(MTYPE_BITFIELD, (v).data); \ (v).data = NULL; \ } while (0) diff --git a/lib/compiler.h b/lib/compiler.h index bbfe01b569..e805eb8be4 100644 --- a/lib/compiler.h +++ b/lib/compiler.h @@ -123,15 +123,6 @@ extern "C" { #define assume(x) #endif -/* pure = function does not modify memory & return value is the same if - * memory hasn't changed (=> allows compiler to optimize) - * - * Mostly autodetected by the compiler if function body is available (i.e. - * static inline functions in headers). Since that implies it should only be - * used in headers for non-inline functions, the "extern" is included here. - */ -#define ext_pure extern __attribute__((pure)) - /* for helper functions defined inside macros */ #define macro_inline static inline __attribute__((unused)) #define macro_pure static inline __attribute__((unused, pure)) @@ -266,20 +266,23 @@ void if_update_to_new_vrf(struct interface *ifp, vrf_id_t vrf_id) char oldpath[XPATH_MAXLEN]; char newpath[XPATH_MAXLEN]; - if_dnode = yang_dnode_getf( - running_config->dnode, - "/frr-interface:lib/interface[name='%s'][vrf='%s']/vrf", - ifp->name, old_vrf->name); + snprintf(oldpath, sizeof(oldpath), + "/frr-interface:lib/interface[name='%s'][vrf='%s']", + ifp->name, old_vrf->name); + snprintf(newpath, sizeof(newpath), + "/frr-interface:lib/interface[name='%s'][vrf='%s']", + ifp->name, vrf->name); + + if_dnode = yang_dnode_getf(running_config->dnode, "%s/vrf", + oldpath); if (if_dnode) { - yang_dnode_get_path(lyd_parent(if_dnode), oldpath, - sizeof(oldpath)); yang_dnode_change_leaf(if_dnode, vrf->name); - yang_dnode_get_path(lyd_parent(if_dnode), newpath, - sizeof(newpath)); nb_running_move_tree(oldpath, newpath); running_config->version++; } + + vty_update_xpath(oldpath, newpath); } } diff --git a/lib/link_state.c b/lib/link_state.c index afeb89c592..e8a6b89f89 100644 --- a/lib/link_state.c +++ b/lib/link_state.c @@ -79,7 +79,6 @@ void ls_node_del(struct ls_node *node) return; XFREE(MTYPE_LS_DB, node); - node = NULL; } int ls_node_same(struct ls_node *n1, struct ls_node *n2) @@ -168,7 +167,6 @@ void ls_attributes_del(struct ls_attributes *attr) ls_attributes_srlg_del(attr); XFREE(MTYPE_LS_DB, attr); - attr = NULL; } int ls_attributes_same(struct ls_attributes *l1, struct ls_attributes *l2) @@ -221,7 +219,6 @@ void ls_prefix_del(struct ls_prefix *pref) return; XFREE(MTYPE_LS_DB, pref); - pref = NULL; } int ls_prefix_same(struct ls_prefix *p1, struct ls_prefix *p2) @@ -839,7 +836,6 @@ void ls_ted_del(struct ls_ted *ted) subnets_fini(&ted->subnets); XFREE(MTYPE_LS_DB, ted); - ted = NULL; } void ls_ted_del_all(struct ls_ted *ted) diff --git a/lib/memory.c b/lib/memory.c index 0dc8e90524..18811777ae 100644 --- a/lib/memory.c +++ b/lib/memory.c @@ -36,6 +36,7 @@ struct memgroup **mg_insert = &mg_first; DEFINE_MGROUP(LIB, "libfrr"); DEFINE_MTYPE(LIB, TMP, "Temporary memory"); +DEFINE_MTYPE(LIB, BITFIELD, "Bitfield memory"); static inline void mt_count_alloc(struct memtype *mt, size_t size, void *ptr) { diff --git a/lib/northbound.c b/lib/northbound.c index 47af770189..6edd5184ef 100644 --- a/lib/northbound.c +++ b/lib/northbound.c @@ -696,14 +696,14 @@ int nb_candidate_edit(struct nb_config *candidate, NULL, LYD_NEW_PATH_UPDATE, &dep_dnode); /* Create default nodes */ - if (!err) + if (!err && dep_dnode) err = lyd_new_implicit_tree( dep_dnode, LYD_IMPLICIT_NO_STATE, NULL); if (err) { flog_warn( EC_LIB_LIBYANG, - "%s: lyd_new_path(%s) failed: %d", + "%s: dependency: lyd_new_path(%s) failed: %d", __func__, dep_xpath, err); return NB_ERR; } diff --git a/lib/northbound_grpc.cpp b/lib/northbound_grpc.cpp index 807d1252c4..71f07dfe86 100644 --- a/lib/northbound_grpc.cpp +++ b/lib/northbound_grpc.cpp @@ -1222,7 +1222,7 @@ void HandleUnaryExecute( frr::NAME##Response>( \ (cdb), &frr::Northbound::AsyncService::Request##NAME, \ &HandleUnary##NAME, #NAME); \ - _rpcState->do_request(service, _cq); \ + _rpcState->do_request(service, s_cq); \ } while (0) #define REQUEST_NEWRPC_STREAMING(NAME, cdb) \ @@ -1231,7 +1231,7 @@ void HandleUnaryExecute( frr::NAME##Response>( \ (cdb), &frr::Northbound::AsyncService::Request##NAME, \ &HandleStreaming##NAME, #NAME); \ - _rpcState->do_request(service, _cq); \ + _rpcState->do_request(service, s_cq); \ } while (0) struct grpc_pthread_attr { @@ -1239,6 +1239,10 @@ struct grpc_pthread_attr { unsigned long port; }; +// Capture these objects so we can try to shut down cleanly +static std::unique_ptr<grpc::Server> s_server; +static grpc::ServerCompletionQueue *s_cq; + static void *grpc_pthread_start(void *arg) { struct frr_pthread *fpt = static_cast<frr_pthread *>(arg); @@ -1249,7 +1253,6 @@ static void *grpc_pthread_start(void *arg) std::stringstream server_address; frr::Northbound::AsyncService *service = new frr::Northbound::AsyncService(); - grpc::ServerCompletionQueue *_cq; frr_pthread_set_name(fpt); @@ -1258,8 +1261,8 @@ static void *grpc_pthread_start(void *arg) grpc::InsecureServerCredentials()); builder.RegisterService(service); auto cq = builder.AddCompletionQueue(); - _cq = cq.get(); - auto server = builder.BuildAndStart(); + s_cq = cq.get(); + s_server = builder.BuildAndStart(); /* Schedule all RPC handlers */ REQUEST_NEWRPC(GetCapabilities, NULL); @@ -1284,10 +1287,12 @@ static void *grpc_pthread_start(void *arg) void *tag; bool ok; - _cq->Next(&tag, &ok); + s_cq->Next(&tag, &ok); + if (!ok) + break; + grpc_debug("%s: Got next from CompletionQueue, %p %d", __func__, tag, ok); - GPR_ASSERT(ok); RpcStateBase *rpc = static_cast<RpcStateBase *>(tag); CallState state = rpc->doCallback(); @@ -1302,10 +1307,9 @@ static void *grpc_pthread_start(void *arg) * user indicating Finish() for cleanup. */ if (state == FINISH) - rpc->do_request(service, _cq); + rpc->do_request(service, s_cq); } - /*NOTREACHED*/ return NULL; } @@ -1326,16 +1330,30 @@ static int frr_grpc_init(uint port) __func__, safe_strerror(errno)); return -1; } - pthread_detach(fpt->thread); return 0; } static int frr_grpc_finish(void) { - if (fpt) + // Shutdown the grpc server + if (s_server) { + s_server->Shutdown(); + s_cq->Shutdown(); + + // And drain the queue + void *ignore; + bool ok; + + while (s_cq->Next(&ignore, &ok)) + ; + } + + if (fpt) { + pthread_join(fpt->thread, NULL); frr_pthread_destroy(fpt); - // TODO: cancel the gRPC pthreads gracefully. + } + return 0; } diff --git a/lib/srv6.c b/lib/srv6.c index ceb769ef76..ccb94b2f76 100644 --- a/lib/srv6.c +++ b/lib/srv6.c @@ -129,6 +129,8 @@ struct srv6_locator *srv6_locator_alloc(const char *name) locator = XCALLOC(MTYPE_SRV6_LOCATOR, sizeof(struct srv6_locator)); strlcpy(locator->name, name, sizeof(locator->name)); locator->chunks = list_new(); + locator->chunks->del = (void (*)(void *))srv6_locator_chunk_free; + QOBJ_REG(locator, srv6_locator); return locator; } @@ -144,7 +146,12 @@ struct srv6_locator_chunk *srv6_locator_chunk_alloc(void) void srv6_locator_free(struct srv6_locator *locator) { - XFREE(MTYPE_SRV6_LOCATOR, locator); + if (locator) { + QOBJ_UNREG(locator); + list_delete(&locator->chunks); + + XFREE(MTYPE_SRV6_LOCATOR, locator); + } } void srv6_locator_chunk_free(struct srv6_locator_chunk *chunk) diff --git a/lib/table.h b/lib/table.h index 7e383dce80..5dec69ee7e 100644 --- a/lib/table.h +++ b/lib/table.h @@ -197,29 +197,25 @@ static inline void route_table_set_info(struct route_table *table, void *d) table->info = d; } -/* ext_pure => extern __attribute__((pure)) - * does not modify memory (but depends on mem), allows compiler to optimize - */ - extern void route_table_finish(struct route_table *table); -ext_pure struct route_node *route_top(struct route_table *table); -ext_pure struct route_node *route_next(struct route_node *node); -ext_pure struct route_node *route_next_until(struct route_node *node, - const struct route_node *limit); +extern struct route_node *route_top(struct route_table *table); +extern struct route_node *route_next(struct route_node *node); +extern struct route_node *route_next_until(struct route_node *node, + const struct route_node *limit); extern struct route_node *route_node_get(struct route_table *table, union prefixconstptr pu); -ext_pure struct route_node *route_node_lookup(struct route_table *table, - union prefixconstptr pu); -ext_pure struct route_node *route_node_lookup_maynull(struct route_table *table, - union prefixconstptr pu); -ext_pure struct route_node *route_node_match(struct route_table *table, - union prefixconstptr pu); -ext_pure struct route_node *route_node_match_ipv4(struct route_table *table, - const struct in_addr *addr); -ext_pure struct route_node *route_node_match_ipv6(struct route_table *table, - const struct in6_addr *addr); - -ext_pure unsigned long route_table_count(struct route_table *table); +extern struct route_node *route_node_lookup(struct route_table *table, + union prefixconstptr pu); +extern struct route_node *route_node_lookup_maynull(struct route_table *table, + union prefixconstptr pu); +extern struct route_node *route_node_match(struct route_table *table, + union prefixconstptr pu); +extern struct route_node *route_node_match_ipv4(struct route_table *table, + const struct in_addr *addr); +extern struct route_node *route_node_match_ipv6(struct route_table *table, + const struct in6_addr *addr); + +extern unsigned long route_table_count(struct route_table *table); extern struct route_node *route_node_create(route_table_delegate_t *delegate, struct route_table *table); @@ -228,10 +224,10 @@ extern void route_node_destroy(route_table_delegate_t *delegate, struct route_table *table, struct route_node *node); -ext_pure struct route_node *route_table_get_next(struct route_table *table, - union prefixconstptr pu); -ext_pure int route_table_prefix_iter_cmp(const struct prefix *p1, - const struct prefix *p2); +extern struct route_node *route_table_get_next(struct route_table *table, + union prefixconstptr pu); +extern int route_table_prefix_iter_cmp(const struct prefix *p1, + const struct prefix *p2); /* * Iterator functions. @@ -582,29 +582,38 @@ void vrf_init(int (*create)(struct vrf *), int (*enable)(struct vrf *), cmd_variable_handler_register(vrf_var_handlers); } +static void vrf_terminate_single(struct vrf *vrf) +{ + /* Clear configured flag and invoke delete. */ + UNSET_FLAG(vrf->status, VRF_CONFIGURED); + vrf_delete(vrf); +} + /* Terminate VRF module. */ void vrf_terminate(void) { - struct vrf *vrf; + struct vrf *vrf, *tmp; if (debug_vrf) zlog_debug("%s: Shutting down vrf subsystem", __func__); - while (!RB_EMPTY(vrf_id_head, &vrfs_by_id)) { - vrf = RB_ROOT(vrf_id_head, &vrfs_by_id); + RB_FOREACH_SAFE (vrf, vrf_id_head, &vrfs_by_id, tmp) { + if (vrf->vrf_id == VRF_DEFAULT) + continue; - /* Clear configured flag and invoke delete. */ - UNSET_FLAG(vrf->status, VRF_CONFIGURED); - vrf_delete(vrf); + vrf_terminate_single(vrf); } - while (!RB_EMPTY(vrf_name_head, &vrfs_by_name)) { - vrf = RB_ROOT(vrf_name_head, &vrfs_by_name); + RB_FOREACH_SAFE (vrf, vrf_name_head, &vrfs_by_name, tmp) { + if (vrf->vrf_id == VRF_DEFAULT) + continue; - /* Clear configured flag and invoke delete. */ - UNSET_FLAG(vrf->status, VRF_CONFIGURED); - vrf_delete(vrf); + vrf_terminate_single(vrf); } + + /* Finally terminate default VRF */ + vrf = vrf_lookup_by_id(VRF_DEFAULT); + vrf_terminate_single(vrf); } int vrf_socket(int domain, int type, int protocol, vrf_id_t vrf_id, @@ -818,10 +827,24 @@ DEFUN_YANG (no_vrf, return CMD_WARNING_CONFIG_FAILED; } + if (vrf_get_backend() == VRF_BACKEND_VRF_LITE) { + /* + * Remove the VRF interface config. Currently, we allow to + * remove only inactive VRFs, so we use VRF_DEFAULT_NAME here, + * because when the VRF is removed from kernel, the interface + * is moved to the default VRF. If we ever allow removing + * active VRFs, this code have to be updated accordingly. + */ + snprintf(xpath_list, sizeof(xpath_list), + "/frr-interface:lib/interface[name='%s'][vrf='%s']", + vrfname, VRF_DEFAULT_NAME); + nb_cli_enqueue_change(vty, xpath_list, NB_OP_DESTROY, NULL); + } + snprintf(xpath_list, sizeof(xpath_list), FRR_VRF_KEY_XPATH, vrfname); nb_cli_enqueue_change(vty, xpath_list, NB_OP_DESTROY, NULL); - return nb_cli_apply_changes(vty, xpath_list); + return nb_cli_apply_changes(vty, NULL); } @@ -82,6 +82,9 @@ extern struct host host; /* Vector which store each vty structure. */ static vector vtyvec; +/* Vector for vtysh connections. */ +static vector vtyshvec; + /* Vty timeout value. */ static unsigned long vty_timeout_val = VTY_TIMEOUT_DEFAULT; @@ -2038,6 +2041,7 @@ static int vtysh_accept(struct thread *thread) vty->wfd = sock; vty->type = VTY_SHELL_SERV; vty->node = VIEW_NODE; + vector_set_index(vtyshvec, sock, vty); vty_event(VTYSH_READ, vty); @@ -2211,8 +2215,12 @@ void vty_close(struct vty *vty) } /* Unset vector. */ - if (vty->fd != -1) - vector_unset(vtyvec, vty->fd); + if (vty->fd != -1) { + if (vty->type == VTY_SHELL_SERV) + vector_unset(vtyshvec, vty->fd); + else + vector_unset(vtyvec, vty->fd); + } if (vty->wfd > 0 && vty->type == VTY_FILE) fsync(vty->wfd); @@ -2571,6 +2579,41 @@ void vty_log_fixed(char *buf, size_t len) } } +static void update_xpath(struct vty *vty, const char *oldpath, + const char *newpath) +{ + int i; + + for (i = 0; i < vty->xpath_index; i++) { + if (!frrstr_startswith(vty->xpath[i], oldpath)) + break; + + char *tmp = frrstr_replace(vty->xpath[i], oldpath, newpath); + strlcpy(vty->xpath[i], tmp, sizeof(vty->xpath[0])); + XFREE(MTYPE_TMP, tmp); + } +} + +void vty_update_xpath(const char *oldpath, const char *newpath) +{ + struct vty *vty; + unsigned int i; + + for (i = 0; i < vector_active(vtyshvec); i++) { + if ((vty = vector_slot(vtyshvec, i)) == NULL) + continue; + + update_xpath(vty, oldpath, newpath); + } + + for (i = 0; i < vector_active(vtyvec); i++) { + if ((vty = vector_slot(vtyvec, i)) == NULL) + continue; + + update_xpath(vty, oldpath, newpath); + } +} + int vty_config_enter(struct vty *vty, bool private_config, bool exclusive) { if (exclusive && nb_running_lock(NB_CLIENT_CLI, vty)) { @@ -3114,6 +3157,7 @@ void vty_init(struct thread_master *master_thread, bool do_command_logging) vty_save_cwd(); vtyvec = vector_init(VECTOR_MIN_SIZE); + vtyshvec = vector_init(VECTOR_MIN_SIZE); vty_master = master_thread; @@ -3165,4 +3209,8 @@ void vty_terminate(void) vtyvec = NULL; Vvty_serv_thread = NULL; } + if (vtyshvec) { + vector_free(vtyshvec); + vtyshvec = NULL; + } } @@ -327,6 +327,7 @@ extern void vty_close(struct vty *); extern char *vty_get_cwd(void); extern void vty_log(const char *level, const char *proto, const char *msg, struct timestamp_control *); +extern void vty_update_xpath(const char *oldpath, const char *newpath); extern int vty_config_enter(struct vty *vty, bool private_config, bool exclusive); extern void vty_config_exit(struct vty *); diff --git a/nhrpd/nhrp_main.c b/nhrpd/nhrp_main.c index c2111a7706..54b7850207 100644 --- a/nhrpd/nhrp_main.c +++ b/nhrpd/nhrp_main.c @@ -118,6 +118,7 @@ static struct quagga_signal_t sighandlers[] = { static const struct frr_yang_module_info *const nhrpd_yang_modules[] = { &frr_filter_info, &frr_interface_info, + &frr_vrf_info, }; FRR_DAEMON_INFO(nhrpd, NHRP, .vty_port = NHRP_VTY_PORT, diff --git a/nhrpd/nhrp_vty.c b/nhrpd/nhrp_vty.c index 420ea12ec1..963fa4d995 100644 --- a/nhrpd/nhrp_vty.c +++ b/nhrpd/nhrp_vty.c @@ -1260,6 +1260,8 @@ void nhrp_config_init(void) install_element(CONFIG_NODE, &nhrp_multicast_nflog_group_cmd); install_element(CONFIG_NODE, &no_nhrp_multicast_nflog_group_cmd); + vrf_cmd_init(NULL, &nhrpd_privs); + /* interface specific commands */ install_node(&nhrp_interface_node); diff --git a/ospf6d/ospf6_abr.c b/ospf6d/ospf6_abr.c index a43118cb21..f289bf26b9 100644 --- a/ospf6d/ospf6_abr.c +++ b/ospf6d/ospf6_abr.c @@ -53,7 +53,7 @@ unsigned char conf_debug_ospf6_abr; -int ospf6_is_router_abr(struct ospf6 *o) +bool ospf6_check_and_set_router_abr(struct ospf6 *o) { struct listnode *node; struct ospf6_area *oa; @@ -74,12 +74,12 @@ int ospf6_is_router_abr(struct ospf6 *o) if (IS_OSPF6_DEBUG_ABR) zlog_debug("%s : set flag OSPF6_FLAG_ABR", __func__); SET_FLAG(o->flag, OSPF6_FLAG_ABR); - return 1; + return true; } else { if (IS_OSPF6_DEBUG_ABR) zlog_debug("%s : reset flag OSPF6_FLAG_ABR", __func__); UNSET_FLAG(o->flag, OSPF6_FLAG_ABR); - return 0; + return false; } } @@ -1359,7 +1359,7 @@ void ospf6_abr_reexport(struct ospf6_area *oa) struct ospf6_route *route; /* if not a ABR return success */ - if (!ospf6_is_router_abr(oa->ospf6)) + if (!ospf6_check_and_set_router_abr(oa->ospf6)) return; /* Redo summaries if required */ diff --git a/ospf6d/ospf6_abr.h b/ospf6d/ospf6_abr.h index 7c1ff4d389..a5f0f124b9 100644 --- a/ospf6d/ospf6_abr.h +++ b/ospf6d/ospf6_abr.h @@ -58,7 +58,7 @@ struct ospf6_inter_router_lsa { #define OSPF6_ABR_RANGE_CLEAR_COST(range) (range->path.cost = OSPF_AREA_RANGE_COST_UNSPEC) #define IS_OSPF6_ABR(o) ((o)->flag & OSPF6_FLAG_ABR) -extern int ospf6_is_router_abr(struct ospf6 *o); +extern bool ospf6_check_and_set_router_abr(struct ospf6 *o); extern void ospf6_abr_enable_area(struct ospf6_area *oa); extern void ospf6_abr_disable_area(struct ospf6_area *oa); diff --git a/ospf6d/ospf6_area.c b/ospf6d/ospf6_area.c index 92934d3764..355b8441bd 100644 --- a/ospf6d/ospf6_area.c +++ b/ospf6d/ospf6_area.c @@ -50,20 +50,28 @@ DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_AREA, "OSPF6 area"); DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_PLISTNAME, "Prefix list name"); -/* Utility functions. */ -int str2area_id(const char *str, struct in_addr *area_id, int *area_id_fmt) +int str2area_id(const char *str, uint32_t *area_id, int *area_id_fmt) { char *ep; - area_id->s_addr = htonl(strtoul(str, &ep, 10)); - if (*ep && !inet_aton(str, area_id)) + *area_id = htonl(strtoul(str, &ep, 10)); + if (*ep && inet_pton(AF_INET, str, area_id) != 1) return -1; - *area_id_fmt = *ep ? OSPF6_AREA_FMT_DECIMAL : OSPF6_AREA_FMT_DOTTEDQUAD; + *area_id_fmt = + !*ep ? OSPF6_AREA_FMT_DECIMAL : OSPF6_AREA_FMT_DOTTEDQUAD; return 0; } +void area_id2str(char *buf, int len, uint32_t area_id, int area_id_fmt) +{ + if (area_id_fmt == OSPF6_AREA_FMT_DECIMAL) + snprintf(buf, len, "%u", ntohl(area_id)); + else + inet_ntop(AF_INET, &area_id, buf, len); +} + int ospf6_area_cmp(void *va, void *vb) { struct ospf6_area *oa = (struct ospf6_area *)va; @@ -538,7 +546,7 @@ DEFUN (area_range, ospf6_route_add(range, oa->range_table); } - if (ospf6_is_router_abr(ospf6)) { + if (ospf6_check_and_set_router_abr(ospf6)) { /* Redo summaries if required */ ospf6_abr_prefix_resummarize(ospf6); } @@ -584,7 +592,7 @@ DEFUN (no_area_range, return CMD_SUCCESS; } - if (ospf6_is_router_abr(oa->ospf6)) { + if (ospf6_check_and_set_router_abr(oa->ospf6)) { /* Blow away the aggregated LSA and route */ SET_FLAG(range->flag, OSPF6_ROUTE_REMOVE); diff --git a/ospf6d/ospf6_area.h b/ospf6d/ospf6_area.h index fa761d732d..dd4d019015 100644 --- a/ospf6d/ospf6_area.h +++ b/ospf6d/ospf6_area.h @@ -31,6 +31,7 @@ struct ospf6_area { /* Area-ID */ in_addr_t area_id; +#define OSPF6_AREA_FMT_UNSET 0 #define OSPF6_AREA_FMT_DOTTEDQUAD 1 #define OSPF6_AREA_FMT_DECIMAL 2 /* Area-ID string */ @@ -130,20 +131,22 @@ struct ospf6_area { #define OSPF6_CMD_AREA_GET(str, oa, ospf6) \ { \ - char *ep; \ - uint32_t area_id = htonl(strtoul(str, &ep, 10)); \ - if (*ep && inet_pton(AF_INET, str, &area_id) != 1) { \ + uint32_t area_id; \ + int format, ret; \ + ret = str2area_id(str, &area_id, &format); \ + if (ret) { \ vty_out(vty, "Malformed Area-ID: %s\n", str); \ - return CMD_SUCCESS; \ + return CMD_WARNING; \ } \ - int format = !*ep ? OSPF6_AREA_FMT_DECIMAL \ - : OSPF6_AREA_FMT_DOTTEDQUAD; \ oa = ospf6_area_lookup(area_id, ospf6); \ if (oa == NULL) \ oa = ospf6_area_create(area_id, ospf6, format); \ } /* prototypes */ +extern int str2area_id(const char *str, uint32_t *area_id, int *area_id_fmt); +extern void area_id2str(char *buf, int len, uint32_t area_id, int area_id_fmt); + extern int ospf6_area_cmp(void *va, void *vb); extern struct ospf6_area *ospf6_area_create(uint32_t, struct ospf6 *, int); @@ -163,6 +166,5 @@ extern void ospf6_area_config_write(struct vty *vty, struct ospf6 *ospf6); extern void ospf6_area_init(void); struct ospf6_interface; extern void ospf6_area_interface_delete(struct ospf6_interface *oi); -int str2area_id(const char *str, struct in_addr *area_id, int *area_id_fmt); #endif /* OSPF_AREA_H */ diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c index e22e6560d0..c17af758b0 100644 --- a/ospf6d/ospf6_asbr.c +++ b/ospf6d/ospf6_asbr.c @@ -687,7 +687,7 @@ void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa, zlog_debug("%s: Withdraw AS-External route for %s", __func__, lsa->name); - if (ospf6_is_router_abr(ospf6)) + if (ospf6_check_and_set_router_abr(ospf6)) oa = ospf6->backbone; else oa = listgetdata(listhead(ospf6->area_list)); @@ -1174,6 +1174,8 @@ static struct ospf6_redist *ospf6_redist_add(struct ospf6 *ospf6, int type, red = XCALLOC(MTYPE_OSPF6_REDISTRIBUTE, sizeof(struct ospf6_redist)); red->instance = instance; + red->dmetric.type = -1; + red->dmetric.value = -1; ROUTEMAP_NAME(red) = NULL; ROUTEMAP(red) = NULL; @@ -1376,8 +1378,13 @@ void ospf6_asbr_redistribute_add(int type, ifindex_t ifindex, if (troute.path.metric_type) match->path.metric_type = troute.path.metric_type; + else + match->path.metric_type = + metric_type(ospf6, type, 0); if (troute.path.cost) match->path.cost = troute.path.cost; + else + match->path.cost = metric_value(ospf6, type, 0); if (!IN6_IS_ADDR_UNSPECIFIED(&tinfo.forwarding)) memcpy(&info->forwarding, &tinfo.forwarding, sizeof(struct in6_addr)); @@ -1438,8 +1445,12 @@ void ospf6_asbr_redistribute_add(int type, ifindex_t ifindex, if (ROUTEMAP(red)) { if (troute.path.metric_type) route->path.metric_type = troute.path.metric_type; + else + route->path.metric_type = metric_type(ospf6, type, 0); if (troute.path.cost) route->path.cost = troute.path.cost; + else + route->path.cost = metric_value(ospf6, type, 0); if (!IN6_IS_ADDR_UNSPECIFIED(&tinfo.forwarding)) memcpy(&info->forwarding, &tinfo.forwarding, sizeof(struct in6_addr)); diff --git a/ospf6d/ospf6_flood.c b/ospf6d/ospf6_flood.c index ac16e53d63..05b43189e9 100644 --- a/ospf6d/ospf6_flood.c +++ b/ospf6d/ospf6_flood.c @@ -811,6 +811,18 @@ void ospf6_receive_lsa(struct ospf6_neighbor *from, ismore_recent = 1; assert(from); + /* if we receive a LSA with invalid seqnum drop it */ + if (ntohl(lsa_header->seqnum) - 1 == OSPF_MAX_SEQUENCE_NUMBER) { + if (IS_OSPF6_DEBUG_EXAMIN_TYPE(lsa_header->type)) { + zlog_debug( + "received lsa [%s Id:%pI4 Adv:%pI4] with invalid seqnum 0x%x, ignore", + ospf6_lstype_name(lsa_header->type), + &lsa_header->id, &lsa_header->adv_router, + ntohl(lsa_header->seqnum)); + } + return; + } + /* make lsa structure for received lsa */ new = ospf6_lsa_create(lsa_header); @@ -974,11 +986,12 @@ void ospf6_receive_lsa(struct ospf6_neighbor *from, /* if no database copy, should go above state (5) */ assert(old); - if (is_debug) { - zlog_debug( - "Received is not newer, on the neighbor's request-list"); - zlog_debug("BadLSReq, discard the received LSA"); - } + zlog_warn( + "Received is not newer, on the neighbor %s request-list", + from->name); + zlog_warn( + "BadLSReq, discard the received LSA lsa %s send badLSReq", + new->name); /* BadLSReq */ thread_add_event(master, bad_lsreq, from, 0, NULL); diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c index b71d884fdc..6763e7457f 100644 --- a/ospf6d/ospf6_interface.c +++ b/ospf6d/ospf6_interface.c @@ -36,6 +36,7 @@ #include "ospf6_message.h" #include "ospf6_route.h" #include "ospf6_area.h" +#include "ospf6_abr.h" #include "ospf6_interface.h" #include "ospf6_neighbor.h" #include "ospf6_intra.h" @@ -330,31 +331,6 @@ ospf6_interface_get_linklocal_address(struct interface *ifp) return l; } -void ospf6_interface_if_add(struct interface *ifp) -{ - struct ospf6_interface *oi; - unsigned int iobuflen; - - oi = (struct ospf6_interface *)ifp->info; - if (oi == NULL) - return; - - /* Try to adjust I/O buffer size with IfMtu */ - if (oi->ifmtu == 0) - oi->ifmtu = ifp->mtu6; - iobuflen = ospf6_iobuf_size(ifp->mtu6); - if (oi->ifmtu > iobuflen) { - if (IS_OSPF6_DEBUG_INTERFACE) - zlog_debug( - "Interface %s: IfMtu is adjusted to I/O buffer size: %d.", - ifp->name, iobuflen); - oi->ifmtu = iobuflen; - } - - /* interface start */ - ospf6_interface_state_update(oi->interface); -} - void ospf6_interface_state_update(struct interface *ifp) { struct ospf6_interface *oi; @@ -725,20 +701,17 @@ int interface_up(struct thread *thread) /* check physical interface is up */ if (!if_is_operative(oi->interface)) { - if (IS_OSPF6_DEBUG_INTERFACE) - zlog_debug( - "Interface %s is down, can't execute [InterfaceUp]", - oi->interface->name); + zlog_warn("Interface %s is down, can't execute [InterfaceUp]", + oi->interface->name); return 0; } /* check interface has a link-local address */ if (!(ospf6_interface_get_linklocal_address(oi->interface) || if_is_loopback_or_vrf(oi->interface))) { - if (IS_OSPF6_DEBUG_INTERFACE) - zlog_debug( - "Interface %s has no link local address, can't execute [InterfaceUp]", - oi->interface->name); + zlog_warn( + "Interface %s has no link local address, can't execute [InterfaceUp]", + oi->interface->name); return 0; } @@ -755,7 +728,7 @@ int interface_up(struct thread *thread) /* If no area assigned, return */ if (oi->area == NULL) { - zlog_debug( + zlog_warn( "%s: Not scheduleing Hello for %s as there is no area assigned yet", __func__, oi->interface->name); return 0; @@ -1638,7 +1611,143 @@ DEFUN(show_ipv6_ospf6_interface_prefix, show_ipv6_ospf6_interface_prefix_cmd, return CMD_SUCCESS; } +void ospf6_interface_start(struct ospf6_interface *oi) +{ + struct ospf6 *ospf6; + struct ospf6_area *oa; + + if (oi->area_id_format == OSPF6_AREA_FMT_UNSET) + return; + + ospf6 = ospf6_lookup_by_vrf_id(oi->interface->vrf_id); + if (!ospf6) + return; + + oa = ospf6_area_lookup(oi->area_id, ospf6); + if (oa == NULL) + oa = ospf6_area_create(oi->area_id, ospf6, oi->area_id_format); + + /* attach interface to area */ + listnode_add(oa->if_list, oi); + oi->area = oa; + + SET_FLAG(oa->flag, OSPF6_AREA_ENABLE); + + /* start up */ + ospf6_interface_enable(oi); + + /* If the router is ABR, originate summary routes */ + if (ospf6_check_and_set_router_abr(ospf6)) + ospf6_abr_enable_area(oa); +} + +void ospf6_interface_stop(struct ospf6_interface *oi) +{ + struct ospf6_area *oa; + + oa = oi->area; + if (!oa) + return; + + ospf6_interface_disable(oi); + + listnode_delete(oa->if_list, oi); + oi->area = NULL; + + if (oa->if_list->count == 0) { + UNSET_FLAG(oa->flag, OSPF6_AREA_ENABLE); + ospf6_abr_disable_area(oa); + } +} + /* interface variable set command */ +DEFUN (ipv6_ospf6_area, + ipv6_ospf6_area_cmd, + "ipv6 ospf6 area <A.B.C.D|(0-4294967295)>", + IP6_STR + OSPF6_STR + "Specify the OSPF6 area ID\n" + "OSPF6 area ID in IPv4 address notation\n" + "OSPF6 area ID in decimal notation\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf6_interface *oi; + int idx_ipv4 = 3; + uint32_t area_id; + int format; + int ipv6_count = 0; + + assert(ifp); + + oi = (struct ospf6_interface *)ifp->info; + if (oi == NULL) + oi = ospf6_interface_create(ifp); + assert(oi); + + if (oi->area) { + vty_out(vty, "%s already attached to Area %s\n", + oi->interface->name, oi->area->name); + return CMD_SUCCESS; + } + + /* if more than OSPF6_MAX_IF_ADDRS are configured on this interface + * then don't allow ospfv3 to be configured + */ + ipv6_count = connected_count_by_family(ifp, AF_INET6); + if (oi->ifmtu == OSPF6_DEFAULT_MTU && ipv6_count > OSPF6_MAX_IF_ADDRS) { + vty_out(vty, + "can not configure OSPFv3 on if %s, must have less than %d interface addresses but has %d addresses\n", + ifp->name, OSPF6_MAX_IF_ADDRS, ipv6_count); + return CMD_WARNING_CONFIG_FAILED; + } else if (oi->ifmtu >= OSPF6_JUMBO_MTU + && ipv6_count > OSPF6_MAX_IF_ADDRS_JUMBO) { + vty_out(vty, + "can not configure OSPFv3 on if %s, must have less than %d interface addresses but has %d addresses\n", + ifp->name, OSPF6_MAX_IF_ADDRS_JUMBO, ipv6_count); + return CMD_WARNING_CONFIG_FAILED; + } + + if (str2area_id(argv[idx_ipv4]->arg, &area_id, &format)) { + vty_out(vty, "Malformed Area-ID: %s\n", argv[idx_ipv4]->arg); + return CMD_WARNING_CONFIG_FAILED; + } + + oi->area_id = area_id; + oi->area_id_format = format; + + ospf6_interface_start(oi); + + return CMD_SUCCESS; +} + +DEFUN (no_ipv6_ospf6_area, + no_ipv6_ospf6_area_cmd, + "no ipv6 ospf6 area [<A.B.C.D|(0-4294967295)>]", + NO_STR + IP6_STR + OSPF6_STR + "Specify the OSPF6 area ID\n" + "OSPF6 area ID in IPv4 address notation\n" + "OSPF6 area ID in decimal notation\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf6_interface *oi; + + assert(ifp); + + oi = (struct ospf6_interface *)ifp->info; + if (oi == NULL) + oi = ospf6_interface_create(ifp); + assert(oi); + + ospf6_interface_stop(oi); + + oi->area_id = 0; + oi->area_id_format = OSPF6_AREA_FMT_UNSET; + + return CMD_SUCCESS; +} + DEFUN (ipv6_ospf6_ifmtu, ipv6_ospf6_ifmtu_cmd, "ipv6 ospf6 ifmtu (1-65535)", @@ -2334,6 +2443,7 @@ static int config_write_ospf6_interface(struct vty *vty, struct vrf *vrf) { struct ospf6_interface *oi; struct interface *ifp; + char buf[INET_ADDRSTRLEN]; FOR_ALL_INTERFACES (vrf, ifp) { oi = (struct ospf6_interface *)ifp->info; @@ -2348,6 +2458,11 @@ static int config_write_ospf6_interface(struct vty *vty, struct vrf *vrf) if (ifp->desc) vty_out(vty, " description %s\n", ifp->desc); + if (oi->area_id_format != OSPF6_AREA_FMT_UNSET) { + area_id2str(buf, sizeof(buf), oi->area_id, + oi->area_id_format); + vty_out(vty, " ipv6 ospf6 area %s\n", buf); + } if (oi->c_ifmtu) vty_out(vty, " ipv6 ospf6 ifmtu %d\n", oi->c_ifmtu); @@ -2427,7 +2542,9 @@ static int ospf6_ifp_create(struct interface *ifp) if (IS_OSPF6_DEBUG_ZEBRA(RECV)) zlog_debug("Zebra Interface add: %s index %d mtu %d", ifp->name, ifp->ifindex, ifp->mtu6); - ospf6_interface_if_add(ifp); + + if (ifp->info) + ospf6_interface_start(ifp->info); return 0; } @@ -2468,6 +2585,9 @@ static int ospf6_ifp_destroy(struct interface *ifp) zlog_debug("Zebra Interface delete: %s index %d mtu %d", ifp->name, ifp->ifindex, ifp->mtu6); + if (ifp->info) + ospf6_interface_stop(ifp->info); + return 0; } @@ -2485,6 +2605,8 @@ void ospf6_interface_init(void) &show_ipv6_ospf6_interface_ifname_prefix_cmd); install_element(VIEW_NODE, &show_ipv6_ospf6_interface_traffic_cmd); + install_element(INTERFACE_NODE, &ipv6_ospf6_area_cmd); + install_element(INTERFACE_NODE, &no_ipv6_ospf6_area_cmd); install_element(INTERFACE_NODE, &ipv6_ospf6_cost_cmd); install_element(INTERFACE_NODE, &no_ipv6_ospf6_cost_cmd); install_element(INTERFACE_NODE, &ipv6_ospf6_ifmtu_cmd); diff --git a/ospf6d/ospf6_interface.h b/ospf6d/ospf6_interface.h index fb1b947cf8..796d75e897 100644 --- a/ospf6d/ospf6_interface.h +++ b/ospf6d/ospf6_interface.h @@ -39,6 +39,9 @@ struct ospf6_interface { /* back pointer */ struct ospf6_area *area; + uint32_t area_id; + int area_id_format; + /* list of ospf6 neighbor */ struct list *neighbor_list; @@ -177,6 +180,9 @@ extern const char *const ospf6_interface_state_str[]; /* Function Prototypes */ +extern void ospf6_interface_start(struct ospf6_interface *oi); +extern void ospf6_interface_stop(struct ospf6_interface *oi); + extern struct ospf6_interface * ospf6_interface_lookup_by_ifindex(ifindex_t, vrf_id_t vrf_id); extern struct ospf6_interface *ospf6_interface_create(struct interface *); @@ -185,7 +191,6 @@ extern void ospf6_interface_delete(struct ospf6_interface *); extern void ospf6_interface_enable(struct ospf6_interface *); extern void ospf6_interface_disable(struct ospf6_interface *); -extern void ospf6_interface_if_add(struct interface *); extern void ospf6_interface_state_update(struct interface *); extern void ospf6_interface_connected_route_update(struct interface *); extern void ospf6_interface_connected_route_add(struct connected *); diff --git a/ospf6d/ospf6_intra.c b/ospf6d/ospf6_intra.c index 524edd3fae..c971c6180e 100644 --- a/ospf6d/ospf6_intra.c +++ b/ospf6d/ospf6_intra.c @@ -182,7 +182,7 @@ static void ospf6_router_lsa_options_set(struct ospf6_area *oa, OSPF6_OPT_CLEAR_ALL(router_lsa->options); memcpy(router_lsa->options, oa->options, 3); - if (ospf6_is_router_abr(oa->ospf6)) + if (ospf6_check_and_set_router_abr(oa->ospf6)) SET_FLAG(router_lsa->bits, OSPF6_ROUTER_BIT_B); else UNSET_FLAG(router_lsa->bits, OSPF6_ROUTER_BIT_B); @@ -289,9 +289,8 @@ int ospf6_router_lsa_originate(struct thread *thread) if ((caddr_t)lsdesc == (caddr_t)router_lsa + sizeof(struct ospf6_router_lsa)) { - if (IS_OSPF6_DEBUG_ORIGINATE(ROUTER)) - zlog_debug( - "Size limit setting for Router-LSA too short"); + zlog_warn( + "Size limit setting for Router-LSA too short"); return 0; } diff --git a/ospf6d/ospf6_lsa.c b/ospf6d/ospf6_lsa.c index a8f523295b..d627194252 100644 --- a/ospf6d/ospf6_lsa.c +++ b/ospf6d/ospf6_lsa.c @@ -654,6 +654,7 @@ void ospf6_lsa_show(struct vty *vty, struct ospf6_lsa *lsa, ospf6_lsa_age_current(lsa)); json_object_string_add(json_obj, "type", ospf6_lstype_name(lsa->header->type)); + json_object_string_add(json_obj, "linkStateId", id); json_object_string_add(json_obj, "advertisingRouter", adv_router); json_object_int_add(json_obj, "lsSequenceNumber", diff --git a/ospf6d/ospf6_message.c b/ospf6d/ospf6_message.c index 9a1d8b79bc..1cf4585c95 100644 --- a/ospf6d/ospf6_message.c +++ b/ospf6d/ospf6_message.c @@ -442,34 +442,34 @@ static void ospf6_dbdesc_recv_master(struct ospf6_header *oh, } if (CHECK_FLAG(dbdesc->bits, OSPF6_DBDESC_MSBIT)) { - if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR)) - zlog_debug("Master/Slave bit mismatch"); + zlog_warn( + "DbDesc recv: Master/Slave bit mismatch Nbr %s", + on->name); thread_add_event(master, seqnumber_mismatch, on, 0, NULL); return; } if (CHECK_FLAG(dbdesc->bits, OSPF6_DBDESC_IBIT)) { - if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR)) - zlog_debug("Initialize bit mismatch"); + zlog_warn("DbDesc recv: Initialize bit mismatch Nbr %s", + on->name); thread_add_event(master, seqnumber_mismatch, on, 0, NULL); return; } if (memcmp(on->options, dbdesc->options, sizeof(on->options))) { - if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR)) - zlog_debug("Option field mismatch"); + zlog_warn("DbDesc recv: Option field mismatch Nbr %s", + on->name); thread_add_event(master, seqnumber_mismatch, on, 0, NULL); return; } if (ntohl(dbdesc->seqnum) != on->dbdesc_seqnum) { - if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR)) - zlog_debug( - "Sequence number mismatch (%#lx expected)", - (unsigned long)on->dbdesc_seqnum); + zlog_warn( + "DbDesc recv: Sequence number mismatch Nbr %s (%#lx expected)", + on->name, (unsigned long)on->dbdesc_seqnum); thread_add_event(master, seqnumber_mismatch, on, 0, NULL); return; @@ -488,9 +488,9 @@ static void ospf6_dbdesc_recv_master(struct ospf6_header *oh, return; } - if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR)) - zlog_debug("Not duplicate dbdesc in state %s", - ospf6_neighbor_state_str[on->state]); + zlog_warn( + "DbDesc recv: Not duplicate dbdesc in state %s Nbr %s", + ospf6_neighbor_state_str[on->state], on->name); thread_add_event(master, seqnumber_mismatch, on, 0, NULL); return; @@ -663,34 +663,36 @@ static void ospf6_dbdesc_recv_slave(struct ospf6_header *oh, } if (!CHECK_FLAG(dbdesc->bits, OSPF6_DBDESC_MSBIT)) { - if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR)) - zlog_debug("Master/Slave bit mismatch"); + zlog_warn( + "DbDesc slave recv: Master/Slave bit mismatch Nbr %s", + on->name); thread_add_event(master, seqnumber_mismatch, on, 0, NULL); return; } if (CHECK_FLAG(dbdesc->bits, OSPF6_DBDESC_IBIT)) { - if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR)) - zlog_debug("Initialize bit mismatch"); + zlog_warn( + "DbDesc slave recv: Initialize bit mismatch Nbr %s", + on->name); thread_add_event(master, seqnumber_mismatch, on, 0, NULL); return; } if (memcmp(on->options, dbdesc->options, sizeof(on->options))) { - if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR)) - zlog_debug("Option field mismatch"); + zlog_warn( + "DbDesc slave recv: Option field mismatch Nbr %s", + on->name); thread_add_event(master, seqnumber_mismatch, on, 0, NULL); return; } if (ntohl(dbdesc->seqnum) != on->dbdesc_seqnum + 1) { - if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR)) - zlog_debug( - "Sequence number mismatch (%#lx expected)", - (unsigned long)on->dbdesc_seqnum + 1); + zlog_warn( + "DbDesc slave recv: Sequence number mismatch Nbr %s (%#lx expected)", + on->name, (unsigned long)on->dbdesc_seqnum + 1); thread_add_event(master, seqnumber_mismatch, on, 0, NULL); return; @@ -712,9 +714,9 @@ static void ospf6_dbdesc_recv_slave(struct ospf6_header *oh, return; } - if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR)) - zlog_debug("Not duplicate dbdesc in state %s", - ospf6_neighbor_state_str[on->state]); + zlog_warn( + "DbDesc slave recv: Not duplicate dbdesc in state %s Nbr %s", + ospf6_neighbor_state_str[on->state], on->name); thread_add_event(master, seqnumber_mismatch, on, 0, NULL); return; @@ -888,12 +890,10 @@ static void ospf6_lsreq_recv(struct in6_addr *src, struct in6_addr *dst, /* Find database copy */ lsa = ospf6_lsdb_lookup(e->type, e->id, e->adv_router, lsdb); if (lsa == NULL) { - if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV)) { - zlog_debug( - "Can't find requested [%s Id:%pI4 Adv:%pI4]", - ospf6_lstype_name(e->type), &e->id, - &e->adv_router); - } + zlog_warn( + "Can't find requested lsa [%s Id:%pI4 Adv:%pI4] send badLSReq", + ospf6_lstype_name(e->type), &e->id, + &e->adv_router); thread_add_event(master, bad_lsreq, on, 0, NULL); return; } @@ -923,18 +923,14 @@ static unsigned ospf6_prefixes_examin( while (length) { if (length < OSPF6_PREFIX_MIN_SIZE) { - if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN, - RECV_HDR)) - zlog_debug("%s: undersized IPv6 prefix header", - __func__); + zlog_warn("%s: undersized IPv6 prefix header", + __func__); return MSG_NG; } /* safe to look deeper */ if (current->prefix_length > IPV6_MAX_BITLEN) { - if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN, - RECV_HDR)) - zlog_debug("%s: invalid PrefixLength (%u bits)", - __func__, current->prefix_length); + zlog_warn("%s: invalid PrefixLength (%u bits)", + __func__, current->prefix_length); return MSG_NG; } /* covers both fixed- and variable-sized fields */ @@ -942,10 +938,7 @@ static unsigned ospf6_prefixes_examin( OSPF6_PREFIX_MIN_SIZE + OSPF6_PREFIX_SPACE(current->prefix_length); if (requested_pfx_bytes > length) { - if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN, - RECV_HDR)) - zlog_debug("%s: undersized IPv6 prefix", - __func__); + zlog_warn("%s: undersized IPv6 prefix", __func__); return MSG_NG; } /* next prefix */ @@ -955,11 +948,9 @@ static unsigned ospf6_prefixes_examin( real_num_pfxs++; } if (real_num_pfxs != req_num_pfxs) { - if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN, - RECV_HDR)) - zlog_debug( - "%s: IPv6 prefix number mismatch (%u required, %u real)", - __func__, req_num_pfxs, real_num_pfxs); + zlog_warn( + "%s: IPv6 prefix number mismatch (%u required, %u real)", + __func__, req_num_pfxs, real_num_pfxs); return MSG_NG; } return MSG_OK; @@ -988,10 +979,7 @@ static unsigned ospf6_lsa_examin(struct ospf6_lsa_header *lsah, ltindex = lsatype & OSPF6_LSTYPE_FCODE_MASK; if (ltindex < OSPF6_LSTYPE_SIZE && ospf6_lsa_minlen[ltindex] && lsalen < ospf6_lsa_minlen[ltindex] + OSPF6_LSA_HEADER_SIZE) { - if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN, - RECV_HDR)) - zlog_debug("%s: undersized (%u B) LSA", __func__, - lsalen); + zlog_warn("%s: undersized (%u B) LSA", __func__, lsalen); return MSG_NG; } switch (lsatype) { @@ -1001,11 +989,9 @@ static unsigned ospf6_lsa_examin(struct ospf6_lsa_header *lsah, by N>=0 interface descriptions. */ if ((lsalen - OSPF6_LSA_HEADER_SIZE - OSPF6_ROUTER_LSA_MIN_SIZE) % OSPF6_ROUTER_LSDESC_FIX_SIZE) { - if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN, - RECV_HDR)) - zlog_debug( - "%s: interface description alignment error", - __func__); + zlog_warn( + "%s: Router LSA interface description alignment error", + __func__); return MSG_NG; } break; @@ -1015,11 +1001,9 @@ static unsigned ospf6_lsa_examin(struct ospf6_lsa_header *lsah, if ((lsalen - OSPF6_LSA_HEADER_SIZE - OSPF6_NETWORK_LSA_MIN_SIZE) % OSPF6_NETWORK_LSDESC_FIX_SIZE) { - if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN, - RECV_HDR)) - zlog_debug( - "%s: router description alignment error", - __func__); + zlog_warn( + "%s: Network LSA router description alignment error", + __func__); return MSG_NG; } break; @@ -1040,10 +1024,8 @@ static unsigned ospf6_lsa_examin(struct ospf6_lsa_header *lsah, /* RFC5340 A.4.6, fixed-size LSA. */ if (lsalen > OSPF6_LSA_HEADER_SIZE + OSPF6_INTER_ROUTER_LSA_FIX_SIZE) { - if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN, - RECV_HDR)) - zlog_debug("%s: oversized (%u B) LSA", __func__, - lsalen); + zlog_warn("%s: Inter Router LSA oversized (%u B) LSA", + __func__, lsalen); return MSG_NG; } break; @@ -1069,10 +1051,9 @@ static unsigned ospf6_lsa_examin(struct ospf6_lsa_header *lsah, IPv6 prefix before ospf6_prefix_examin() confirms its sizing. */ if (exp_length + OSPF6_PREFIX_MIN_SIZE > lsalen) { - if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN, - RECV_HDR)) - zlog_debug("%s: undersized (%u B) LSA header", - __func__, lsalen); + zlog_warn( + "%s: AS External undersized (%u B) LSA header", + __func__, lsalen); return MSG_NG; } /* forwarding address */ @@ -1088,10 +1069,9 @@ static unsigned ospf6_lsa_examin(struct ospf6_lsa_header *lsah, I.e., this check does not include any IPv6 prefix fields. */ if (exp_length > lsalen) { - if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN, - RECV_HDR)) - zlog_debug("%s: undersized (%u B) LSA header", - __func__, lsalen); + zlog_warn( + "%s: AS External undersized (%u B) LSA header", + __func__, lsalen); return MSG_NG; } /* The last call completely covers the remainder (IPv6 prefix). @@ -1157,34 +1137,26 @@ ospf6_lsaseq_examin(struct ospf6_lsa_header *lsah, /* start of buffered data */ while (length) { uint16_t lsalen; if (length < OSPF6_LSA_HEADER_SIZE) { - if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN, - RECV_HDR)) - zlog_debug( - "%s: undersized (%zu B) trailing (#%u) LSA header", - __func__, length, counted_lsas); + zlog_warn( + "%s: undersized (%zu B) trailing (#%u) LSA header", + __func__, length, counted_lsas); return MSG_NG; } /* save on ntohs() calls here and in the LSA validator */ lsalen = OSPF6_LSA_SIZE(lsah); if (lsalen < OSPF6_LSA_HEADER_SIZE) { - if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN, - RECV_HDR)) - zlog_debug( - "%s: malformed LSA header #%u, declared length is %u B", - __func__, counted_lsas, lsalen); + zlog_warn( + "%s: malformed LSA header #%u, declared length is %u B", + __func__, counted_lsas, lsalen); return MSG_NG; } if (headeronly) { /* less checks here and in ospf6_lsa_examin() */ if (MSG_OK != ospf6_lsa_examin(lsah, lsalen, 1)) { - if (IS_OSPF6_DEBUG_MESSAGE( - OSPF6_MESSAGE_TYPE_UNKNOWN, - RECV_HDR)) - zlog_debug( - "%s: anomaly in header-only %s LSA #%u", - __func__, - ospf6_lstype_name(lsah->type), - counted_lsas); + zlog_warn( + "%s: anomaly in header-only %s LSA #%u", + __func__, ospf6_lstype_name(lsah->type), + counted_lsas); return MSG_NG; } lsah = (struct ospf6_lsa_header @@ -1195,25 +1167,16 @@ ospf6_lsaseq_examin(struct ospf6_lsa_header *lsah, /* start of buffered data */ /* make sure the input buffer is deep enough before * further checks */ if (lsalen > length) { - if (IS_OSPF6_DEBUG_MESSAGE( - OSPF6_MESSAGE_TYPE_UNKNOWN, - RECV_HDR)) - zlog_debug( - "%s: anomaly in %s LSA #%u: declared length is %u B, buffered length is %zu B", - __func__, - ospf6_lstype_name(lsah->type), - counted_lsas, lsalen, length); + zlog_warn( + "%s: anomaly in %s LSA #%u: declared length is %u B, buffered length is %zu B", + __func__, ospf6_lstype_name(lsah->type), + counted_lsas, lsalen, length); return MSG_NG; } if (MSG_OK != ospf6_lsa_examin(lsah, lsalen, 0)) { - if (IS_OSPF6_DEBUG_MESSAGE( - OSPF6_MESSAGE_TYPE_UNKNOWN, - RECV_HDR)) - zlog_debug( - "%s: anomaly in %s LSA #%u", - __func__, - ospf6_lstype_name(lsah->type), - counted_lsas); + zlog_warn("%s: anomaly in %s LSA #%u", __func__, + ospf6_lstype_name(lsah->type), + counted_lsas); return MSG_NG; } lsah = (struct ospf6_lsa_header *)((caddr_t)lsah @@ -1224,11 +1187,8 @@ ospf6_lsaseq_examin(struct ospf6_lsa_header *lsah, /* start of buffered data */ } if (declared_num_lsas && counted_lsas != declared_num_lsas) { - if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN, - RECV_HDR)) - zlog_debug( - "%s: #LSAs declared (%u) does not match actual (%u)", - __func__, declared_num_lsas, counted_lsas); + zlog_warn("%s: #LSAs declared (%u) does not match actual (%u)", + __func__, declared_num_lsas, counted_lsas); return MSG_NG; } return MSG_OK; @@ -1243,41 +1203,31 @@ static unsigned ospf6_packet_examin(struct ospf6_header *oh, /* length, 1st approximation */ if (bytesonwire < OSPF6_HEADER_SIZE) { - if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN, - RECV_HDR)) - zlog_debug("%s: undersized (%u B) packet", __func__, - bytesonwire); + zlog_warn("%s: undersized (%u B) packet", __func__, + bytesonwire); return MSG_NG; } /* Now it is safe to access header fields. */ if (bytesonwire != ntohs(oh->length)) { - if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN, - RECV_HDR)) - zlog_debug( - "%s: %s packet length error (%u real, %u declared)", - __func__, lookup_msg(ospf6_message_type_str, - oh->type, NULL), - bytesonwire, ntohs(oh->length)); + zlog_warn("%s: %s packet length error (%u real, %u declared)", + __func__, + lookup_msg(ospf6_message_type_str, oh->type, NULL), + bytesonwire, ntohs(oh->length)); return MSG_NG; } /* version check */ if (oh->version != OSPFV3_VERSION) { - if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN, - RECV_HDR)) - zlog_debug("%s: invalid (%u) protocol version", - __func__, oh->version); + zlog_warn("%s: invalid (%u) protocol version", __func__, + oh->version); return MSG_NG; } /* length, 2nd approximation */ if (oh->type < OSPF6_MESSAGE_TYPE_ALL && ospf6_packet_minlen[oh->type] && bytesonwire < OSPF6_HEADER_SIZE + ospf6_packet_minlen[oh->type]) { - if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN, - RECV_HDR)) - zlog_debug("%s: undersized (%u B) %s packet", __func__, - bytesonwire, - lookup_msg(ospf6_message_type_str, oh->type, - NULL)); + zlog_warn("%s: undersized (%u B) %s packet", __func__, + bytesonwire, + lookup_msg(ospf6_message_type_str, oh->type, NULL)); return MSG_NG; } /* type-specific deeper validation */ @@ -1290,11 +1240,8 @@ static unsigned ospf6_packet_examin(struct ospf6_header *oh, == (bytesonwire - OSPF6_HEADER_SIZE - OSPF6_HELLO_MIN_SIZE) % 4) return MSG_OK; - if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN, - RECV_HDR)) - zlog_debug("%s: alignment error in %s packet", __func__, - lookup_msg(ospf6_message_type_str, oh->type, - NULL)); + zlog_warn("%s: alignment error in %s packet", __func__, + lookup_msg(ospf6_message_type_str, oh->type, NULL)); return MSG_NG; case OSPF6_MESSAGE_TYPE_DBDESC: /* RFC5340 A.3.3, packet header + OSPF6_DB_DESC_MIN_SIZE bytes @@ -1314,11 +1261,8 @@ static unsigned ospf6_packet_examin(struct ospf6_header *oh, == (bytesonwire - OSPF6_HEADER_SIZE - OSPF6_LS_REQ_MIN_SIZE) % OSPF6_LSREQ_LSDESC_FIX_SIZE) return MSG_OK; - if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN, - RECV_HDR)) - zlog_debug("%s: alignment error in %s packet", __func__, - lookup_msg(ospf6_message_type_str, oh->type, - NULL)); + zlog_warn("%s: alignment error in %s packet", __func__, + lookup_msg(ospf6_message_type_str, oh->type, NULL)); return MSG_NG; case OSPF6_MESSAGE_TYPE_LSUPDATE: /* RFC5340 A.3.5, packet header + OSPF6_LS_UPD_MIN_SIZE bytes @@ -1343,16 +1287,12 @@ static unsigned ospf6_packet_examin(struct ospf6_header *oh, 1, 0); break; default: - if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN, - RECV_HDR)) - zlog_debug("%s: invalid (%u) message type", __func__, - oh->type); + zlog_warn("%s: invalid (%u) message type", __func__, oh->type); return MSG_NG; } - if (test != MSG_OK - && IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN, RECV_HDR)) - zlog_debug("%s: anomaly in %s packet", __func__, - lookup_msg(ospf6_message_type_str, oh->type, NULL)); + if (test != MSG_OK) + zlog_warn("%s: anomaly in %s packet", __func__, + lookup_msg(ospf6_message_type_str, oh->type, NULL)); return test; } diff --git a/ospf6d/ospf6_neighbor.c b/ospf6d/ospf6_neighbor.c index 9323da8be3..a08ca904ea 100644 --- a/ospf6d/ospf6_neighbor.c +++ b/ospf6d/ospf6_neighbor.c @@ -145,6 +145,8 @@ void ospf6_neighbor_delete(struct ospf6_neighbor *on) THREAD_OFF(on->inactivity_timer); + THREAD_OFF(on->last_dbdesc_release_timer); + THREAD_OFF(on->thread_send_dbdesc); THREAD_OFF(on->thread_send_lsreq); THREAD_OFF(on->thread_send_lsupdate); @@ -350,6 +352,16 @@ int negotiation_done(struct thread *thread) return 0; } +static int ospf6_neighbor_last_dbdesc_release(struct thread *thread) +{ + struct ospf6_neighbor *on = THREAD_ARG(thread); + + assert(on); + memset(&on->dbdesc_last, 0, sizeof(struct ospf6_dbdesc)); + + return 0; +} + int exchange_done(struct thread *thread) { struct ospf6_neighbor *on; @@ -366,10 +378,13 @@ int exchange_done(struct thread *thread) THREAD_OFF(on->thread_send_dbdesc); ospf6_lsdb_remove_all(on->dbdesc_list); - /* XXX - thread_add_timer (master, ospf6_neighbor_last_dbdesc_release, on, - on->ospf6_if->dead_interval); - */ + /* RFC 2328 (10.8): Release the last dbdesc after dead_interval */ + if (!CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MSBIT)) { + THREAD_OFF(on->last_dbdesc_release_timer); + thread_add_timer(master, ospf6_neighbor_last_dbdesc_release, on, + on->ospf6_if->dead_interval, + &on->last_dbdesc_release_timer); + } if (on->request_list->count == 0) ospf6_neighbor_state_change(OSPF6_NEIGHBOR_FULL, on, diff --git a/ospf6d/ospf6_neighbor.h b/ospf6d/ospf6_neighbor.h index 47f8c834e2..85b8e4b8ae 100644 --- a/ospf6d/ospf6_neighbor.h +++ b/ospf6d/ospf6_neighbor.h @@ -89,6 +89,9 @@ struct ospf6_neighbor { /* Inactivity timer */ struct thread *inactivity_timer; + /* Timer to release the last dbdesc packet */ + struct thread *last_dbdesc_release_timer; + /* Thread for sending message */ struct thread *thread_send_dbdesc; struct thread *thread_send_lsreq; diff --git a/ospf6d/ospf6_nssa.c b/ospf6d/ospf6_nssa.c index 4d9b0a1978..dfae51cec1 100644 --- a/ospf6d/ospf6_nssa.c +++ b/ospf6d/ospf6_nssa.c @@ -1093,7 +1093,7 @@ static int ospf6_abr_task_timer(struct thread *thread) if (IS_OSPF6_DEBUG_ABR) zlog_debug("Running ABR task on timer"); - ospf6_is_router_abr(ospf6); + (void)ospf6_check_and_set_router_abr(ospf6); ospf6_abr_nssa_check_status(ospf6); ospf6_abr_task(ospf6); /* if nssa-abr, then scan Type-7 LSDB */ @@ -1137,7 +1137,7 @@ static void ospf6_nssa_flush_area(struct ospf6_area *area) SET_FLAG(lsa->flag, OSPF6_LSA_FLUSH); ospf6_flood(NULL, lsa); /* Flush the translated LSA */ - if (ospf6_is_router_abr(ospf6)) { + if (ospf6_check_and_set_router_abr(ospf6)) { type = htons(OSPF6_LSTYPE_AS_EXTERNAL); type5 = ospf6_lsdb_lookup( htons(type), lsa->external_lsa_id, @@ -1158,7 +1158,7 @@ static void ospf6_area_nssa_update(struct ospf6_area *area) struct ospf6_route *route; if (IS_AREA_NSSA(area)) { - if (!ospf6_is_router_abr(area->ospf6)) + if (!ospf6_check_and_set_router_abr(area->ospf6)) OSPF6_OPT_CLEAR(area->options, OSPF6_OPT_E); area->ospf6->anyNSSA++; OSPF6_OPT_SET(area->options, OSPF6_OPT_N); @@ -1167,7 +1167,7 @@ static void ospf6_area_nssa_update(struct ospf6_area *area) if (IS_OSPF6_DEBUG_ORIGINATE(ROUTER)) zlog_debug("Normal area for if %s", area->name); OSPF6_OPT_CLEAR(area->options, OSPF6_OPT_N); - if (ospf6_is_router_abr(area->ospf6)) + if (ospf6_check_and_set_router_abr(area->ospf6)) OSPF6_OPT_SET(area->options, OSPF6_OPT_E); area->ospf6->anyNSSA--; area->NSSATranslatorState = OSPF6_NSSA_TRANSLATE_DISABLED; @@ -1178,7 +1178,7 @@ static void ospf6_area_nssa_update(struct ospf6_area *area) OSPF6_ROUTER_LSA_SCHEDULE(area); /* Check if router is ABR */ - if (ospf6_is_router_abr(area->ospf6)) { + if (ospf6_check_and_set_router_abr(area->ospf6)) { if (IS_OSPF6_DEBUG_NSSA) zlog_debug("Router is ABR area %s", area->name); ospf6_schedule_abr_task(area->ospf6); @@ -1353,7 +1353,7 @@ void ospf6_abr_check_translate_nssa(struct ospf6_area *area, lsa->external_lsa_id, ospf6->router_id, ospf6->lsdb); - if (ospf6_is_router_abr(ospf6) && (type5 == NULL)) { + if (ospf6_check_and_set_router_abr(ospf6) && (type5 == NULL)) { if (IS_OSPF6_DEBUG_NSSA) zlog_debug("%s : Originating type5 LSA", __func__); ospf6_lsa_translated_nssa_new(area, lsa); diff --git a/ospf6d/ospf6_snmp.c b/ospf6d/ospf6_snmp.c index dcf76c7038..92922567e8 100644 --- a/ospf6d/ospf6_snmp.c +++ b/ospf6d/ospf6_snmp.c @@ -664,9 +664,10 @@ static uint8_t *ospfv3GeneralGroup(struct variable *v, oid *name, return SNMP_INTEGER(3); case OSPFv3AREABDRRTRSTATUS: if (ospf6) - return SNMP_INTEGER(ospf6_is_router_abr(ospf6) - ? SNMP_TRUE - : SNMP_FALSE); + return SNMP_INTEGER( + ospf6_check_and_set_router_abr(ospf6) + ? SNMP_TRUE + : SNMP_FALSE); return SNMP_INTEGER(SNMP_FALSE); case OSPFv3ASBDRRTRSTATUS: if (ospf6) diff --git a/ospf6d/ospf6_spf.c b/ospf6d/ospf6_spf.c index b0a8f01bcc..032484e288 100644 --- a/ospf6d/ospf6_spf.c +++ b/ospf6d/ospf6_spf.c @@ -284,9 +284,7 @@ static void ospf6_nexthop_calc(struct ospf6_vertex *w, struct ospf6_vertex *v, oi = ospf6_interface_lookup_by_ifindex(ifindex, ospf6->vrf_id); if (oi == NULL) { - if (IS_OSPF6_DEBUG_SPF(PROCESS)) - zlog_debug("Can't find interface in SPF: ifindex %d", - ifindex); + zlog_warn("Can't find interface in SPF: ifindex %d", ifindex); return; } @@ -475,9 +473,7 @@ void ospf6_spf_calculation(uint32_t router_id, /* construct root vertex */ lsa = ospf6_create_single_router_lsa(oa, oa->lsdb_self, router_id); if (lsa == NULL) { - if (IS_OSPF6_DEBUG_SPF(PROCESS)) - zlog_debug("%s: No router LSA for area %s", __func__, - oa->name); + zlog_warn("%s: No router LSA for area %s", __func__, oa->name); return; } @@ -605,7 +601,7 @@ static int ospf6_spf_calculation_thread(struct thread *t) monotime(&start); ospf6->ts_spf = start; - if (ospf6_is_router_abr(ospf6)) + if (ospf6_check_and_set_router_abr(ospf6)) ospf6_abr_range_reset_cost(ospf6); for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, oa)) { @@ -645,7 +641,7 @@ static int ospf6_spf_calculation_thread(struct thread *t) /* External LSA calculation */ ospf6_ase_calculate_timer_add(ospf6); - if (ospf6_is_router_abr(ospf6)) + if (ospf6_check_and_set_router_abr(ospf6)) ospf6_abr_defaults_to_stub(ospf6); monotime(&end); diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c index 33b5dd1960..7e4604e987 100644 --- a/ospf6d/ospf6_top.c +++ b/ospf6d/ospf6_top.c @@ -232,6 +232,8 @@ void ospf6_vrf_init(void) { vrf_init(ospf6_vrf_new, ospf6_vrf_enable, ospf6_vrf_disable, ospf6_vrf_delete, ospf6_vrf_enable); + + vrf_cmd_init(NULL, &ospf6d_privs); } static void ospf6_top_lsdb_hook_add(struct ospf6_lsa *lsa) @@ -268,6 +270,13 @@ static void ospf6_top_route_hook_add(struct ospf6_route *route) else if (route->table->scope_type == OSPF6_SCOPE_TYPE_AREA) { oa = (struct ospf6_area *)route->table->scope; ospf6 = oa->ospf6; + } else { + if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL) + || IS_OSPF6_DEBUG_BROUTER) + zlog_debug( + "%s: Route is not GLOBAL or scope is not of TYPE_AREA: %pFX", + __func__, &route->prefix); + return; } ospf6_abr_originate_summary(route, ospf6); @@ -284,6 +293,13 @@ static void ospf6_top_route_hook_remove(struct ospf6_route *route) else if (route->table->scope_type == OSPF6_SCOPE_TYPE_AREA) { oa = (struct ospf6_area *)route->table->scope; ospf6 = oa->ospf6; + } else { + if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL) + || IS_OSPF6_DEBUG_BROUTER) + zlog_debug( + "%s: Route is not GLOBAL or scope is not of TYPE_AREA: %pFX", + __func__, &route->prefix); + return; } route->flag |= OSPF6_ROUTE_REMOVE; @@ -410,6 +426,8 @@ static struct ospf6 *ospf6_create(const char *name) struct ospf6 *ospf6_instance_create(const char *name) { struct ospf6 *ospf6; + struct vrf *vrf; + struct interface *ifp; ospf6 = ospf6_create(name); if (DFLT_OSPF6_LOG_ADJACENCY_CHANGES) @@ -417,6 +435,13 @@ struct ospf6 *ospf6_instance_create(const char *name) if (ospf6->router_id == 0) ospf6_router_id_update(ospf6); ospf6_add(ospf6); + if (ospf6->vrf_id != VRF_UNKNOWN) { + vrf = vrf_lookup_by_id(ospf6->vrf_id); + FOR_ALL_INTERFACES (vrf, ifp) { + if (ifp->info) + ospf6_interface_start(ifp->info); + } + } if (ospf6->fd < 0) return ospf6; @@ -867,7 +892,7 @@ DEFUN (no_ospf6_distance_ospf6, return CMD_SUCCESS; } -DEFUN (ospf6_interface_area, +DEFUN_HIDDEN (ospf6_interface_area, ospf6_interface_area_cmd, "interface IFNAME area <A.B.C.D|(0-4294967295)>", "Enable routing on an IPv6 interface\n" @@ -885,6 +910,13 @@ DEFUN (ospf6_interface_area, struct interface *ifp; vrf_id_t vrf_id = VRF_DEFAULT; int ipv6_count = 0; + uint32_t area_id; + int format; + + vty_out(vty, + "This command is deprecated, because it is not VRF-aware.\n"); + vty_out(vty, + "Please, use \"ipv6 ospf6 area\" on an interface instead.\n"); if (ospf6->vrf_id != VRF_UNKNOWN) vrf_id = ospf6->vrf_id; @@ -917,8 +949,17 @@ DEFUN (ospf6_interface_area, return CMD_WARNING_CONFIG_FAILED; } - /* parse Area-ID */ - OSPF6_CMD_AREA_GET(argv[idx_ipv4]->arg, oa, ospf6); + if (str2area_id(argv[idx_ipv4]->arg, &area_id, &format)) { + vty_out(vty, "Malformed Area-ID: %s\n", argv[idx_ipv4]->arg); + return CMD_WARNING_CONFIG_FAILED; + } + + oi->area_id = area_id; + oi->area_id_format = format; + + oa = ospf6_area_lookup(area_id, ospf6); + if (oa == NULL) + oa = ospf6_area_create(area_id, ospf6, format); /* attach interface to area */ listnode_add(oa->if_list, oi); /* sort ?? */ @@ -934,7 +975,7 @@ DEFUN (ospf6_interface_area, ospf6_interface_enable(oi); /* If the router is ABR, originate summary routes */ - if (ospf6_is_router_abr(ospf6)) { + if (ospf6_check_and_set_router_abr(ospf6)) { ospf6_abr_enable_area(oa); ospf6_schedule_abr_task(oa->ospf6); } @@ -942,7 +983,7 @@ DEFUN (ospf6_interface_area, return CMD_SUCCESS; } -DEFUN (no_ospf6_interface_area, +DEFUN_HIDDEN (no_ospf6_interface_area, no_ospf6_interface_area_cmd, "no interface IFNAME area <A.B.C.D|(0-4294967295)>", NO_STR @@ -962,6 +1003,11 @@ DEFUN (no_ospf6_interface_area, uint32_t area_id; vrf_id_t vrf_id = VRF_DEFAULT; + vty_out(vty, + "This command is deprecated, because it is not VRF-aware.\n"); + vty_out(vty, + "Please, use \"no ipv6 ospf6 area\" on an interface instead.\n"); + if (ospf6->vrf_id != VRF_UNKNOWN) vrf_id = ospf6->vrf_id; @@ -1008,6 +1054,9 @@ DEFUN (no_ospf6_interface_area, ospf6_abr_disable_area(oa); } + oi->area_id = 0; + oi->area_id_format = OSPF6_AREA_FMT_UNSET; + return CMD_SUCCESS; } @@ -1585,9 +1634,6 @@ static int ospf6_distance_config_write(struct vty *vty, struct ospf6 *ospf6) /* OSPF configuration write function. */ static int config_write_ospf6(struct vty *vty) { - struct listnode *j, *k; - struct ospf6_area *oa; - struct ospf6_interface *oi; struct ospf6 *ospf6; struct listnode *node, *nnode; @@ -1638,11 +1684,6 @@ static int config_write_ospf6(struct vty *vty) ospf6_distance_config_write(vty, ospf6); ospf6_distribute_config_write(vty, ospf6); - for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, j, oa)) { - for (ALL_LIST_ELEMENTS_RO(oa->if_list, k, oi)) - vty_out(vty, " interface %s area %s\n", - oi->interface->name, oa->name); - } vty_out(vty, "!\n"); } return 0; diff --git a/ospfd/ospf_asbr.c b/ospfd/ospf_asbr.c index bda00e0c9e..2fd195bb6d 100644 --- a/ospfd/ospf_asbr.c +++ b/ospfd/ospf_asbr.c @@ -508,7 +508,6 @@ static void ospf_external_aggr_delete(struct ospf *ospf, struct route_node *rn) rn->info = NULL; route_unlock_node(rn); - route_unlock_node(rn); } struct ospf_external_aggr_rt * @@ -1160,6 +1159,7 @@ int ospf_asbr_external_aggregator_unset(struct ospf *ospf, rn = route_node_lookup(ospf->rt_aggr_tbl, (struct prefix *)p); if (!rn) return OSPF_INVALID; + route_unlock_node(rn); aggr = rn->info; @@ -1217,6 +1217,7 @@ int ospf_asbr_external_rt_advertise(struct ospf *ospf, struct prefix_ipv4 *p) rn = route_node_lookup(ospf->rt_aggr_tbl, (struct prefix *)p); if (!rn) return OSPF_INVALID; + route_unlock_node(rn); aggr = rn->info; diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c index 49829d86f1..c850df55bb 100644 --- a/ospfd/ospf_lsa.c +++ b/ospfd/ospf_lsa.c @@ -2084,6 +2084,8 @@ void ospf_external_lsa_rid_change(struct ospf *ospf) { struct external_info *ei; struct ospf_external_aggr_rt *aggr; + struct ospf_lsa *lsa = NULL; + int force; int type; for (type = 0; type < ZEBRA_ROUTE_MAX; type++) { @@ -2112,24 +2114,48 @@ void ospf_external_lsa_rid_change(struct ospf *ospf) continue; if (is_prefix_default( - (struct prefix_ipv4 *)&ei->p)) + (struct prefix_ipv4 *)&ei->p)) continue; - if (!ospf_redistribute_check(ospf, ei, NULL)) - continue; + lsa = ospf_external_info_find_lsa(ospf, &ei->p); aggr = ospf_external_aggr_match(ospf, &ei->p); if (aggr) { + + if (!ospf_redistribute_check(ospf, ei, + NULL)) + continue; + if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR)) zlog_debug( "Originate Summary LSA after reset/router-ID change"); + /* Here the LSA is originated as new */ ospf_originate_summary_lsa(ospf, aggr, ei); - } else if (!ospf_external_lsa_originate(ospf, - ei)) - flog_warn(EC_OSPF_LSA_INSTALL_FAILURE, - "LSA: AS-external-LSA was not originated."); + } else if (lsa) { + /* LSA needs to be refreshed even if + * there is no change in the route + * params if the LSA is in maxage. + */ + if (IS_LSA_MAXAGE(lsa)) + force = LSA_REFRESH_FORCE; + else + force = LSA_REFRESH_IF_CHANGED; + + ospf_external_lsa_refresh(ospf, lsa, + ei, force, 0); + } else { + if (!ospf_redistribute_check(ospf, ei, + NULL)) + continue; + + if (!ospf_external_lsa_originate(ospf, + NULL)) + flog_warn( + EC_OSPF_LSA_INSTALL_FAILURE, + "LSA: AS-external-LSA was not originated."); + } } } } diff --git a/ospfd/ospf_neighbor.c b/ospfd/ospf_neighbor.c index a1b35b2fcd..8725497f2d 100644 --- a/ospfd/ospf_neighbor.c +++ b/ospfd/ospf_neighbor.c @@ -407,6 +407,9 @@ void ospf_renegotiate_optional_capabilities(struct ospf *top) } } + /* Refresh/Re-originate external LSAs (Type-7 and Type-5).*/ + ospf_external_lsa_rid_change(top); + return; } diff --git a/ospfd/ospf_routemap.c b/ospfd/ospf_routemap.c index d3b114840e..2525c1cf3a 100644 --- a/ospfd/ospf_routemap.c +++ b/ospfd/ospf_routemap.c @@ -71,19 +71,14 @@ static void ospf_route_map_update(const char *name) /* Keep old route-map. */ struct route_map *old = ROUTEMAP(red); - if (!old) { - /* Route-map creation */ - /* Update route-map. */ - ROUTEMAP(red) = - route_map_lookup_by_name( - ROUTEMAP_NAME(red)); - - route_map_counter_increment( - ROUTEMAP(red)); - } else { - /* Route-map deletion */ - ROUTEMAP(red) = NULL; - } + ROUTEMAP(red) = + route_map_lookup_by_name( + ROUTEMAP_NAME(red)); + + if (!old) + route_map_counter_increment( + ROUTEMAP(red)); + /* No update for this distribute type. */ if (old == NULL diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index cb64187d72..54ce248d89 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -12959,6 +12959,8 @@ void ospf_vty_init(void) install_element(OSPF_NODE, &ospf_max_multipath_cmd); install_element(OSPF_NODE, &no_ospf_max_multipath_cmd); + vrf_cmd_init(NULL, &ospfd_privs); + /* Init interface related vty commands. */ ospf_vty_if_init(); diff --git a/pbrd/pbr_main.c b/pbrd/pbr_main.c index 1badaf95bd..7861559034 100644 --- a/pbrd/pbr_main.c +++ b/pbrd/pbr_main.c @@ -119,6 +119,7 @@ struct quagga_signal_t pbr_signals[] = { static const struct frr_yang_module_info *const pbrd_yang_modules[] = { &frr_filter_info, &frr_interface_info, + &frr_vrf_info, }; FRR_DAEMON_INFO(pbrd, PBR, .vty_port = PBR_VTY_PORT, diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c index 216834fe0c..3d56fc3daa 100644 --- a/pbrd/pbr_vty.c +++ b/pbrd/pbr_vty.c @@ -1143,10 +1143,14 @@ static const struct cmd_variable_handler pbr_map_name[] = { } }; +extern struct zebra_privs_t pbr_privs; + void pbr_vty_init(void) { cmd_variable_handler_register(pbr_map_name); + vrf_cmd_init(NULL, &pbr_privs); + install_node(&interface_node); if_cmd_init(); diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index f6072b1771..b3d4444652 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -3853,6 +3853,54 @@ static const char *pim_cli_get_vrf_name(struct vty *vty) return yang_dnode_get_string(vrf_node, "./name"); } +/** + * Compatibility function to keep the legacy mesh group CLI behavior: + * Delete group when there are no more configurations in it. + * + * NOTE: + * Don't forget to call `nb_cli_apply_changes` after this. + */ +static void pim_cli_legacy_mesh_group_behavior(struct vty *vty, + const char *gname) +{ + const char *vrfname; + char xpath_value[XPATH_MAXLEN]; + char xpath_member_value[XPATH_MAXLEN]; + const struct lyd_node *member_dnode; + + vrfname = pim_cli_get_vrf_name(vty); + if (vrfname == NULL) + return; + + /* Get mesh group base XPath. */ + snprintf(xpath_value, sizeof(xpath_value), + FRR_PIM_AF_XPATH "/msdp-mesh-groups[name='%s']", + "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4", gname); + /* Group must exists, otherwise just quit. */ + if (!yang_dnode_exists(vty->candidate_config->dnode, xpath_value)) + return; + + /* Group members check: */ + strlcpy(xpath_member_value, xpath_value, sizeof(xpath_member_value)); + strlcat(xpath_member_value, "/members", sizeof(xpath_member_value)); + if (yang_dnode_exists(vty->candidate_config->dnode, + xpath_member_value)) { + member_dnode = yang_dnode_get(vty->candidate_config->dnode, + xpath_member_value); + if (!yang_is_last_list_dnode(member_dnode)) + return; + } + + /* Source address check: */ + strlcpy(xpath_member_value, xpath_value, sizeof(xpath_member_value)); + strlcat(xpath_member_value, "/source", sizeof(xpath_member_value)); + if (yang_dnode_exists(vty->candidate_config->dnode, xpath_member_value)) + return; + + /* No configurations found: delete it. */ + nb_cli_enqueue_change(vty, xpath_value, NB_OP_DESTROY, NULL); +} + DEFUN (clear_ip_interfaces, clear_ip_interfaces_cmd, "clear ip interfaces [vrf NAME]", @@ -9685,305 +9733,199 @@ DEFUN (no_ip_msdp_peer, return nb_cli_apply_changes(vty, NULL); } -DEFUN (ip_msdp_mesh_group_member, - ip_msdp_mesh_group_member_cmd, - "ip msdp mesh-group WORD member A.B.C.D", - IP_STR - CFG_MSDP_STR - "Configure MSDP mesh-group\n" - "mesh group name\n" - "mesh group member\n" - "peer ip address\n") +DEFPY(ip_msdp_mesh_group_member, + ip_msdp_mesh_group_member_cmd, + "ip msdp mesh-group WORD$gname member A.B.C.D$maddr", + IP_STR + CFG_MSDP_STR + "Configure MSDP mesh-group\n" + "Mesh group name\n" + "Mesh group member\n" + "Peer IP address\n") { const char *vrfname; - char msdp_mesh_group_name_xpath[XPATH_MAXLEN]; - char msdp_mesh_group_member_xpath[XPATH_MAXLEN]; + char xpath_value[XPATH_MAXLEN]; vrfname = pim_cli_get_vrf_name(vty); if (vrfname == NULL) return CMD_WARNING_CONFIG_FAILED; - snprintf(msdp_mesh_group_name_xpath, sizeof(msdp_mesh_group_name_xpath), - FRR_PIM_AF_XPATH, - "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4"); - strlcat(msdp_mesh_group_name_xpath, "/msdp-mesh-group/mesh-group-name", - sizeof(msdp_mesh_group_name_xpath)); - snprintf(msdp_mesh_group_member_xpath, - sizeof(msdp_mesh_group_member_xpath), - FRR_PIM_AF_XPATH, - "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4"); - strlcat(msdp_mesh_group_member_xpath, "/msdp-mesh-group/member-ip", - sizeof(msdp_mesh_group_member_xpath)); + /* Create mesh group. */ + snprintf(xpath_value, sizeof(xpath_value), + FRR_PIM_AF_XPATH "/msdp-mesh-groups[name='%s']", + "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4", gname); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_CREATE, NULL); - nb_cli_enqueue_change(vty, msdp_mesh_group_name_xpath, NB_OP_MODIFY, - argv[3]->arg); - nb_cli_enqueue_change(vty, msdp_mesh_group_member_xpath, NB_OP_CREATE, - argv[5]->arg); + /* Create mesh group member. */ + strlcat(xpath_value, "/members[address='", sizeof(xpath_value)); + strlcat(xpath_value, maddr_str, sizeof(xpath_value)); + strlcat(xpath_value, "']", sizeof(xpath_value)); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_CREATE, NULL); return nb_cli_apply_changes(vty, NULL); } -DEFUN (no_ip_msdp_mesh_group_member, - no_ip_msdp_mesh_group_member_cmd, - "no ip msdp mesh-group WORD member A.B.C.D", - NO_STR - IP_STR - CFG_MSDP_STR - "Delete MSDP mesh-group member\n" - "mesh group name\n" - "mesh group member\n" - "peer ip address\n") +DEFPY(no_ip_msdp_mesh_group_member, + no_ip_msdp_mesh_group_member_cmd, + "no ip msdp mesh-group WORD$gname member A.B.C.D$maddr", + NO_STR + IP_STR + CFG_MSDP_STR + "Delete MSDP mesh-group member\n" + "Mesh group name\n" + "Mesh group member\n" + "Peer IP address\n") { const char *vrfname; - char pim_af_xpath[XPATH_MAXLEN]; - char mesh_group_xpath[XPATH_MAXLEN + 32]; - char group_member_list_xpath[XPATH_MAXLEN + 64]; - char group_member_xpath[XPATH_MAXLEN + 128]; - char source_xpath[XPATH_MAXLEN + 64]; - char mesh_group_name_xpath[XPATH_MAXLEN + 64]; - const char *mesh_group_name; - const struct lyd_node *member_dnode; + char xpath_value[XPATH_MAXLEN]; + char xpath_member_value[XPATH_MAXLEN]; vrfname = pim_cli_get_vrf_name(vty); if (vrfname == NULL) return CMD_WARNING_CONFIG_FAILED; - snprintf(pim_af_xpath, sizeof(pim_af_xpath), FRR_PIM_AF_XPATH, - "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4"); + /* Get mesh group base XPath. */ + snprintf(xpath_value, sizeof(xpath_value), + FRR_PIM_AF_XPATH "/msdp-mesh-groups[name='%s']", + "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4", gname); - snprintf(mesh_group_xpath, sizeof(mesh_group_xpath), - "%s/msdp-mesh-group", pim_af_xpath); - - snprintf(group_member_list_xpath, sizeof(group_member_list_xpath), - "%s/msdp-mesh-group/member-ip", pim_af_xpath); - - snprintf(group_member_xpath, sizeof(group_member_xpath), "%s[.='%s']", - group_member_list_xpath, argv[6]->arg); - - snprintf(source_xpath, sizeof(source_xpath), - "%s/msdp-mesh-group/source-ip", pim_af_xpath); - - snprintf(mesh_group_name_xpath, sizeof(mesh_group_name_xpath), - "%s/msdp-mesh-group/mesh-group-name", pim_af_xpath); - - if (yang_dnode_exists(running_config->dnode, mesh_group_name_xpath) - == true) { - mesh_group_name = yang_dnode_get_string(running_config->dnode, - mesh_group_name_xpath); - if (strcmp(mesh_group_name, argv[4]->arg)) { - vty_out(vty, "%% mesh-group does not exist\n"); - return CMD_WARNING_CONFIG_FAILED; - } + if (!yang_dnode_exists(vty->candidate_config->dnode, xpath_value)) { + vty_out(vty, "%% mesh-group does not exist\n"); + return CMD_WARNING_CONFIG_FAILED; } - if (yang_dnode_exists(vty->candidate_config->dnode, - group_member_xpath)) { - if (!yang_dnode_exists(vty->candidate_config->dnode, - source_xpath)) { - member_dnode = yang_dnode_get( - vty->candidate_config->dnode, - group_member_xpath); - if (yang_is_last_list_dnode(member_dnode)) { - nb_cli_enqueue_change(vty, mesh_group_xpath, - NB_OP_DESTROY, NULL); - return nb_cli_apply_changes(vty, NULL); - } - nb_cli_enqueue_change(vty, group_member_list_xpath, - NB_OP_DESTROY, argv[6]->arg); - return nb_cli_apply_changes(vty, NULL); - } - nb_cli_enqueue_change(vty, group_member_list_xpath, - NB_OP_DESTROY, argv[6]->arg); - return nb_cli_apply_changes(vty, NULL); + /* Remove mesh group member. */ + strlcpy(xpath_member_value, xpath_value, sizeof(xpath_member_value)); + strlcat(xpath_member_value, "/members[address='", + sizeof(xpath_member_value)); + strlcat(xpath_member_value, maddr_str, sizeof(xpath_member_value)); + strlcat(xpath_member_value, "']", sizeof(xpath_member_value)); + if (!yang_dnode_exists(vty->candidate_config->dnode, + xpath_member_value)) { + vty_out(vty, "%% mesh-group member does not exist\n"); + return CMD_WARNING_CONFIG_FAILED; } - vty_out(vty, "%% mesh-group member does not exist\n"); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_DESTROY, NULL); - return CMD_SUCCESS; + /* + * If this is the last member, then we must remove the group altogether + * to not break legacy CLI behaviour. + */ + pim_cli_legacy_mesh_group_behavior(vty, gname); + + return nb_cli_apply_changes(vty, NULL); } -DEFUN (ip_msdp_mesh_group_source, - ip_msdp_mesh_group_source_cmd, - "ip msdp mesh-group WORD source A.B.C.D", - IP_STR - CFG_MSDP_STR - "Configure MSDP mesh-group\n" - "mesh group name\n" - "mesh group local address\n" - "source ip address for the TCP connection\n") +DEFPY(ip_msdp_mesh_group_source, + ip_msdp_mesh_group_source_cmd, + "ip msdp mesh-group WORD$gname source A.B.C.D$saddr", + IP_STR + CFG_MSDP_STR + "Configure MSDP mesh-group\n" + "Mesh group name\n" + "Mesh group local address\n" + "Source IP address for the TCP connection\n") { const char *vrfname; - char msdp_mesh_source_ip_xpath[XPATH_MAXLEN]; - char msdp_mesh_group_name_xpath[XPATH_MAXLEN]; + char xpath_value[XPATH_MAXLEN]; vrfname = pim_cli_get_vrf_name(vty); if (vrfname == NULL) return CMD_WARNING_CONFIG_FAILED; - snprintf(msdp_mesh_group_name_xpath, sizeof(msdp_mesh_group_name_xpath), - FRR_PIM_AF_XPATH, - "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4"); - strlcat(msdp_mesh_group_name_xpath, "/msdp-mesh-group/mesh-group-name", - sizeof(msdp_mesh_group_name_xpath)); - - snprintf(msdp_mesh_source_ip_xpath, sizeof(msdp_mesh_source_ip_xpath), - FRR_PIM_AF_XPATH, - "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4"); - strlcat(msdp_mesh_source_ip_xpath, "/msdp-mesh-group/source-ip", - sizeof(msdp_mesh_source_ip_xpath)); + /* Create mesh group. */ + snprintf(xpath_value, sizeof(xpath_value), + FRR_PIM_AF_XPATH "/msdp-mesh-groups[name='%s']", + "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4", gname); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_CREATE, NULL); - nb_cli_enqueue_change(vty, msdp_mesh_group_name_xpath, NB_OP_MODIFY, - argv[3]->arg); - nb_cli_enqueue_change(vty, msdp_mesh_source_ip_xpath, NB_OP_MODIFY, - argv[5]->arg); + /* Create mesh group member. */ + strlcat(xpath_value, "/source", sizeof(xpath_value)); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, saddr_str); return nb_cli_apply_changes(vty, NULL); } -DEFUN (no_ip_msdp_mesh_group_source, - no_ip_msdp_mesh_group_source_cmd, - "no ip msdp mesh-group WORD source [A.B.C.D]", - NO_STR - IP_STR - CFG_MSDP_STR - "Delete MSDP mesh-group source\n" - "mesh group name\n" - "mesh group source\n" - "mesh group local address\n") +DEFPY(no_ip_msdp_mesh_group_source, + no_ip_msdp_mesh_group_source_cmd, + "no ip msdp mesh-group WORD$gname source [A.B.C.D]", + NO_STR + IP_STR + CFG_MSDP_STR + "Delete MSDP mesh-group source\n" + "Mesh group name\n" + "Mesh group source\n" + "Mesh group local address\n") { const char *vrfname; - char msdp_mesh_xpath[XPATH_MAXLEN]; - char source_xpath[XPATH_MAXLEN]; - char group_member_xpath[XPATH_MAXLEN]; - char mesh_group_name_xpath[XPATH_MAXLEN]; - const char *mesh_group_name; + char xpath_value[XPATH_MAXLEN]; vrfname = pim_cli_get_vrf_name(vty); if (vrfname == NULL) return CMD_WARNING_CONFIG_FAILED; - snprintf(msdp_mesh_xpath, sizeof(msdp_mesh_xpath), - FRR_PIM_AF_XPATH, - "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4"); - strlcat(msdp_mesh_xpath, "/msdp-mesh-group", sizeof(msdp_mesh_xpath)); + /* Get mesh group base XPath. */ + snprintf(xpath_value, sizeof(xpath_value), + FRR_PIM_AF_XPATH "/msdp-mesh-groups[name='%s']", + "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4", gname); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_CREATE, NULL); - snprintf(source_xpath, sizeof(source_xpath), - FRR_PIM_AF_XPATH, - "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4"); - strlcat(source_xpath, "/msdp-mesh-group/source-ip", - sizeof(source_xpath)); + /* Create mesh group member. */ + strlcat(xpath_value, "/source", sizeof(xpath_value)); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_DESTROY, NULL); - snprintf(group_member_xpath, - sizeof(group_member_xpath), - FRR_PIM_AF_XPATH, - "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4"); - strlcat(group_member_xpath, "/msdp-mesh-group/member-ip", - sizeof(group_member_xpath)); + /* + * If this is the last member, then we must remove the group altogether + * to not break legacy CLI behaviour. + */ + pim_cli_legacy_mesh_group_behavior(vty, gname); - snprintf(mesh_group_name_xpath, sizeof(mesh_group_name_xpath), - FRR_PIM_AF_XPATH, - "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4"); - strlcat(mesh_group_name_xpath, "/msdp-mesh-group/mesh-group-name", - sizeof(mesh_group_name_xpath)); - - if (yang_dnode_exists(running_config->dnode, mesh_group_name_xpath) - == true) { - mesh_group_name = yang_dnode_get_string(running_config->dnode, - mesh_group_name_xpath); - if (strcmp(mesh_group_name, argv[4]->arg)) { - vty_out(vty, "%% mesh-group does not exist\n"); - return CMD_WARNING_CONFIG_FAILED; - } - } - - if (!yang_dnode_exists(vty->candidate_config->dnode, - group_member_xpath)) { - nb_cli_enqueue_change(vty, msdp_mesh_xpath, NB_OP_DESTROY, - NULL); - return nb_cli_apply_changes(vty, NULL); - } - nb_cli_enqueue_change(vty, source_xpath, NB_OP_DESTROY, NULL); return nb_cli_apply_changes(vty, NULL); } -DEFUN (no_ip_msdp_mesh_group, - no_ip_msdp_mesh_group_cmd, - "no ip msdp mesh-group [WORD]", - NO_STR - IP_STR - CFG_MSDP_STR - "Delete MSDP mesh-group\n" - "mesh group name") +DEFPY(no_ip_msdp_mesh_group, + no_ip_msdp_mesh_group_cmd, + "no ip msdp mesh-group WORD$gname", + NO_STR + IP_STR + CFG_MSDP_STR + "Delete MSDP mesh-group\n" + "Mesh group name") { const char *vrfname; - const char *mesh_group_name; - char xpath[XPATH_MAXLEN]; - char msdp_mesh_xpath[XPATH_MAXLEN]; + char xpath_value[XPATH_MAXLEN]; vrfname = pim_cli_get_vrf_name(vty); if (vrfname == NULL) return CMD_WARNING_CONFIG_FAILED; - if (argc == 5) { - snprintf(xpath, sizeof(xpath), FRR_PIM_AF_XPATH, "frr-pim:pimd", - "pim", vrfname, "frr-routing:ipv4"); - strlcat(xpath, "/msdp-mesh-group/mesh-group-name", - sizeof(xpath)); - - if (yang_dnode_exists(running_config->dnode, xpath) == true) { - mesh_group_name = - yang_dnode_get_string(running_config->dnode, - xpath); - - if (strcmp(mesh_group_name, argv[4]->arg)) { - vty_out(vty, "%% mesh-group does not exist\n"); - return CMD_WARNING_CONFIG_FAILED; - } - } - } - - snprintf(msdp_mesh_xpath, sizeof(msdp_mesh_xpath), - FRR_PIM_AF_XPATH, - "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4"); - strlcat(msdp_mesh_xpath, "/msdp-mesh-group", sizeof(msdp_mesh_xpath)); + /* Get mesh group base XPath. */ + snprintf(xpath_value, sizeof(xpath_value), + FRR_PIM_AF_XPATH "/msdp-mesh-groups[name='%s']", + "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4", gname); + if (!yang_dnode_exists(vty->candidate_config->dnode, xpath_value)) + return CMD_SUCCESS; - nb_cli_enqueue_change(vty, msdp_mesh_xpath, NB_OP_DESTROY, NULL); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_DESTROY, NULL); return nb_cli_apply_changes(vty, NULL); } -static void print_empty_json_obj(struct vty *vty) -{ - json_object *json; - json = json_object_new_object(); - vty_out(vty, "%s\n", - json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY)); - json_object_free(json); -} - -static void ip_msdp_show_mesh_group(struct pim_instance *pim, struct vty *vty, - bool uj) +static void ip_msdp_show_mesh_group(struct vty *vty, struct pim_msdp_mg *mg, + struct json_object *json) { struct listnode *mbrnode; struct pim_msdp_mg_mbr *mbr; - struct pim_msdp_mg *mg = pim->msdp.mg; char mbr_str[INET_ADDRSTRLEN]; char src_str[INET_ADDRSTRLEN]; char state_str[PIM_MSDP_STATE_STRLEN]; enum pim_msdp_peer_state state; - json_object *json = NULL; json_object *json_mg_row = NULL; json_object *json_members = NULL; json_object *json_row = NULL; - if (!mg) { - if (uj) - print_empty_json_obj(vty); - return; - } - pim_inet4_dump("<source?>", mg->src_ip, src_str, sizeof(src_str)); - if (uj) { - json = json_object_new_object(); + if (json) { /* currently there is only one mesh group but we should still * make * it a dict with mg-name as key */ @@ -10005,7 +9947,7 @@ static void ip_msdp_show_mesh_group(struct pim_instance *pim, struct vty *vty, state = PIM_MSDP_DISABLED; } pim_msdp_state_dump(state, state_str, sizeof(state_str)); - if (uj) { + if (json) { json_row = json_object_new_object(); json_object_string_add(json_row, "member", mbr_str); json_object_string_add(json_row, "state", state_str); @@ -10020,12 +9962,8 @@ static void ip_msdp_show_mesh_group(struct pim_instance *pim, struct vty *vty, } } - if (uj) { + if (json) json_object_object_add(json, mg->mesh_group_name, json_mg_row); - vty_out(vty, "%s\n", json_object_to_json_string_ext( - json, JSON_C_TO_STRING_PRETTY)); - json_object_free(json); - } } DEFUN (show_ip_msdp_mesh_group, @@ -10040,12 +9978,34 @@ DEFUN (show_ip_msdp_mesh_group, { bool uj = use_json(argc, argv); int idx = 2; + struct pim_msdp_mg *mg; struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + struct pim_instance *pim = vrf->info; + struct json_object *json = NULL; if (!vrf) return CMD_WARNING; - ip_msdp_show_mesh_group(vrf->info, vty, uj); + /* Quick case: list is empty. */ + if (SLIST_EMPTY(&pim->msdp.mglist)) { + if (uj) + vty_out(vty, "{}\n"); + + return CMD_SUCCESS; + } + + if (uj) + json = json_object_new_object(); + + SLIST_FOREACH (mg, &pim->msdp.mglist, mg_entry) + ip_msdp_show_mesh_group(vty, mg, json); + + if (uj) { + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } return CMD_SUCCESS; } @@ -10061,23 +10021,32 @@ DEFUN (show_ip_msdp_mesh_group_vrf_all, JSON_STR) { bool uj = use_json(argc, argv); + struct json_object *json = NULL, *vrf_json = NULL; + struct pim_instance *pim; + struct pim_msdp_mg *mg; struct vrf *vrf; - bool first = true; if (uj) - vty_out(vty, "{ "); + json = json_object_new_object(); + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { if (uj) { - if (!first) - vty_out(vty, ", "); - vty_out(vty, " \"%s\": ", vrf->name); - first = false; + vrf_json = json_object_new_object(); + json_object_object_add(json, vrf->name, vrf_json); } else vty_out(vty, "VRF: %s\n", vrf->name); - ip_msdp_show_mesh_group(vrf->info, vty, uj); + + pim = vrf->info; + SLIST_FOREACH (mg, &pim->msdp.mglist, mg_entry) + ip_msdp_show_mesh_group(vty, mg, vrf_json); + } + + if (uj) { + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); } - if (uj) - vty_out(vty, "}\n"); return CMD_SUCCESS; } diff --git a/pimd/pim_msdp.c b/pimd/pim_msdp.c index 9cf73c38c3..095c6de549 100644 --- a/pimd/pim_msdp.c +++ b/pimd/pim_msdp.c @@ -58,8 +58,6 @@ static void pim_msdp_sa_deref(struct pim_msdp_sa *sa, enum pim_msdp_sa_flags flags); static int pim_msdp_mg_mbr_comp(const void *p1, const void *p2); static void pim_msdp_mg_mbr_free(struct pim_msdp_mg_mbr *mbr); -static void pim_msdp_mg_mbr_do_del(struct pim_msdp_mg *mg, - struct pim_msdp_mg_mbr *mbr); /************************ SA cache management ******************************/ static void pim_msdp_sa_timer_expiry_log(struct pim_msdp_sa *sa, @@ -1252,27 +1250,33 @@ static int pim_msdp_peer_comp(const void *p1, const void *p2) } /************************** Mesh group management **************************/ -static void pim_msdp_mg_free(struct pim_instance *pim) +void pim_msdp_mg_free(struct pim_instance *pim, struct pim_msdp_mg **mgp) { - struct pim_msdp_mg *mg = pim->msdp.mg; + struct pim_msdp_mg_mbr *mbr; + struct listnode *n, *nn; - /* If the mesh-group has valid member or src_ip don't delete it */ - if (!mg || mg->mbr_cnt || (mg->src_ip.s_addr != INADDR_ANY)) { + if (*mgp == NULL) return; - } + + /* SIP is being removed - tear down all active peer sessions */ + for (ALL_LIST_ELEMENTS((*mgp)->mbr_list, n, nn, mbr)) + pim_msdp_mg_mbr_del((*mgp), mbr); if (PIM_DEBUG_MSDP_EVENTS) { - zlog_debug("MSDP mesh-group %s deleted", mg->mesh_group_name); + zlog_debug("MSDP mesh-group %s deleted", + (*mgp)->mesh_group_name); } - XFREE(MTYPE_PIM_MSDP_MG_NAME, mg->mesh_group_name); - if (mg->mbr_list) - list_delete(&mg->mbr_list); + XFREE(MTYPE_PIM_MSDP_MG_NAME, (*mgp)->mesh_group_name); + + if ((*mgp)->mbr_list) + list_delete(&(*mgp)->mbr_list); - XFREE(MTYPE_PIM_MSDP_MG, pim->msdp.mg); + XFREE(MTYPE_PIM_MSDP_MG, (*mgp)); } -static struct pim_msdp_mg *pim_msdp_mg_new(const char *mesh_group_name) +struct pim_msdp_mg *pim_msdp_mg_new(struct pim_instance *pim, + const char *mesh_group_name) { struct pim_msdp_mg *mg; @@ -1286,52 +1290,10 @@ static struct pim_msdp_mg *pim_msdp_mg_new(const char *mesh_group_name) if (PIM_DEBUG_MSDP_EVENTS) { zlog_debug("MSDP mesh-group %s created", mg->mesh_group_name); } - return mg; -} - -enum pim_msdp_err pim_msdp_mg_del(struct pim_instance *pim, - const char *mesh_group_name) -{ - struct pim_msdp_mg *mg = pim->msdp.mg; - struct pim_msdp_mg_mbr *mbr; - - if (!mg - || (mesh_group_name - && strcmp(mg->mesh_group_name, mesh_group_name))) { - return PIM_MSDP_ERR_NO_MG; - } - - /* delete all the mesh-group members */ - while (!list_isempty(mg->mbr_list)) { - mbr = listnode_head(mg->mbr_list); - pim_msdp_mg_mbr_do_del(mg, mbr); - } - - /* clear src ip */ - mg->src_ip.s_addr = INADDR_ANY; - - /* free up the mesh-group */ - pim_msdp_mg_free(pim); - return PIM_MSDP_ERR_NONE; -} - -static enum pim_msdp_err pim_msdp_mg_add(struct pim_instance *pim, - const char *mesh_group_name) -{ - if (pim->msdp.mg) { - if (!strcmp(pim->msdp.mg->mesh_group_name, mesh_group_name)) { - return PIM_MSDP_ERR_NONE; - } - /* currently only one mesh-group can exist at a time */ - return PIM_MSDP_ERR_MAX_MESH_GROUPS; - } - pim->msdp.mg = pim_msdp_mg_new(mesh_group_name); - if (!pim->msdp.mg) { - return PIM_MSDP_ERR_OOM; - } + SLIST_INSERT_HEAD(&pim->msdp.mglist, mg, mg_entry); - return PIM_MSDP_ERR_NONE; + return mg; } static int pim_msdp_mg_mbr_comp(const void *p1, const void *p2) @@ -1353,66 +1315,7 @@ static void pim_msdp_mg_mbr_free(struct pim_msdp_mg_mbr *mbr) XFREE(MTYPE_PIM_MSDP_MG_MBR, mbr); } -static struct pim_msdp_mg_mbr *pim_msdp_mg_mbr_find(struct pim_instance *pim, - struct in_addr mbr_ip) -{ - struct pim_msdp_mg_mbr *mbr; - struct listnode *mbr_node; - - if (!pim->msdp.mg) { - return NULL; - } - /* we can move this to a hash but considering that number of peers in - * a mesh-group that seems like bit of an overkill */ - for (ALL_LIST_ELEMENTS_RO(pim->msdp.mg->mbr_list, mbr_node, mbr)) { - if (mbr->mbr_ip.s_addr == mbr_ip.s_addr) { - return mbr; - } - } - return mbr; -} - -enum pim_msdp_err pim_msdp_mg_mbr_add(struct pim_instance *pim, - const char *mesh_group_name, - struct in_addr mbr_ip) -{ - int rc; - struct pim_msdp_mg_mbr *mbr; - struct pim_msdp_mg *mg; - - rc = pim_msdp_mg_add(pim, mesh_group_name); - if (rc != PIM_MSDP_ERR_NONE) { - return rc; - } - - mg = pim->msdp.mg; - mbr = pim_msdp_mg_mbr_find(pim, mbr_ip); - if (mbr) { - return PIM_MSDP_ERR_MG_MBR_EXISTS; - } - - mbr = XCALLOC(MTYPE_PIM_MSDP_MG_MBR, sizeof(*mbr)); - mbr->mbr_ip = mbr_ip; - listnode_add_sort(mg->mbr_list, mbr); - - /* if valid SIP has been configured add peer session */ - if (mg->src_ip.s_addr != INADDR_ANY) { - pim_msdp_peer_add(pim, mbr_ip, mg->src_ip, mesh_group_name, - &mbr->mp); - } - - if (PIM_DEBUG_MSDP_EVENTS) { - char ip_str[INET_ADDRSTRLEN]; - pim_inet4_dump("<mbr?>", mbr->mbr_ip, ip_str, sizeof(ip_str)); - zlog_debug("MSDP mesh-group %s mbr %s created", - mg->mesh_group_name, ip_str); - } - ++mg->mbr_cnt; - return PIM_MSDP_ERR_NONE; -} - -static void pim_msdp_mg_mbr_do_del(struct pim_msdp_mg *mg, - struct pim_msdp_mg_mbr *mbr) +void pim_msdp_mg_mbr_del(struct pim_msdp_mg *mg, struct pim_msdp_mg_mbr *mbr) { /* Delete active peer session if any */ if (mbr->mp) { @@ -1432,34 +1335,10 @@ static void pim_msdp_mg_mbr_do_del(struct pim_msdp_mg *mg, } } -enum pim_msdp_err pim_msdp_mg_mbr_del(struct pim_instance *pim, - const char *mesh_group_name, - struct in_addr mbr_ip) -{ - struct pim_msdp_mg_mbr *mbr; - struct pim_msdp_mg *mg = pim->msdp.mg; - - if (!mg || strcmp(mg->mesh_group_name, mesh_group_name)) { - return PIM_MSDP_ERR_NO_MG; - } - - mbr = pim_msdp_mg_mbr_find(pim, mbr_ip); - if (!mbr) { - return PIM_MSDP_ERR_NO_MG_MBR; - } - - pim_msdp_mg_mbr_do_del(mg, mbr); - /* if there are no references to the mg free it */ - pim_msdp_mg_free(pim); - - return PIM_MSDP_ERR_NONE; -} - -static void pim_msdp_mg_src_do_del(struct pim_instance *pim) +static void pim_msdp_src_del(struct pim_msdp_mg *mg) { struct pim_msdp_mg_mbr *mbr; struct listnode *mbr_node; - struct pim_msdp_mg *mg = pim->msdp.mg; /* SIP is being removed - tear down all active peer sessions */ for (ALL_LIST_ELEMENTS_RO(mg->mbr_list, mbr_node, mbr)) { @@ -1474,91 +1353,38 @@ static void pim_msdp_mg_src_do_del(struct pim_instance *pim) } } -enum pim_msdp_err pim_msdp_mg_src_del(struct pim_instance *pim, - const char *mesh_group_name) -{ - struct pim_msdp_mg *mg = pim->msdp.mg; - - if (!mg || strcmp(mg->mesh_group_name, mesh_group_name)) { - return PIM_MSDP_ERR_NO_MG; - } - - if (mg->src_ip.s_addr != INADDR_ANY) { - mg->src_ip.s_addr = INADDR_ANY; - pim_msdp_mg_src_do_del(pim); - /* if there are no references to the mg free it */ - pim_msdp_mg_free(pim); - } - return PIM_MSDP_ERR_NONE; -} - -enum pim_msdp_err pim_msdp_mg_src_add(struct pim_instance *pim, - const char *mesh_group_name, - struct in_addr src_ip) -{ - int rc; - struct pim_msdp_mg_mbr *mbr; - struct listnode *mbr_node; - struct pim_msdp_mg *mg; - - if (src_ip.s_addr == INADDR_ANY) { - pim_msdp_mg_src_del(pim, mesh_group_name); - return PIM_MSDP_ERR_NONE; - } - - rc = pim_msdp_mg_add(pim, mesh_group_name); - if (rc != PIM_MSDP_ERR_NONE) { - return rc; - } - - mg = pim->msdp.mg; - if (mg->src_ip.s_addr != INADDR_ANY) { - pim_msdp_mg_src_do_del(pim); - } - mg->src_ip = src_ip; - - for (ALL_LIST_ELEMENTS_RO(mg->mbr_list, mbr_node, mbr)) { - pim_msdp_peer_add(pim, mbr->mbr_ip, mg->src_ip, mesh_group_name, - &mbr->mp); - } - - if (PIM_DEBUG_MSDP_EVENTS) { - char ip_str[INET_ADDRSTRLEN]; - pim_inet4_dump("<src?>", mg->src_ip, ip_str, sizeof(ip_str)); - zlog_debug("MSDP mesh-group %s src %s set", mg->mesh_group_name, - ip_str); - } - return PIM_MSDP_ERR_NONE; -} - /*********************** MSDP feature APIs *********************************/ int pim_msdp_config_write(struct pim_instance *pim, struct vty *vty, const char *spaces) { + struct pim_msdp_mg *mg; struct listnode *mbrnode; struct pim_msdp_mg_mbr *mbr; - struct pim_msdp_mg *mg = pim->msdp.mg; char mbr_str[INET_ADDRSTRLEN]; char src_str[INET_ADDRSTRLEN]; int count = 0; - if (!mg) { + if (SLIST_EMPTY(&pim->msdp.mglist)) return count; - } - if (mg->src_ip.s_addr != INADDR_ANY) { - pim_inet4_dump("<src?>", mg->src_ip, src_str, sizeof(src_str)); - vty_out(vty, "%sip msdp mesh-group %s source %s\n", spaces, - mg->mesh_group_name, src_str); - ++count; - } + SLIST_FOREACH (mg, &pim->msdp.mglist, mg_entry) { + if (mg->src_ip.s_addr != INADDR_ANY) { + pim_inet4_dump("<src?>", mg->src_ip, src_str, + sizeof(src_str)); + vty_out(vty, "%sip msdp mesh-group %s source %s\n", + spaces, mg->mesh_group_name, src_str); + ++count; + } - for (ALL_LIST_ELEMENTS_RO(mg->mbr_list, mbrnode, mbr)) { - pim_inet4_dump("<mbr?>", mbr->mbr_ip, mbr_str, sizeof(mbr_str)); - vty_out(vty, "%sip msdp mesh-group %s member %s\n", spaces, - mg->mesh_group_name, mbr_str); - ++count; + for (ALL_LIST_ELEMENTS_RO(mg->mbr_list, mbrnode, mbr)) { + pim_inet4_dump("<mbr?>", mbr->mbr_ip, mbr_str, + sizeof(mbr_str)); + vty_out(vty, "%sip msdp mesh-group %s member %s\n", + spaces, mg->mesh_group_name, mbr_str); + ++count; + } } + return count; } @@ -1623,11 +1449,13 @@ void pim_msdp_init(struct pim_instance *pim, struct thread_master *master) /* counterpart to MSDP init; XXX: unused currently */ void pim_msdp_exit(struct pim_instance *pim) { - pim_msdp_sa_adv_timer_setup(pim, false); + struct pim_msdp_mg *mg; - /* XXX: stop listener and delete all peer sessions */ + pim_msdp_sa_adv_timer_setup(pim, false); - pim_msdp_mg_free(pim); + /* Stop listener and delete all peer sessions */ + while ((mg = SLIST_FIRST(&pim->msdp.mglist)) != NULL) + pim_msdp_mg_free(pim, &mg); if (pim->msdp.peer_hash) { hash_clean(pim->msdp.peer_hash, NULL); @@ -1653,3 +1481,57 @@ void pim_msdp_exit(struct pim_instance *pim) stream_free(pim->msdp.work_obuf); pim->msdp.work_obuf = NULL; } + +void pim_msdp_mg_src_add(struct pim_instance *pim, struct pim_msdp_mg *mg, + struct in_addr *ai) +{ + struct pim_msdp_mg_mbr *mbr; + struct listnode *mbr_node; + + /* Stop all connections and remove data structures. */ + pim_msdp_src_del(mg); + + /* Set new address. */ + mg->src_ip = *ai; + + /* No new address, disable everyone. */ + if (ai->s_addr == INADDR_ANY) { + if (PIM_DEBUG_MSDP_EVENTS) + zlog_debug("MSDP mesh-group %s src unset", + mg->mesh_group_name); + return; + } + + /* Create data structures and start TCP connection. */ + for (ALL_LIST_ELEMENTS_RO(mg->mbr_list, mbr_node, mbr)) + pim_msdp_peer_add(pim, mbr->mbr_ip, mg->src_ip, + mg->mesh_group_name, &mbr->mp); + + if (PIM_DEBUG_MSDP_EVENTS) + zlog_debug("MSDP mesh-group %s src %pI4 set", + mg->mesh_group_name, &mg->src_ip); +} + +struct pim_msdp_mg_mbr *pim_msdp_mg_mbr_add(struct pim_instance *pim, + struct pim_msdp_mg *mg, + struct in_addr *ia) +{ + struct pim_msdp_mg_mbr *mbr; + + mbr = XCALLOC(MTYPE_PIM_MSDP_MG_MBR, sizeof(*mbr)); + mbr->mbr_ip = *ia; + listnode_add_sort(mg->mbr_list, mbr); + + /* if valid SIP has been configured add peer session */ + if (mg->src_ip.s_addr != INADDR_ANY) + pim_msdp_peer_add(pim, mbr->mbr_ip, mg->src_ip, + mg->mesh_group_name, &mbr->mp); + + if (PIM_DEBUG_MSDP_EVENTS) + zlog_debug("MSDP mesh-group %s mbr %pI4 created", + mg->mesh_group_name, &mbr->mbr_ip); + + ++mg->mbr_cnt; + + return mbr; +} diff --git a/pimd/pim_msdp.h b/pimd/pim_msdp.h index 4d01880fbf..bb7ee01ad8 100644 --- a/pimd/pim_msdp.h +++ b/pimd/pim_msdp.h @@ -19,6 +19,8 @@ #ifndef PIM_MSDP_H #define PIM_MSDP_H +#include "lib/openbsd-queue.h" + enum pim_msdp_peer_state { PIM_MSDP_DISABLED, PIM_MSDP_INACTIVE, @@ -160,8 +162,13 @@ struct pim_msdp_mg { struct in_addr src_ip; uint32_t mbr_cnt; struct list *mbr_list; + + /** Belongs to PIM instance list. */ + SLIST_ENTRY(pim_msdp_mg) mg_entry; }; +SLIST_HEAD(pim_mesh_group_list, pim_msdp_mg); + enum pim_msdp_flags { PIM_MSDPF_NONE = 0, PIM_MSDPF_ENABLE = (1 << 0), @@ -196,8 +203,8 @@ struct pim_msdp { struct in_addr originator_id; - /* currently only one mesh-group is supported - so just stash it here */ - struct pim_msdp_mg *mg; + /** List of mesh groups. */ + struct pim_mesh_group_list mglist; }; #define PIM_MSDP_PEER_READ_ON(mp) \ @@ -246,17 +253,39 @@ bool pim_msdp_peer_rpf_check(struct pim_msdp_peer *mp, struct in_addr rp); void pim_msdp_up_join_state_changed(struct pim_instance *pim, struct pim_upstream *xg_up); void pim_msdp_up_del(struct pim_instance *pim, struct prefix_sg *sg); -enum pim_msdp_err pim_msdp_mg_mbr_add(struct pim_instance *pim, - const char *mesh_group_name, - struct in_addr mbr_ip); -enum pim_msdp_err pim_msdp_mg_mbr_del(struct pim_instance *pim, - const char *mesh_group_name, - struct in_addr mbr_ip); -enum pim_msdp_err pim_msdp_mg_src_del(struct pim_instance *pim, - const char *mesh_group_name); -enum pim_msdp_err pim_msdp_mg_src_add(struct pim_instance *pim, - const char *mesh_group_name, - struct in_addr src_ip); enum pim_msdp_err pim_msdp_mg_del(struct pim_instance *pim, const char *mesh_group_name); + +/** + * Allocates a new mesh group data structure under PIM instance. + */ +struct pim_msdp_mg *pim_msdp_mg_new(struct pim_instance *pim, + const char *mesh_group_name); +/** + * Deallocates mesh group data structure under PIM instance. + */ +void pim_msdp_mg_free(struct pim_instance *pim, struct pim_msdp_mg **mgp); + +/** + * Change the source address of a mesh group peers. It will do the following: + * - Close all peers TCP connections + * - Recreate peers data structure + * - Start TCP connections with new local address. + */ +void pim_msdp_mg_src_add(struct pim_instance *pim, struct pim_msdp_mg *mg, + struct in_addr *ai); + +/** + * Add new peer to mesh group and starts the connection if source address is + * configured. + */ +struct pim_msdp_mg_mbr *pim_msdp_mg_mbr_add(struct pim_instance *pim, + struct pim_msdp_mg *mg, + struct in_addr *ia); + +/** + * Stops the connection and removes the peer data structures. + */ +void pim_msdp_mg_mbr_del(struct pim_msdp_mg *mg, struct pim_msdp_mg_mbr *mbr); + #endif diff --git a/pimd/pim_nb.c b/pimd/pim_nb.c index 37c539883d..ea53f1ef12 100644 --- a/pimd/pim_nb.c +++ b/pimd/pim_nb.c @@ -118,31 +118,24 @@ const struct frr_yang_module_info frr_pim_info = { } }, { - .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-mesh-group", + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-mesh-groups", .cbs = { - .create = routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_create, - .destroy = routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_destroy, + .create = pim_msdp_mesh_group_create, + .destroy = pim_msdp_mesh_group_destroy, } }, { - .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-mesh-group/mesh-group-name", + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-mesh-groups/source", .cbs = { - .modify = routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_mesh_group_name_modify, - .destroy = routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_mesh_group_name_destroy, + .modify = pim_msdp_mesh_group_source_modify, + .destroy = pim_msdp_mesh_group_source_destroy, } }, { - .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-mesh-group/member-ip", + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-mesh-groups/members", .cbs = { - .create = routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_member_ip_create, - .destroy = routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_member_ip_destroy, - } - }, - { - .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-mesh-group/source-ip", - .cbs = { - .modify = routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_source_ip_modify, - .destroy = routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_source_ip_destroy, + .create = pim_msdp_mesh_group_members_create, + .destroy = pim_msdp_mesh_group_members_destroy, } }, { diff --git a/pimd/pim_nb.h b/pimd/pim_nb.h index 440384e45c..1959b403ff 100644 --- a/pimd/pim_nb.h +++ b/pimd/pim_nb.h @@ -60,22 +60,12 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_ss struct nb_cb_create_args *args); int routing_control_plane_protocols_control_plane_protocol_pim_address_family_ssm_pingd_source_ip_destroy( struct nb_cb_destroy_args *args); -int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_create( - struct nb_cb_create_args *args); -int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_destroy( - struct nb_cb_destroy_args *args); -int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_mesh_group_name_modify( - struct nb_cb_modify_args *args); -int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_mesh_group_name_destroy( - struct nb_cb_destroy_args *args); -int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_member_ip_create( - struct nb_cb_create_args *args); -int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_member_ip_destroy( - struct nb_cb_destroy_args *args); -int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_source_ip_modify( - struct nb_cb_modify_args *args); -int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_source_ip_destroy( - struct nb_cb_destroy_args *args); +int pim_msdp_mesh_group_create(struct nb_cb_create_args *args); +int pim_msdp_mesh_group_destroy(struct nb_cb_destroy_args *args); +int pim_msdp_mesh_group_members_create(struct nb_cb_create_args *args); +int pim_msdp_mesh_group_members_destroy(struct nb_cb_destroy_args *args); +int pim_msdp_mesh_group_source_modify(struct nb_cb_modify_args *args); +int pim_msdp_mesh_group_source_destroy(struct nb_cb_destroy_args *args); int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_peer_create( struct nb_cb_create_args *args); int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_peer_destroy( diff --git a/pimd/pim_nb_config.c b/pimd/pim_nb_config.c index 11e8da3b87..b70656ea7b 100644 --- a/pimd/pim_nb_config.c +++ b/pimd/pim_nb_config.c @@ -243,147 +243,6 @@ static int pim_ssm_cmd_worker(struct pim_instance *pim, const char *plist, return ret; } -static int ip_no_msdp_mesh_group_cmd_worker(struct pim_instance *pim, - const char *mg, - char *errmsg, size_t errmsg_len) -{ - enum pim_msdp_err result; - - result = pim_msdp_mg_del(pim, mg); - - switch (result) { - case PIM_MSDP_ERR_NONE: - break; - case PIM_MSDP_ERR_NO_MG: - snprintf(errmsg, errmsg_len, - "%% mesh-group does not exist"); - break; - default: - snprintf(errmsg, errmsg_len, - "mesh-group source del failed"); - } - - return result ? NB_ERR : NB_OK; -} - -static int ip_msdp_mesh_group_member_cmd_worker(struct pim_instance *pim, - const char *mg, - struct in_addr mbr_ip, - char *errmsg, size_t errmsg_len) -{ - enum pim_msdp_err result; - int ret = NB_OK; - - result = pim_msdp_mg_mbr_add(pim, mg, mbr_ip); - - switch (result) { - case PIM_MSDP_ERR_NONE: - break; - case PIM_MSDP_ERR_OOM: - ret = NB_ERR; - snprintf(errmsg, errmsg_len, - "%% Out of memory"); - break; - case PIM_MSDP_ERR_MG_MBR_EXISTS: - ret = NB_ERR; - snprintf(errmsg, errmsg_len, - "%% mesh-group member exists"); - break; - case PIM_MSDP_ERR_MAX_MESH_GROUPS: - ret = NB_ERR; - snprintf(errmsg, errmsg_len, - "%% Only one mesh-group allowed currently"); - break; - default: - ret = NB_ERR; - snprintf(errmsg, errmsg_len, - "%% member add failed"); - } - - return ret; -} - -static int ip_no_msdp_mesh_group_member_cmd_worker(struct pim_instance *pim, - const char *mg, - struct in_addr mbr_ip, - char *errmsg, - size_t errmsg_len) -{ - enum pim_msdp_err result; - - result = pim_msdp_mg_mbr_del(pim, mg, mbr_ip); - - switch (result) { - case PIM_MSDP_ERR_NONE: - break; - case PIM_MSDP_ERR_NO_MG: - snprintf(errmsg, errmsg_len, - "%% mesh-group does not exist"); - break; - case PIM_MSDP_ERR_NO_MG_MBR: - snprintf(errmsg, errmsg_len, - "%% mesh-group member does not exist"); - break; - default: - snprintf(errmsg, errmsg_len, - "%% mesh-group member del failed"); - } - - return result ? NB_ERR : NB_OK; -} - -static int ip_msdp_mesh_group_source_cmd_worker(struct pim_instance *pim, - const char *mg, - struct in_addr src_ip, - char *errmsg, size_t errmsg_len) -{ - enum pim_msdp_err result; - - result = pim_msdp_mg_src_add(pim, mg, src_ip); - - switch (result) { - case PIM_MSDP_ERR_NONE: - break; - case PIM_MSDP_ERR_OOM: - snprintf(errmsg, errmsg_len, - "%% Out of memory"); - break; - case PIM_MSDP_ERR_MAX_MESH_GROUPS: - snprintf(errmsg, errmsg_len, - "%% Only one mesh-group allowed currently"); - break; - default: - snprintf(errmsg, errmsg_len, - "%% source add failed"); - } - - return result ? NB_ERR : NB_OK; -} - -static int ip_no_msdp_mesh_group_source_cmd_worker(struct pim_instance *pim, - const char *mg, - char *errmsg, - size_t errmsg_len) -{ - enum pim_msdp_err result; - - result = pim_msdp_mg_src_del(pim, mg); - - switch (result) { - case PIM_MSDP_ERR_NONE: - break; - case PIM_MSDP_ERR_NO_MG: - snprintf(errmsg, errmsg_len, - "%% mesh-group does not exist"); - break; - default: - snprintf(errmsg, errmsg_len, - "%% mesh-group source del failed"); - } - - return result ? NB_ERR : NB_OK; -} - static int ip_msdp_peer_cmd_worker(struct pim_instance *pim, struct in_addr peer_addr, struct in_addr local_addr, @@ -1146,29 +1005,13 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_ss } /* - * XPath: /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-mesh-group + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-mesh-groups */ -int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_create( - struct nb_cb_create_args *args) -{ - switch (args->event) { - case NB_EV_VALIDATE: - case NB_EV_PREPARE: - case NB_EV_ABORT: - case NB_EV_APPLY: - break; - } - - return NB_OK; -} - -int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_destroy( - struct nb_cb_destroy_args *args) +int pim_msdp_mesh_group_create(struct nb_cb_create_args *args) { + struct pim_msdp_mg *mg; struct vrf *vrf; - struct pim_instance *pim; - const char *mesh_group_name; - int result; switch (args->event) { case NB_EV_VALIDATE: @@ -1177,67 +1020,29 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_ms break; case NB_EV_APPLY: vrf = nb_running_get_entry(args->dnode, NULL, true); - pim = vrf->info; - mesh_group_name = yang_dnode_get_string(args->dnode, "mesh-group-name"); - - result = ip_no_msdp_mesh_group_cmd_worker(pim, mesh_group_name, - args->errmsg, - args->errmsg_len); - - if (result != PIM_MSDP_ERR_NONE) - return NB_ERR_INCONSISTENCY; - + mg = pim_msdp_mg_new(vrf->info, yang_dnode_get_string( + args->dnode, "./name")); + nb_running_set_entry(args->dnode, mg); break; } return NB_OK; } -/* - * XPath: /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-mesh-group/mesh-group-name - */ -int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_mesh_group_name_modify( - struct nb_cb_modify_args *args) +int pim_msdp_mesh_group_destroy(struct nb_cb_destroy_args *args) { - const char *mesh_group_name; - const char *mesh_group_name_old; - char xpath[XPATH_MAXLEN]; + struct pim_msdp_mg *mg; + struct vrf *vrf; switch (args->event) { case NB_EV_VALIDATE: - mesh_group_name = yang_dnode_get_string(args->dnode, "."); - yang_dnode_get_path(args->dnode, xpath, sizeof(xpath)); - - if (yang_dnode_exists(running_config->dnode, xpath) == false) - break; - - mesh_group_name_old = yang_dnode_get_string( - running_config->dnode, - xpath); - if (strcmp(mesh_group_name, mesh_group_name_old)) { - /* currently only one mesh-group can exist at a time */ - snprintf(args->errmsg, args->errmsg_len, - "Only one mesh-group allowed currently"); - return NB_ERR_VALIDATION; - } - break; case NB_EV_PREPARE: case NB_EV_ABORT: - case NB_EV_APPLY: break; - } - - return NB_OK; -} - -int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_mesh_group_name_destroy( - struct nb_cb_destroy_args *args) -{ - switch (args->event) { - case NB_EV_VALIDATE: - case NB_EV_PREPARE: - case NB_EV_ABORT: case NB_EV_APPLY: + mg = nb_running_unset_entry(args->dnode); + vrf = nb_running_get_entry(args->dnode, NULL, true); + pim_msdp_mg_free(vrf->info, &mg); break; } @@ -1245,16 +1050,15 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_ms } /* - * XPath: /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-mesh-group/member-ip + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-mesh-groups/source */ -int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_member_ip_create( - struct nb_cb_create_args *args) +int pim_msdp_mesh_group_source_modify(struct nb_cb_modify_args *args) { + const struct lyd_node *vrf_dnode; + struct pim_msdp_mg *mg; struct vrf *vrf; - struct pim_instance *pim; - const char *mesh_group_name; - struct ipaddr mbr_ip; - enum pim_msdp_err result; + struct ipaddr ip; switch (args->event) { case NB_EV_VALIDATE: @@ -1262,33 +1066,24 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_ms case NB_EV_ABORT: break; case NB_EV_APPLY: - vrf = nb_running_get_entry(args->dnode, NULL, true); - pim = vrf->info; - mesh_group_name = yang_dnode_get_string(args->dnode, - "../mesh-group-name"); - yang_dnode_get_ip(&mbr_ip, args->dnode, NULL); - - result = ip_msdp_mesh_group_member_cmd_worker( - pim, mesh_group_name, mbr_ip.ip._v4_addr, - args->errmsg, args->errmsg_len); - - if (result != PIM_MSDP_ERR_NONE) - return NB_ERR_INCONSISTENCY; + mg = nb_running_get_entry(args->dnode, NULL, true); + vrf_dnode = + yang_dnode_get_parent(args->dnode, "address-family"); + vrf = nb_running_get_entry(vrf_dnode, "../../", true); + yang_dnode_get_ip(&ip, args->dnode, NULL); + pim_msdp_mg_src_add(vrf->info, mg, &ip.ip._v4_addr); break; } - return NB_OK; } -int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_member_ip_destroy( - struct nb_cb_destroy_args *args) +int pim_msdp_mesh_group_source_destroy(struct nb_cb_destroy_args *args) { + const struct lyd_node *vrf_dnode; + struct pim_msdp_mg *mg; struct vrf *vrf; - struct pim_instance *pim; - const char *mesh_group_name; - struct ipaddr mbr_ip; - enum pim_msdp_err result; + struct in_addr addr; switch (args->event) { case NB_EV_VALIDATE: @@ -1296,36 +1091,30 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_ms case NB_EV_ABORT: break; case NB_EV_APPLY: - vrf = nb_running_get_entry(args->dnode, NULL, true); - pim = vrf->info; - mesh_group_name = yang_dnode_get_string(args->dnode, - "../mesh-group-name"); - yang_dnode_get_ip(&mbr_ip, args->dnode, NULL); - - result = ip_no_msdp_mesh_group_member_cmd_worker( - pim, mesh_group_name, mbr_ip.ip._v4_addr, - args->errmsg, args->errmsg_len); - - if (result != PIM_MSDP_ERR_NONE) - return NB_ERR_INCONSISTENCY; + mg = nb_running_get_entry(args->dnode, NULL, true); + vrf_dnode = + yang_dnode_get_parent(args->dnode, "address-family"); + vrf = nb_running_get_entry(vrf_dnode, "../../", true); + addr.s_addr = INADDR_ANY; + pim_msdp_mg_src_add(vrf->info, mg, &addr); break; } - return NB_OK; } + /* - * XPath: /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-mesh-group/source-ip + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-mesh-groups/members */ -int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_source_ip_modify( - struct nb_cb_modify_args *args) +int pim_msdp_mesh_group_members_create(struct nb_cb_create_args *args) { + const struct lyd_node *vrf_dnode; + struct pim_msdp_mg_mbr *mbr; + struct pim_msdp_mg *mg; struct vrf *vrf; - struct pim_instance *pim; - const char *mesh_group_name; - struct ipaddr src_ip; - enum pim_msdp_err result; + struct ipaddr ip; switch (args->event) { case NB_EV_VALIDATE: @@ -1333,31 +1122,24 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_ms case NB_EV_ABORT: break; case NB_EV_APPLY: - vrf = nb_running_get_entry(args->dnode, NULL, true); - pim = vrf->info; - mesh_group_name = yang_dnode_get_string(args->dnode, - "../mesh-group-name"); - yang_dnode_get_ip(&src_ip, args->dnode, NULL); - - result = ip_msdp_mesh_group_source_cmd_worker( - pim, mesh_group_name, src_ip.ip._v4_addr, - args->errmsg, args->errmsg_len); - - if (result != PIM_MSDP_ERR_NONE) - return NB_ERR_INCONSISTENCY; + mg = nb_running_get_entry(args->dnode, NULL, true); + vrf_dnode = + yang_dnode_get_parent(args->dnode, "address-family"); + vrf = nb_running_get_entry(vrf_dnode, "../../", true); + yang_dnode_get_ip(&ip, args->dnode, "address"); + mbr = pim_msdp_mg_mbr_add(vrf->info, mg, &ip.ip._v4_addr); + nb_running_set_entry(args->dnode, mbr); break; } + return NB_OK; } -int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_source_ip_destroy( - struct nb_cb_destroy_args *args) +int pim_msdp_mesh_group_members_destroy(struct nb_cb_destroy_args *args) { - struct vrf *vrf; - struct pim_instance *pim; - const char *mesh_group_name; - enum pim_msdp_err result; + struct pim_msdp_mg_mbr *mbr; + struct pim_msdp_mg *mg; switch (args->event) { case NB_EV_VALIDATE: @@ -1365,20 +1147,13 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_ms case NB_EV_ABORT: break; case NB_EV_APPLY: - vrf = nb_running_get_entry(args->dnode, NULL, true); - pim = vrf->info; - mesh_group_name = yang_dnode_get_string(args->dnode, - "../mesh-group-name"); - - result = ip_no_msdp_mesh_group_source_cmd_worker( - pim, mesh_group_name, args->errmsg, - args->errmsg_len); - - if (result != PIM_MSDP_ERR_NONE) - return NB_ERR_INCONSISTENCY; + mbr = nb_running_get_entry(args->dnode, NULL, true); + mg = nb_running_get_entry(args->dnode, "../", true); + pim_msdp_mg_mbr_del(mg, mbr); break; } + return NB_OK; } diff --git a/ripd/ripd.c b/ripd/ripd.c index c6c82fb65a..7d940efd9c 100644 --- a/ripd/ripd.c +++ b/ripd/ripd.c @@ -3690,6 +3690,8 @@ void rip_vrf_init(void) { vrf_init(rip_vrf_new, rip_vrf_enable, rip_vrf_disable, rip_vrf_delete, rip_vrf_enable); + + vrf_cmd_init(NULL, &ripd_privs); } void rip_vrf_terminate(void) diff --git a/ripngd/ripngd.c b/ripngd/ripngd.c index 3b8d2076f3..a0ea18f3e9 100644 --- a/ripngd/ripngd.c +++ b/ripngd/ripngd.c @@ -2691,6 +2691,8 @@ void ripng_vrf_init(void) { vrf_init(ripng_vrf_new, ripng_vrf_enable, ripng_vrf_disable, ripng_vrf_delete, ripng_vrf_enable); + + vrf_cmd_init(NULL, &ripngd_privs); } void ripng_vrf_terminate(void) diff --git a/tests/isisd/test_common.c b/tests/isisd/test_common.c index 5b2028ffd4..ade3573535 100644 --- a/tests/isisd/test_common.c +++ b/tests/isisd/test_common.c @@ -309,7 +309,8 @@ static int topology_load_node(const struct isis_topology *topology, { int ret; - isis_dynhn_insert(tnode->sysid, tnode->hostname, tnode->level); + isis_dynhn_insert(area->isis, tnode->sysid, tnode->hostname, + tnode->level); for (int level = IS_LEVEL_1; level <= IS_LEVEL_2; level++) { if ((tnode->level & level) == 0) diff --git a/tests/isisd/test_isis_spf.c b/tests/isisd/test_isis_spf.c index b89a5a008e..a30f33ccad 100644 --- a/tests/isisd/test_isis_spf.c +++ b/tests/isisd/test_isis_spf.c @@ -269,7 +269,7 @@ static int test_run(struct vty *vty, const struct isis_topology *topology, if (sysid2buff(fail_id, fail_sysid_str) == 0) { struct isis_dynhn *dynhn; - dynhn = dynhn_find_by_name(fail_sysid_str); + dynhn = dynhn_find_by_name(area->isis, fail_sysid_str); if (dynhn == NULL) { vty_out(vty, "Invalid system id %s\n", fail_sysid_str); @@ -339,9 +339,6 @@ static int test_run(struct vty *vty, const struct isis_topology *topology, /* Cleanup IS-IS area. */ isis_area_destroy(area); - /* Cleanup hostnames. */ - dyn_cache_cleanup_all(); - return CMD_SUCCESS; } diff --git a/tests/topotests/all_protocol_startup/test_all_protocol_startup.py b/tests/topotests/all_protocol_startup/test_all_protocol_startup.py index 597e230696..2d75428f1a 100644 --- a/tests/topotests/all_protocol_startup/test_all_protocol_startup.py +++ b/tests/topotests/all_protocol_startup/test_all_protocol_startup.py @@ -906,17 +906,25 @@ def test_bgp_summary(): # Read expected result from file expected_original = open(refTableFile).read().rstrip() - for filter in ["", "remote-as internal", "remote-as external", + for arguments in ["", "remote-as internal", "remote-as external", "remote-as 100", "remote-as 123", "neighbor 192.168.7.10", "neighbor 192.168.7.10", "neighbor fc00:0:0:8::1000", - "neighbor 10.0.0.1"]: + "neighbor 10.0.0.1", + "terse", + "remote-as internal terse", + "remote-as external terse", + "remote-as 100 terse", "remote-as 123 terse", + "neighbor 192.168.7.10 terse", "neighbor 192.168.7.10 terse", + "neighbor fc00:0:0:8::1000 terse", + "neighbor 10.0.0.1 terse"]: # Actual output from router actual = ( net["r%s" % i] - .cmd('vtysh -c "show ip bgp summary ' + filter + '" 2> /dev/null') + .cmd('vtysh -c "show ip bgp summary ' + arguments + '" 2> /dev/null') .rstrip() ) + # Mask out "using XXiXX bytes" portion. They are random... actual = re.sub(r"using [0-9]+ bytes", "using XXXX bytes", actual) # Mask out "using XiXXX KiB" portion. They are random... @@ -928,48 +936,55 @@ def test_bgp_summary(): actual = re.sub(r"Total number.*", "", actual) actual = re.sub(r"Displayed.*", "", actual) # Remove IPv4 Unicast Summary (Title only) - actual = re.sub(r"IPv4 Unicast Summary:", "", actual) + actual = re.sub(r"IPv4 Unicast Summary \(VRF default\):", "", actual) # Remove IPv4 Multicast Summary (all of it) - actual = re.sub(r"IPv4 Multicast Summary:", "", actual) + actual = re.sub(r"IPv4 Multicast Summary \(VRF default\):", "", actual) actual = re.sub(r"No IPv4 Multicast neighbor is configured", "", actual) # Remove IPv4 VPN Summary (all of it) - actual = re.sub(r"IPv4 VPN Summary:", "", actual) + actual = re.sub(r"IPv4 VPN Summary \(VRF default\):", "", actual) actual = re.sub(r"No IPv4 VPN neighbor is configured", "", actual) # Remove IPv4 Encap Summary (all of it) - actual = re.sub(r"IPv4 Encap Summary:", "", actual) + actual = re.sub(r"IPv4 Encap Summary \(VRF default\):", "", actual) actual = re.sub(r"No IPv4 Encap neighbor is configured", "", actual) # Remove Unknown Summary (all of it) - actual = re.sub(r"Unknown Summary:", "", actual) + actual = re.sub(r"Unknown Summary \(VRF default\):", "", actual) actual = re.sub(r"No Unknown neighbor is configured", "", actual) - actual = re.sub(r"IPv4 labeled-unicast Summary:", "", actual) + actual = re.sub(r"IPv4 labeled-unicast Summary \(VRF default\):", "", actual) actual = re.sub( r"No IPv4 labeled-unicast neighbor is configured", "", actual ) expected = expected_original - # apply filters on expected output - if "internal" in filter or "remote-as 100" in filter: + # apply argumentss on expected output + if "internal" in arguments or "remote-as 100" in arguments: expected = re.sub(r".+\s+200\s+.+", "", expected) - elif "external" in filter: + elif "external" in arguments: expected = re.sub(r".+\s+100\s+.+Active.+", "", expected) - elif "remote-as 123" in filter: + elif "remote-as 123" in arguments: expected = re.sub( r"(192.168.7.(1|2)0|fc00:0:0:8::(1|2)000).+Active.+", "", expected ) - elif "192.168.7.10" in filter: + expected = re.sub(r"\nNeighbor.+Desc", "", expected) + expected = expected + "% No matching neighbor\n" + elif "192.168.7.10" in arguments: expected = re.sub( r"(192.168.7.20|fc00:0:0:8::(1|2)000).+Active.+", "", expected ) - elif "fc00:0:0:8::1000" in filter: + elif "fc00:0:0:8::1000" in arguments: expected = re.sub( r"(192.168.7.(1|2)0|fc00:0:0:8::2000).+Active.+", "", expected ) - elif "10.0.0.1" in filter: - expected = "No such neighbor in this view/vrf" + elif "10.0.0.1" in arguments: + expected = "No such neighbor in VRF default" + + if "terse" in arguments: + expected = re.sub(r"BGP table version .+", "", expected) + expected = re.sub(r"RIB entries .+", "", expected) + expected = re.sub(r"Peers [0-9]+, using .+", "", expected) # Strip empty lines actual = actual.lstrip().rstrip() @@ -978,13 +993,17 @@ def test_bgp_summary(): expected = re.sub(r"\n+", "\n", expected) # reapply initial formatting - actual = re.sub(r"KiB of memory\n", "KiB of memory\n\n", actual) - expected = re.sub(r"KiB of memory\n", "KiB of memory\n\n", expected) + if "terse" in arguments: + actual = re.sub(r" vrf-id 0\n", " vrf-id 0\n\n", actual) + expected = re.sub(r" vrf-id 0\n", " vrf-id 0\n\n", expected) + else: + actual = re.sub(r"KiB of memory\n", "KiB of memory\n\n", actual) + expected = re.sub(r"KiB of memory\n", "KiB of memory\n\n", expected) # realign expected neighbor columns if needed try: - idx_actual = re.search(r"\n(Neighbor\s+V\s+)", actual).group(1).find("V") - idx_expected = re.search(r"\n(Neighbor\s+V\s+)", expected).group(1).find("V") + idx_actual = re.search(r"(Neighbor\s+V\s+)", actual).group(1).find("V") + idx_expected = re.search(r"(Neighbor\s+V\s+)", expected).group(1).find("V") idx_diff = idx_expected - idx_actual if idx_diff > 0: # Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd @@ -1002,8 +1021,8 @@ def test_bgp_summary(): diff = topotest.get_textdiff( actual, expected, - title1="actual SHOW IP BGP SUMMARY " + filter.upper() , - title2="expected SHOW IP BGP SUMMARY " + filter.upper(), + title1="actual SHOW IP BGP SUMMARY " + arguments.upper() , + title2="expected SHOW IP BGP SUMMARY " + arguments.upper(), ) # Empty string if it matches, otherwise diff contains unified diff @@ -1020,13 +1039,6 @@ def test_bgp_summary(): diff, ) - # Actual output from router - actual = ( - net["r%s" % i] - .cmd('vtysh -c "show ip bgp summary" 2> /dev/null') - .rstrip() - ) - # Make sure that all daemons are running for i in range(1, 2): fatal_error = net["r%s" % i].checkRouterRunning() @@ -1074,22 +1086,22 @@ def test_bgp_ipv6_summary(): actual = re.sub(r"Total number.*", "", actual) actual = re.sub(r"Displayed.*", "", actual) # Remove IPv4 Unicast Summary (Title only) - actual = re.sub(r"IPv6 Unicast Summary:", "", actual) + actual = re.sub(r"IPv6 Unicast Summary \(VRF default\):", "", actual) # Remove IPv4 Multicast Summary (all of it) - actual = re.sub(r"IPv6 Multicast Summary:", "", actual) + actual = re.sub(r"IPv6 Multicast Summary \(VRF default\):", "", actual) actual = re.sub(r"No IPv6 Multicast neighbor is configured", "", actual) # Remove IPv4 VPN Summary (all of it) - actual = re.sub(r"IPv6 VPN Summary:", "", actual) + actual = re.sub(r"IPv6 VPN Summary \(VRF default\):", "", actual) actual = re.sub(r"No IPv6 VPN neighbor is configured", "", actual) # Remove IPv4 Encap Summary (all of it) - actual = re.sub(r"IPv6 Encap Summary:", "", actual) + actual = re.sub(r"IPv6 Encap Summary \(VRF default\):", "", actual) actual = re.sub(r"No IPv6 Encap neighbor is configured", "", actual) # Remove Unknown Summary (all of it) - actual = re.sub(r"Unknown Summary:", "", actual) + actual = re.sub(r"Unknown Summary \(VRF default\):", "", actual) actual = re.sub(r"No Unknown neighbor is configured", "", actual) # Remove Labeled Unicast Summary (all of it) - actual = re.sub(r"IPv6 labeled-unicast Summary:", "", actual) + actual = re.sub(r"IPv6 labeled-unicast Summary \(VRF default\):", "", actual) actual = re.sub( r"No IPv6 labeled-unicast neighbor is configured", "", actual ) diff --git a/tests/topotests/bfd_topo2/r4/ipv6_routes.json b/tests/topotests/bfd_topo2/r4/ipv6_routes.json index c828575c84..eb571d5d1c 100644 --- a/tests/topotests/bfd_topo2/r4/ipv6_routes.json +++ b/tests/topotests/bfd_topo2/r4/ipv6_routes.json @@ -35,7 +35,7 @@ { "distance": 110, "protocol": "ospf6", - "metric": 10, + "metric": 20, "selected": true, "installed": true, "prefix": "2001:db8:1::/64", diff --git a/tests/topotests/conftest.py b/tests/topotests/conftest.py index de5c584e91..e57db7471c 100755 --- a/tests/topotests/conftest.py +++ b/tests/topotests/conftest.py @@ -2,8 +2,10 @@ Topotest conftest.py file. """ +import glob import os import pdb +import re import pytest from lib.topogen import get_topogen, diagnose_env @@ -11,6 +13,12 @@ from lib.topotest import json_cmp_result from lib.topotest import g_extra_config as topotest_extra_config from lib.topolog import logger +try: + from _pytest._code.code import ExceptionInfo + leak_check_ok = True +except ImportError: + leak_check_ok = False + def pytest_addoption(parser): """ @@ -67,6 +75,18 @@ def pytest_addoption(parser): ) parser.addoption( + "--valgrind-extra", + action="store_true", + help="Generate suppression file, and enable more precise (slower) valgrind checks", + ) + + parser.addoption( + "--valgrind-memleaks", + action="store_true", + help="Run all daemons under valgrind for memleak detection", + ) + + parser.addoption( "--vtysh", metavar="ROUTER[,ROUTER...]", help="Comma-separated list of routers to spawn vtysh on, or 'all'", @@ -79,6 +99,37 @@ def pytest_addoption(parser): ) +def check_for_memleaks(): + if not topotest_extra_config["valgrind_memleaks"]: + return + + leaks = [] + tgen = get_topogen() + latest = [] + existing = [] + if tgen is not None: + logdir = "/tmp/topotests/{}".format(tgen.modname) + if hasattr(tgen, "valgrind_existing_files"): + existing = tgen.valgrind_existing_files + latest = glob.glob(os.path.join(logdir, "*.valgrind.*")) + + for vfile in latest: + if vfile in existing: + continue + with open(vfile) as vf: + vfcontent = vf.read() + match = re.search(r"ERROR SUMMARY: (\d+) errors", vfcontent) + if match and match.group(1) != "0": + emsg = '{} in {}'.format(match.group(1), vfile) + leaks.append(emsg) + + if leaks: + if leak_check_ok: + pytest.fail("Memleaks found:\n\t" + "\n\t".join(leaks)) + else: + logger.error("Memleaks found:\n\t" + "\n\t".join(leaks)) + + def pytest_runtest_call(): """ This function must be run after setup_module(), it does standarized post @@ -139,6 +190,9 @@ def pytest_configure(config): shell_on_error = config.getoption("--shell-on-error") topotest_extra_config["shell_on_error"] = shell_on_error + topotest_extra_config["valgrind_extra"] = config.getoption("--valgrind-extra") + topotest_extra_config["valgrind_memleaks"] = config.getoption("--valgrind-memleaks") + vtysh = config.getoption("--vtysh") topotest_extra_config["vtysh"] = vtysh.split(",") if vtysh else [] @@ -159,6 +213,12 @@ def pytest_runtest_makereport(item, call): else: pause = False + if call.excinfo is None and call.when == "call": + try: + check_for_memleaks() + except: + call.excinfo = ExceptionInfo() + if call.excinfo is None: error = False else: diff --git a/tests/topotests/lib/bgp.py b/tests/topotests/lib/bgp.py index 50cb586acd..db7b3586f1 100644 --- a/tests/topotests/lib/bgp.py +++ b/tests/topotests/lib/bgp.py @@ -990,7 +990,7 @@ def modify_bgp_config_when_bgpd_down(tgen, topo, input_dict): # Verification APIs ############################################# @retry(attempts=4, wait=2, return_is_str=True) -def verify_router_id(tgen, topo, input_dict): +def verify_router_id(tgen, topo, input_dict, expected=True): """ Running command "show ip bgp json" for DUT and reading router-id from input_dict and verifying with command output. @@ -1006,6 +1006,8 @@ def verify_router_id(tgen, topo, input_dict): * `topo`: input json file data * `input_dict`: input dictionary, have details of Device Under Test, for which user wants to test the data + * `expected` : expected results from API, by-default True + Usage ----- # Verify if router-id for r1 is 12.12.12.12 @@ -1060,7 +1062,7 @@ def verify_router_id(tgen, topo, input_dict): @retry(attempts=50, wait=3, return_is_str=True) -def verify_bgp_convergence(tgen, topo, dut=None): +def verify_bgp_convergence(tgen, topo, dut=None, expected=True): """ API will verify if BGP is converged with in the given time frame. Running "show bgp summary json" command and verify bgp neighbor @@ -1070,6 +1072,8 @@ def verify_bgp_convergence(tgen, topo, dut=None): * `tgen`: topogen object * `topo`: input json file data * `dut`: device under test + * `expected` : expected results from API, by-default True + Usage ----- # To veriry is BGP is converged for all the routers used in @@ -1264,7 +1268,7 @@ def verify_bgp_convergence(tgen, topo, dut=None): @retry(attempts=4, wait=4, return_is_str=True) def verify_bgp_community( - tgen, addr_type, router, network, input_dict=None, vrf=None, bestpath=False + tgen, addr_type, router, network, input_dict=None, vrf=None, bestpath=False, expected=True ): """ API to veiryf BGP large community is attached in route for any given @@ -1280,6 +1284,7 @@ def verify_bgp_community( values needs to be verified * `vrf`: VRF name * `bestpath`: To check best path cli + * `expected` : expected results from API, by-default True Usage ----- @@ -1423,7 +1428,7 @@ def modify_as_number(tgen, topo, input_dict): @retry(attempts=4, wait=2, return_is_str=True) -def verify_as_numbers(tgen, topo, input_dict): +def verify_as_numbers(tgen, topo, input_dict, expected=True): """ This API is to verify AS numbers for given DUT by running "show ip bgp neighbor json" command. Local AS and Remote AS @@ -1435,6 +1440,7 @@ def verify_as_numbers(tgen, topo, input_dict): * `topo`: input json file data * `addr_type` : ip type, ipv4/ipv6 * `input_dict`: defines - for which router, AS numbers needs to be verified + * `expected` : expected results from API, by-default True Usage ----- @@ -1522,7 +1528,7 @@ def verify_as_numbers(tgen, topo, input_dict): @retry(attempts=50, wait=3, return_is_str=True) -def verify_bgp_convergence_from_running_config(tgen, dut=None): +def verify_bgp_convergence_from_running_config(tgen, dut=None, expected=True): """ API to verify BGP convergence b/w loopback and physical interface. This API would be used when routers have BGP neighborship is loopback @@ -1532,6 +1538,7 @@ def verify_bgp_convergence_from_running_config(tgen, dut=None): ---------- * `tgen`: topogen object * `dut`: device under test + * `expected` : expected results from API, by-default True Usage ----- @@ -2086,6 +2093,7 @@ def verify_bgp_attributes( input_dict=None, seq_id=None, nexthop=None, + expected=True ): """ API will verify BGP attributes set by Route-map for given prefix and @@ -2101,6 +2109,7 @@ def verify_bgp_attributes( * `rmap_name`: route map name for which set criteria needs to be verified * `input_dict`: defines for which router, AS numbers needs * `seq_id`: sequence number of rmap, default is None + * `expected` : expected results from API, by-default True Usage ----- @@ -2216,7 +2225,7 @@ def verify_bgp_attributes( @retry(attempts=4, wait=2, return_is_str=True) def verify_best_path_as_per_bgp_attribute( - tgen, addr_type, router, input_dict, attribute + tgen, addr_type, router, input_dict, attribute, expected=True ): """ API is to verify best path according to BGP attributes for given routes. @@ -2231,6 +2240,8 @@ def verify_best_path_as_per_bgp_attribute( * `attribute` : calculate best path using this attribute * `input_dict`: defines different routes to calculate for which route best path is selected + * `expected` : expected results from API, by-default True + Usage ----- # To verify best path for routes 200.50.2.0/32 and 200.60.2.0/32 from @@ -2420,7 +2431,7 @@ def verify_best_path_as_per_bgp_attribute( @retry(attempts=5, wait=2, return_is_str=True) def verify_best_path_as_per_admin_distance( - tgen, addr_type, router, input_dict, attribute + tgen, addr_type, router, input_dict, attribute, expected=True ): """ API is to verify best path according to admin distance for given @@ -2435,6 +2446,8 @@ def verify_best_path_as_per_admin_distance( * `attribute` : calculate best path using admin distance * `input_dict`: defines different routes with different admin distance to calculate for which route best path is selected + * `expected` : expected results from API, by-default True + Usage ----- # To verify best path for route 200.50.2.0/32 from router r2 to @@ -2532,7 +2545,7 @@ def verify_best_path_as_per_admin_distance( @retry(attempts=5, wait=2, return_is_str=True, initial_wait=2) def verify_bgp_rib( - tgen, addr_type, dut, input_dict, next_hop=None, aspath=None, multi_nh=None + tgen, addr_type, dut, input_dict, next_hop=None, aspath=None, multi_nh=None, expected=True ): """ This API is to verify whether bgp rib has any @@ -2547,6 +2560,7 @@ def verify_bgp_rib( * `next_hop`[optional]: next_hop which needs to be verified, default = static * 'aspath'[optional]: aspath which needs to be verified + * `expected` : expected results from API, by-default True Usage ----- @@ -2833,7 +2847,7 @@ def verify_bgp_rib( @retry(attempts=5, wait=2, return_is_str=True) -def verify_graceful_restart(tgen, topo, addr_type, input_dict, dut, peer): +def verify_graceful_restart(tgen, topo, addr_type, input_dict, dut, peer, expected=True): """ This API is to verify verify_graceful_restart configuration of DUT and cross verify the same from the peer bgp routerrouter. @@ -2847,6 +2861,7 @@ def verify_graceful_restart(tgen, topo, addr_type, input_dict, dut, peer): which user wants to test the data * `dut`: input dut router name * `peer`: input peer router name + * `expected` : expected results from API, by-default True Usage ----- @@ -3082,7 +3097,7 @@ def verify_graceful_restart(tgen, topo, addr_type, input_dict, dut, peer): @retry(attempts=5, wait=2, return_is_str=True) -def verify_r_bit(tgen, topo, addr_type, input_dict, dut, peer): +def verify_r_bit(tgen, topo, addr_type, input_dict, dut, peer, expected=True): """ This API is to verify r_bit in the BGP gr capability advertised by the neighbor router @@ -3096,6 +3111,8 @@ def verify_r_bit(tgen, topo, addr_type, input_dict, dut, peer): which user wants to test the data * `dut`: input dut router name * `peer`: peer name + * `expected` : expected results from API, by-default True + Usage ----- input_dict = { @@ -3200,7 +3217,7 @@ def verify_r_bit(tgen, topo, addr_type, input_dict, dut, peer): @retry(attempts=5, wait=2, return_is_str=True) -def verify_eor(tgen, topo, addr_type, input_dict, dut, peer): +def verify_eor(tgen, topo, addr_type, input_dict, dut, peer, expected=True): """ This API is to verify EOR @@ -3363,7 +3380,7 @@ def verify_eor(tgen, topo, addr_type, input_dict, dut, peer): @retry(attempts=4, wait=2, return_is_str=True) -def verify_f_bit(tgen, topo, addr_type, input_dict, dut, peer): +def verify_f_bit(tgen, topo, addr_type, input_dict, dut, peer, expected=True): """ This API is to verify f_bit in the BGP gr capability advertised by the neighbor router @@ -3377,6 +3394,7 @@ def verify_f_bit(tgen, topo, addr_type, input_dict, dut, peer): which user wants to test the data * `dut`: input dut router name * `peer`: peer name + * `expected` : expected results from API, by-default True Usage ----- @@ -3516,6 +3534,8 @@ def verify_graceful_restart_timers(tgen, topo, addr_type, input_dict, dut, peer) for which user wants to test the data * `dut`: input dut router name * `peer`: peer name + * `expected` : expected results from API, by-default True + Usage ----- # Configure graceful-restart @@ -3629,7 +3649,7 @@ def verify_graceful_restart_timers(tgen, topo, addr_type, input_dict, dut, peer) @retry(attempts=4, wait=2, return_is_str=True) -def verify_gr_address_family(tgen, topo, addr_type, addr_family, dut): +def verify_gr_address_family(tgen, topo, addr_type, addr_family, dut, expected=True): """ This API is to verify gr_address_family in the BGP gr capability advertised by the neighbor router @@ -3641,6 +3661,7 @@ def verify_gr_address_family(tgen, topo, addr_type, addr_family, dut): * `addr_type` : ip type ipv4/ipv6 * `addr_type` : ip type IPV4 Unicast/IPV6 Unicast * `dut`: input dut router name + * `expected` : expected results from API, by-default True Usage ----- @@ -3730,6 +3751,7 @@ def verify_attributes_for_evpn_routes( ipLen=None, rd_peer=None, rt_peer=None, + expected=True ): """ API to verify rd and rt value using "sh bgp l2vpn evpn 10.1.1.1" @@ -3747,6 +3769,8 @@ def verify_attributes_for_evpn_routes( * `ipLen` : IP prefix length * `rd_peer` : Peer name from which RD will be auto-generated * `rt_peer` : Peer name from which RT will be auto-generated + * `expected` : expected results from API, by-default True + Usage ----- input_dict_1 = { @@ -4117,7 +4141,7 @@ def verify_attributes_for_evpn_routes( @retry(attempts=5, wait=2, return_is_str=True) def verify_evpn_routes( - tgen, topo, dut, input_dict, routeType=5, EthTag=0, next_hop=None + tgen, topo, dut, input_dict, routeType=5, EthTag=0, next_hop=None, expected=True ): """ API to verify evpn routes using "sh bgp l2vpn evpn" @@ -4132,6 +4156,8 @@ def verify_evpn_routes( * `route_type` : Route type 5 is supported as of now * `EthTag` : Ethernet tag, by-default is 0 * `next_hop` : Prefered nexthop for the evpn routes + * `expected` : expected results from API, by-default True + Usage ----- input_dict_1 = { diff --git a/tests/topotests/lib/common_config.py b/tests/topotests/lib/common_config.py index ee7cd6a7af..3f78f020bc 100644 --- a/tests/topotests/lib/common_config.py +++ b/tests/topotests/lib/common_config.py @@ -123,6 +123,17 @@ DEBUG_LOGS = { "debug ospf te", "debug ospf zebra", ], + "ospf6": [ + "debug ospf6 event", + "debug ospf6 ism", + "debug ospf6 lsa", + "debug ospf6 nsm", + "debug ospf6 nssa", + "debug ospf6 packet all", + "debug ospf6 sr", + "debug ospf6 te", + "debug ospf6 zebra", + ], } if config.has_option("topogen", "verbosity"): @@ -422,7 +433,10 @@ def check_router_status(tgen): daemons.append("zebra") if "pimd" in result: daemons.append("pimd") - + if "ospfd" in result: + daemons.append("ospfd") + if "ospf6d" in result: + daemons.append("ospf6d") rnode.startDaemons(daemons) except Exception as e: @@ -890,6 +904,10 @@ def topo_daemons(tgen, topo): for val in topo["routers"][rtr]["links"].values(): if "pim" in val and "pimd" not in daemon_list: daemon_list.append("pimd") + if "ospf" in val and "ospfd" not in daemon_list: + daemon_list.append("ospfd") + if "ospf6" in val and "ospf6d" not in daemon_list: + daemon_list.append("ospf6d") break return daemon_list diff --git a/tests/topotests/lib/mcast-tester.py b/tests/topotests/lib/mcast-tester.py new file mode 100755 index 0000000000..07e4ab8773 --- /dev/null +++ b/tests/topotests/lib/mcast-tester.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 by +# Network Device Education Foundation, Inc. ("NetDEF") +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +# AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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. + +""" +Subscribe to a multicast group so that the kernel sends an IGMP JOIN +for the multicast group we subscribed to. +""" + +import argparse +import os +import json +import socket +import subprocess +import struct +import sys +import time + +# +# Functions +# +def interface_name_to_index(name): + "Gets the interface index using its name. Returns None on failure." + interfaces = json.loads( + subprocess.check_output('ip -j link show', shell=True)) + + for interface in interfaces: + if interface['ifname'] == name: + return interface['ifindex'] + + return None + + +def multicast_join(sock, ifindex, group, port): + "Joins a multicast group." + mreq = struct.pack( + "=4sLL", socket.inet_aton(args.group), socket.INADDR_ANY, ifindex + ) + + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + sock.bind((group, port)) + sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) + + +# +# Main code. +# +parser = argparse.ArgumentParser(description="Multicast RX utility") +parser.add_argument('socket', help='Point to topotest UNIX socket') +parser.add_argument('group', help='Multicast IP') +parser.add_argument('interface', help='Interface name') +parser.add_argument( + '--send', + help='Transmit instead of join with interval (defaults to 0.7 sec)', + type=float, default=0) +args = parser.parse_args() + +ttl = 16 +port = 1000 + +# Get interface index/validate. +ifindex = interface_name_to_index(args.interface) +if ifindex is None: + sys.stderr.write('Interface {} does not exists\n'.format(args.interface)) + sys.exit(1) + +# We need root privileges to set up multicast. +if os.geteuid() != 0: + sys.stderr.write("ERROR: You must have root privileges\n") + sys.exit(1) + +# Wait for topotest to synchronize with us. +toposock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM, 0) +while True: + try: + toposock.connect(args.socket) + break + except ConnectionRefusedError: + time.sleep(1) + continue + +msock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +if args.send > 0: + # Prepare multicast bit in that interface. + msock.setsockopt( + socket.SOL_SOCKET, 25, + struct.pack("%ds" % len(args.interface), + args.interface.encode('utf-8'))) + # Set packets TTL. + msock.setsockopt( + socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, struct.pack("b", ttl)) + # Block to ensure packet send. + msock.setblocking(True) + # Set topotest socket non blocking so we can multiplex the main loop. + toposock.setblocking(False) +else: + multicast_join(msock, ifindex, args.group, port) + +counter = 0 +while True: + if args.send > 0: + msock.sendto(b"test %d" % counter, (args.group, port)) + counter += 1 + time.sleep(args.send) + + try: + data = toposock.recv(1) + if data == b'': + print(' -> Connection closed') + break + except BlockingIOError: + continue + +msock.close() + +sys.exit(0) diff --git a/tests/topotests/lib/ospf.py b/tests/topotests/lib/ospf.py index 7ad64de4a1..3f39b93d8c 100644 --- a/tests/topotests/lib/ospf.py +++ b/tests/topotests/lib/ospf.py @@ -18,13 +18,16 @@ # OF THIS SOFTWARE. # -from copy import deepcopy import traceback +import ipaddr +import ipaddress +import sys + +from copy import deepcopy from time import sleep from lib.topolog import logger -import ipaddr from lib.topotest import frr_unicode - +from ipaddress import IPv6Address # Import common_config to use commomnly used APIs from lib.common_config import ( create_common_configuration, @@ -86,10 +89,21 @@ def create_router_ospf(tgen, topo, input_dict=None, build=False, load_config=Tru logger.debug("Router %s: 'ospf' not present in input_dict", router) continue - result = __create_ospf_global(tgen, input_dict, router, build, load_config) + result = __create_ospf_global( + tgen, input_dict, router, build, load_config) if result is True: ospf_data = input_dict[router]["ospf"] + for router in input_dict.keys(): + if "ospf6" not in input_dict[router]: + logger.debug("Router %s: 'ospf6' not present in input_dict", router) + continue + + result = __create_ospf_global( + tgen, input_dict, router, build, load_config, ospf='ospf6') + if result is True: + ospf_data = input_dict[router]["ospf6"] + logger.debug("Exiting lib API: create_router_ospf()") return result @@ -158,6 +172,7 @@ def __create_ospf_global( config_data.append(cmd) + # router id router_id = ospf_data.setdefault("router_id", None) del_router_id = ospf_data.setdefault("del_router_id", False) @@ -166,6 +181,33 @@ def __create_ospf_global( if router_id: config_data.append("{} router-id {}".format(ospf, router_id)) + # log-adjacency-changes + log_adj_changes = ospf_data.setdefault("log_adj_changes", None) + del_log_adj_changes = ospf_data.setdefault("del_log_adj_changes", False) + if del_log_adj_changes: + config_data.append("no log-adjacency-changes detail") + if log_adj_changes: + config_data.append("log-adjacency-changes {}".format( + log_adj_changes)) + + # aggregation timer + aggr_timer = ospf_data.setdefault("aggr_timer", None) + del_aggr_timer = ospf_data.setdefault("del_aggr_timer", False) + if del_aggr_timer: + config_data.append("no aggregation timer") + if aggr_timer: + config_data.append("aggregation timer {}".format( + aggr_timer)) + + # maximum path information + ecmp_data = ospf_data.setdefault("maximum-paths", {}) + if ecmp_data: + cmd = "maximum-paths {}".format(ecmp_data) + del_action = ospf_data.setdefault("del_max_path", False) + if del_action: + cmd = "no maximum-paths" + config_data.append(cmd) + # redistribute command redistribute_data = ospf_data.setdefault("redistribute", {}) if redistribute_data: @@ -203,6 +245,34 @@ def __create_ospf_global( cmd = "no {}".format(cmd) config_data.append(cmd) + #def route information + def_rte_data = ospf_data.setdefault("default-information", {}) + if def_rte_data: + if "originate" not in def_rte_data: + logger.debug("Router %s: 'originate key' not present in " + "input_dict", router) + else: + cmd = "default-information originate" + + if "always" in def_rte_data: + cmd = cmd + " always" + + if "metric" in def_rte_data: + cmd = cmd + " metric {}".format(def_rte_data["metric"]) + + if "metric-type" in def_rte_data: + cmd = cmd + " metric-type {}".format(def_rte_data[ + "metric-type"]) + + if "route-map" in def_rte_data: + cmd = cmd + " route-map {}".format(def_rte_data[ + "route-map"]) + + del_action = def_rte_data.setdefault("delete", False) + if del_action: + cmd = "no {}".format(cmd) + config_data.append(cmd) + # area interface information for ospf6d only if ospf == "ospf6": area_iface = ospf_data.setdefault("neighbors", {}) @@ -217,6 +287,21 @@ def __create_ospf_global( cmd = "no {}".format(cmd) config_data.append(cmd) + try: + if "area" in input_dict[router]['links'][neighbor][ + 'ospf6']: + iface = input_dict[router]["links"][neighbor]["interface"] + cmd = "interface {} area {}".format( + iface, input_dict[router]['links'][neighbor][ + 'ospf6']['area']) + if input_dict[router]['links'][neighbor].setdefault( + "delete", False): + cmd = "no {}".format(cmd) + config_data.append(cmd) + except KeyError: + pass + + # summary information summary_data = ospf_data.setdefault("summary-address", {}) if summary_data: @@ -427,11 +512,11 @@ def config_ospf_interface(tgen, topo, input_dict=None, build=False, load_config= result = create_common_configuration( tgen, router, config_data, "interface_config", build=build ) - logger.debug("Exiting lib API: create_igmp_config()") + logger.debug("Exiting lib API: config_ospf_interface()") return result -def clear_ospf(tgen, router): +def clear_ospf(tgen, router, ospf=None): """ This API is to clear ospf neighborship by running clear ip ospf interface * command, @@ -451,11 +536,16 @@ def clear_ospf(tgen, router): return False rnode = tgen.routers()[router] - # Clearing OSPF - logger.info("Clearing ospf process for router %s..", router) + if ospf: + version = "ipv6" + else: + version = "ip" - run_frr_cmd(rnode, "clear ip ospf interface ") + cmd = "clear {} ospf interface".format(version) + logger.info( + "Clearing ospf process on router %s.. using command '%s'", router, cmd) + run_frr_cmd(rnode, cmd) logger.debug("Exiting lib API: clear_ospf()") @@ -490,7 +580,7 @@ def redistribute_ospf(tgen, topo, dut, route_type, **kwargs): # Verification procs ################################ @retry(attempts=40, wait=2, return_is_str=True) -def verify_ospf_neighbor(tgen, topo, dut=None, input_dict=None, lan=False): +def verify_ospf_neighbor(tgen, topo, dut=None, input_dict=None, lan=False, expected=True): """ This API is to verify ospf neighborship by running show ip ospf neighbour command, @@ -502,6 +592,7 @@ def verify_ospf_neighbor(tgen, topo, dut=None, input_dict=None, lan=False): * `dut`: device under test * `input_dict` : Input dict data, required when configuring from testcase * `lan` : verify neighbors in lan topology + * `expected` : expected results from API, by-default True Usage ----- @@ -683,70 +774,194 @@ def verify_ospf_neighbor(tgen, topo, dut=None, input_dict=None, lan=False): ################################ # Verification procs ################################ -@retry(attempts=40, wait=2, return_is_str=True) -def verify_ospf6_neighbor(tgen, topo): +@retry(attempts=10, wait=2, return_is_str=True) +def verify_ospf6_neighbor(tgen, topo, dut=None, input_dict=None, lan=False): """ This API is to verify ospf neighborship by running - show ip ospf neighbour command, + show ipv6 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 ----- - Check FULL neighbors. - verify_ospf_neighbor(tgen, topo) + 1. To check FULL neighbors. + verify_ospf_neighbor(tgen, topo, dut=dut) - result = verify_ospf_neighbor(tgen, topo) + 2. To check neighbors with their roles. + input_dict = { + "r0": { + "ospf6": { + "neighbors": { + "r1": { + "state": "Full", + "role": "DR" + }, + "r2": { + "state": "Full", + "role": "DROther" + }, + "r3": { + "state": "Full", + "role": "DROther" + } + } + } + } + } + result = verify_ospf6_neighbor(tgen, topo, dut, input_dict, lan=True) Returns ------- True or False (Error Message) """ - - logger.debug("Entering lib API: verify_ospf6_neighbor()") + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) result = False - for router, rnode in tgen.routers().items(): - if "ospf6" not in topo["routers"][router]: - continue - logger.info("Verifying OSPF6 neighborship on router %s:", router) - show_ospf_json = run_frr_cmd( - rnode, "show ipv6 ospf6 neighbor json", isjson=True - ) + if input_dict: + for router, rnode in tgen.routers().items(): + if 'ospf6' not in topo['routers'][router]: + continue - if not show_ospf_json: - return "OSPF6 is not running" - - ospf_nbr_list = topo["routers"][router]["ospf6"]["neighbors"] - no_of_peer = 0 - for ospf_nbr in ospf_nbr_list: - ospf_nbr_rid = topo["routers"][ospf_nbr]["ospf6"]["router_id"] - for neighbor in show_ospf_json["neighbors"]: - if neighbor["neighborId"] == ospf_nbr_rid: - nh_state = neighbor["state"] - break - else: - return "[DUT: {}] OSPF6 peer {} missing".format(router, ospf_nbr_rid) + 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 ipv6 ospf neighbor json", isjson=True) + # Verifying output dictionary show_ospf_json is empty or not + if not bool(show_ospf_json): + errormsg = "OSPF6 is not running" + return errormsg + + ospf_data_list = input_dict[router]["ospf6"] + ospf_nbr_list = ospf_data_list['neighbors'] + + for ospf_nbr, nbr_data in ospf_nbr_list.items(): + data_ip = data_rid = topo['routers'][ospf_nbr]['ospf6']['router_id'] + if ospf_nbr in data_ip: + nbr_details = nbr_data[ospf_nbr] + elif lan: + for switch in topo['switches']: + if 'ospf6' in topo['switches'][switch]['links'][router]: + neighbor_ip = data_ip + else: + continue + else: + neighbor_ip = data_ip[router]['ipv6'].split("/")[0] - if nh_state == "Full": - no_of_peer += 1 + nh_state = None + neighbor_ip = neighbor_ip.lower() + nbr_rid = data_rid + get_index_val = dict((d['neighborId'], dict( \ + d, index=index)) for (index, d) in enumerate( \ + show_ospf_json['neighbors'])) + try: + nh_state = get_index_val.get(neighbor_ip)['state'] + intf_state = get_index_val.get(neighbor_ip)['ifState'] + except TypeError: + errormsg = "[DUT: {}] OSPF peer {} missing,from "\ + "{} ".format(router, + nbr_rid, ospf_nbr) + return errormsg - if no_of_peer == len(ospf_nbr_list): - logger.info("[DUT: {}] OSPF6 is Converged".format(router)) - result = True - else: - return "[DUT: {}] OSPF6 is not Converged".format(router) + nbr_state = nbr_data.setdefault("state",None) + nbr_role = nbr_data.setdefault("role",None) - logger.debug("Exiting API: verify_ospf6_neighbor()") + if nbr_state: + if nbr_state == nh_state: + logger.info("[DUT: {}] OSPF6 Nbr is {}:{} State {}".format + (router, ospf_nbr, nbr_rid, nh_state)) + result = True + else: + errormsg = ("[DUT: {}] OSPF6 is not Converged, neighbor" + " state is {} , Expected state is {}".format(router, + nh_state, nbr_state)) + return errormsg + if nbr_role: + if nbr_role == intf_state: + logger.info("[DUT: {}] OSPF6 Nbr is {}: {} Role {}".format( + router, ospf_nbr, nbr_rid, nbr_role)) + else: + errormsg = ("[DUT: {}] OSPF6 is not Converged with rid" + "{}, role is {}, Expected role is {}".format(router, + nbr_rid, intf_state, nbr_role)) + return errormsg + continue + else: + + for router, rnode in tgen.routers().items(): + if 'ospf6' not in topo['routers'][router]: + continue + + if dut is not None and dut != router: + continue + + logger.info("Verifying OSPF6 neighborship on router %s:", router) + show_ospf_json = run_frr_cmd(rnode, + "show ipv6 ospf neighbor json", isjson=True) + # Verifying output dictionary show_ospf_json is empty or not + if not bool(show_ospf_json): + errormsg = "OSPF6 is not running" + return errormsg + + ospf_data_list = topo["routers"][router]["ospf6"] + 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(): + data_ip = data_rid = topo['routers'][ospf_nbr]['ospf6']['router_id'] + if ospf_nbr in data_ip: + nbr_details = nbr_data[ospf_nbr] + elif lan: + for switch in topo['switches']: + if 'ospf6' in topo['switches'][switch]['links'][router]: + neighbor_ip = data_ip + else: + continue + else: + neighbor_ip = data_ip + + nh_state = None + neighbor_ip = neighbor_ip.lower() + nbr_rid = data_rid + get_index_val = dict((d['neighborId'], dict( \ + d, index=index)) for (index, d) in enumerate( \ + show_ospf_json['neighbors'])) + try: + nh_state = get_index_val.get(neighbor_ip)['state'] + intf_state = get_index_val.get(neighbor_ip)['ifState'] + except TypeError: + 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: {}] OSPF6 is Converged".format(router)) + result = True + else: + errormsg = ("[DUT: {}] OSPF6 is not Converged".format(router)) + return errormsg + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) 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 + tgen, dut, input_dict, next_hop=None, tag=None, metric=None, fib=None, expected=True ): """ This API is to verify ospf routes by running @@ -761,6 +976,7 @@ def verify_ospf_rib( * `tag` : tag to be verified * `metric` : metric to be verified * `fib` : True if the route is installed in FIB. + * `expected` : expected results from API, by-default True Usage ----- @@ -1021,7 +1237,7 @@ def verify_ospf_rib( @retry(attempts=10, wait=2, return_is_str=True) -def verify_ospf_interface(tgen, topo, dut=None, lan=False, input_dict=None): +def verify_ospf_interface(tgen, topo, dut=None, lan=False, input_dict=None, expected=True): """ This API is to verify ospf routes by running show ip ospf interface command. @@ -1033,6 +1249,7 @@ def verify_ospf_interface(tgen, topo, dut=None, lan=False, input_dict=None): * `dut`: device under test * `lan`: if set to true this interface belongs to LAN. * `input_dict` : Input dict data, required when configuring from testcase + * `expected` : expected results from API, by-default True Usage ----- @@ -1110,7 +1327,7 @@ def verify_ospf_interface(tgen, topo, dut=None, lan=False, input_dict=None): @retry(attempts=11, wait=2, return_is_str=True) -def verify_ospf_database(tgen, topo, dut, input_dict): +def verify_ospf_database(tgen, topo, dut, input_dict, expected=True): """ This API is to verify ospf lsa's by running show ip ospf database command. @@ -1121,6 +1338,7 @@ def verify_ospf_database(tgen, topo, dut, input_dict): * `dut`: device under test * `input_dict` : Input dict data, required when configuring from testcase * `topo` : next to be verified + * `expected` : expected results from API, by-default True Usage ----- @@ -1273,7 +1491,7 @@ def verify_ospf_database(tgen, topo, dut, input_dict): @retry(attempts=10, wait=2, return_is_str=True) -def verify_ospf_summary(tgen, topo, dut, input_dict): +def verify_ospf_summary(tgen, topo, dut, input_dict, expected=True): """ This API is to verify ospf routes by running show ip ospf interface command. @@ -1284,6 +1502,7 @@ def verify_ospf_summary(tgen, topo, dut, input_dict): * `topo` : topology descriptions * `dut`: device under test * `input_dict` : Input dict data, required when configuring from testcase + * `expected` : expected results from API, by-default True Usage ----- @@ -1349,3 +1568,667 @@ def verify_ospf_summary(tgen, topo, dut, input_dict): logger.debug("Exiting API: verify_ospf_summary()") return result + + + +@retry(attempts=10, wait=3, return_is_str=True) +def verify_ospf6_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_ospf6_rib(tgen, dut, input_dict,next_hop=nh) + + Returns + ------- + True or False (Error Message) + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + 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 ipv6 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) + + # Fix for PR 2644182 + try: + ospf_rib_json = ospf_rib_json['routes'] + except KeyError: + pass + + # Verifying output dictionary ospf_rib_json is not empty + if bool(ospf_rib_json) is False: + errormsg = "[DUT: {}] No routes found in OSPF6 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(ipaddress.ip_network(frr_unicode(st_rt))) + + _addr_type = validate_ip_address(st_rt) + if _addr_type != 'ipv6': + 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['nextHop'] 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 "destinationType" not in ospf_rib_json[ + st_rt]: + errormsg = ("[DUT: {}]: destinationType missing" + "for route {} in OSPF RIB \n".\ + format(dut, st_rt)) + return errormsg + elif _rtype != ospf_rib_json[st_rt][ + "destinationType"]: + errormsg = ("[DUT: {}]: destinationType mismatch" + "for route {} in OSPF RIB \n".\ + format(dut, st_rt)) + return errormsg + else: + logger.info("DUT: {}]: Found destinationType {}" + "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.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return result + + +@retry(attempts=3, wait=2, return_is_str=True) +def verify_ospf6_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': { + 'ospf6':{ + '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: {}".format(sys._getframe().f_code.co_name)) + result = False + + for router, rnode in tgen.routers().iteritems(): + if 'ospf6' 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 ipv6 ospf interface json", + isjson=True) + + # Verifying output dictionary show_ospf_json is empty or not + if not bool(show_ospf_json): + errormsg = "OSPF6 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: + for intf_attribute in intf_data['ospf6']: + if intf_data['ospf6'][intf_attribute] is not list: + if intf_data['ospf6'][intf_attribute] == show_ospf_json[ + intf][intf_attribute]: + logger.info("[DUT: %s] OSPF6 interface %s: %s is %s", + router, intf, intf_attribute, intf_data['ospf6'][ + intf_attribute]) + elif intf_data['ospf6'][intf_attribute] is list: + for addr_list in len(show_ospf_json[intf][intf_attribute]): + if show_ospf_json[intf][intf_attribute][addr_list][ + 'address'].split('/')[0] == intf_data['ospf6'][ + 'internetAddress'][0]['address']: + break + else: + errormsg= "[DUT: {}] OSPF6 interface {}: {} is {}, \ + Expected is {}".format(router, intf, intf_attribute, + intf_data['ospf6'][intf_attribute], intf_data['ospf6'][ + intf_attribute]) + return errormsg + else: + errormsg= "[DUT: {}] OSPF6 interface {}: {} is {}, \ + Expected is {}".format(router, intf, intf_attribute, + intf_data['ospf6'][intf_attribute], intf_data['ospf6'][ + intf_attribute]) + return errormsg + result = True + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return result + + +@retry(attempts=11, wait=2, return_is_str=True) +def verify_ospf6_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": { + "routerLinkStates": { + "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 + } + }, + "networkLinkStates": { + "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: {}".format(sys._getframe().f_code.co_name)) + + 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( + 'asExternalLinkStates', None) + + if ospf_db_data: + for ospf_area, area_lsa in ospf_db_data.items(): + if ospf_area in show_ospf_json['areas']: + if 'routerLinkStates' in area_lsa: + for lsa in area_lsa['routerLinkStates']: + for rtrlsa in show_ospf_json['areas'][ospf_area][ + 'routerLinkStates']: + if lsa['lsaId'] == rtrlsa['lsaId'] and \ + lsa['advertisedRouter'] == rtrlsa[ + 'advertisedRouter']: + result = True + break + if result: + logger.info( + "[DUT: %s] OSPF LSDB area %s:Router " + "LSA %s", router, ospf_area, lsa) + break + else: + errormsg = \ + "[DUT: {}] OSPF LSDB area {}: expected" \ + " Router LSA is {}".format(router, ospf_area, lsa) + return errormsg + + if 'networkLinkStates' in area_lsa: + for lsa in area_lsa['networkLinkStates']: + for netlsa in show_ospf_json['areas'][ospf_area][ + 'networkLinkStates']: + if lsa in show_ospf_json['areas'][ospf_area][ + 'networkLinkStates']: + if lsa['lsaId'] == netlsa['lsaId'] and \ + lsa['advertisedRouter'] == netlsa[ + 'advertisedRouter']: + result = True + break + if result: + logger.info( + "[DUT: %s] OSPF LSDB area %s:Network " + "LSA %s", router, ospf_area, lsa) + break + else: + errormsg = \ + "[DUT: {}] OSPF LSDB area {}: expected" \ + " Network LSA is {}".format(router, ospf_area, lsa) + return errormsg + + if 'summaryLinkStates' in area_lsa: + for lsa in area_lsa['summaryLinkStates']: + for t3lsa in show_ospf_json['areas'][ospf_area][ + 'summaryLinkStates']: + if lsa['lsaId'] == t3lsa['lsaId'] and \ + lsa['advertisedRouter'] == t3lsa[ + 'advertisedRouter']: + result = True + break + if result: + logger.info( + "[DUT: %s] OSPF LSDB area %s:Summary " + "LSA %s", router, ospf_area, lsa) + break + else: + errormsg = \ + "[DUT: {}] OSPF LSDB area {}: expected" \ + " Summary LSA is {}".format(router, ospf_area, lsa) + return errormsg + + if 'nssaExternalLinkStates' in area_lsa: + for lsa in area_lsa['nssaExternalLinkStates']: + for t7lsa in show_ospf_json['areas'][ospf_area][ + 'nssaExternalLinkStates']: + if lsa['lsaId'] == t7lsa['lsaId'] and \ + lsa['advertisedRouter'] == t7lsa[ + 'advertisedRouter']: + result = True + break + if result: + logger.info( + "[DUT: %s] OSPF LSDB area %s:Type7 " + "LSA %s", router, ospf_area, lsa) + break + else: + errormsg = \ + "[DUT: {}] OSPF LSDB area {}: expected" \ + " Type7 LSA is {}".format(router, ospf_area, lsa) + return errormsg + + if 'asbrSummaryLinkStates' in area_lsa: + for lsa in area_lsa['asbrSummaryLinkStates']: + for t4lsa in show_ospf_json['areas'][ospf_area][ + 'asbrSummaryLinkStates']: + if lsa['lsaId'] == t4lsa['lsaId'] and \ + lsa['advertisedRouter'] == t4lsa[ + 'advertisedRouter']: + result = True + break + if result: + 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 'linkLocalOpaqueLsa' in area_lsa: + for lsa in area_lsa['linkLocalOpaqueLsa']: + try: + for lnklsa in show_ospf_json['areas'][ospf_area][ + 'linkLocalOpaqueLsa']: + if lsa['lsaId'] in lnklsa['lsaId'] and \ + 'linkLocalOpaqueLsa' in show_ospf_json[ + 'areas'][ospf_area]: + logger.info(( + "[DUT: FRR] OSPF LSDB area %s:Opaque-LSA" + "%s", ospf_area, lsa)) + result = True + else: + errormsg = ("[DUT: FRR] OSPF LSDB area: {} " + "expected Opaque-LSA is {}, Found is {}".format( + ospf_area, lsa, show_ospf_json)) + raise ValueError (errormsg) + return errormsg + except KeyError: + errormsg = ("[DUT: FRR] linkLocalOpaqueLsa Not " + "present") + return errormsg + + if ospf_external_lsa: + for lsa in ospf_external_lsa: + try: + for t5lsa in show_ospf_json['asExternalLinkStates']: + if lsa['lsaId'] == t5lsa['lsaId'] and \ + lsa['advertisedRouter'] == t5lsa[ + 'advertisedRouter']: + result = True + break + except KeyError: + result = False + if result: + logger.info( + "[DUT: %s] OSPF LSDB:External LSA %s", + router, lsa) + result = True + else: + errormsg = \ + "[DUT: {}] OSPF LSDB : expected" \ + " External LSA is {}".format(router, lsa) + return errormsg + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return result + + + +def config_ospf6_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_ospf6_interface(tgen, topo, r1_ospf_auth) + + Returns + ------- + True or False + """ + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + result = False + 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 "ospf6" not in input_dict[router]['links'][lnk]: + logger.debug("Router %s: ospf6 configs is not present in" + "input_dict, passed input_dict", router, + input_dict) + continue + ospf_data = input_dict[router]['links'][lnk]['ospf6'] + 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) + data_ospf_mtu = ospf_data.setdefault("mtu_ignore", 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 = "ipv6 ospf area {}".format(data_ospf_area) + config_data.append(cmd) + + # interface ospf dr priority + if data_ospf_dr_priority: + cmd = "ipv6 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: + cmd = "ipv6 ospf cost {}".format( + ospf_data["cost"]) + if 'del_action' in ospf_data: + cmd = "no {}".format(cmd) + config_data.append(cmd) + + # interface ospf mtu + if data_ospf_mtu: + cmd = "ipv6 ospf mtu-ignore" + 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: {}".format(sys._getframe().f_code.co_name)) + return result diff --git a/tests/topotests/lib/pim.py b/tests/topotests/lib/pim.py index 61a5705a5d..ce90717fa4 100644 --- a/tests/topotests/lib/pim.py +++ b/tests/topotests/lib/pim.py @@ -496,7 +496,7 @@ def configure_pim_force_expire(tgen, topo, input_dict, build=False): # Verification APIs ############################################# @retry(attempts=6, wait=2, return_is_str=True) -def verify_pim_neighbors(tgen, topo, dut=None, iface=None, nbr_ip=None): +def verify_pim_neighbors(tgen, topo, dut=None, iface=None, nbr_ip=None, expected=True): """ Verify all PIM neighbors are up and running, config is verified using "show ip pim neighbor" cli @@ -508,6 +508,7 @@ def verify_pim_neighbors(tgen, topo, dut=None, iface=None, nbr_ip=None): * `dut` : dut info * `iface` : link for which PIM nbr need to check * `nbr_ip` : neighbor ip of interface + * `expected` : expected results from API, by-default True Usage ----- @@ -619,7 +620,7 @@ def verify_pim_neighbors(tgen, topo, dut=None, iface=None, nbr_ip=None): @retry(attempts=21, wait=2, return_is_str=True) -def verify_igmp_groups(tgen, dut, interface, group_addresses): +def verify_igmp_groups(tgen, dut, interface, group_addresses, expected=True): """ Verify IGMP groups are received from an intended interface by running "show ip igmp groups" command @@ -630,6 +631,7 @@ def verify_igmp_groups(tgen, dut, interface, group_addresses): * `dut`: device under test * `interface`: interface, from which IGMP groups would be received * `group_addresses`: IGMP group address + * `expected` : expected results from API, by-default True Usage ----- @@ -693,7 +695,7 @@ def verify_igmp_groups(tgen, dut, interface, group_addresses): @retry(attempts=31, wait=2, return_is_str=True) def verify_upstream_iif( - tgen, dut, iif, src_address, group_addresses, joinState=None, refCount=1 + tgen, dut, iif, src_address, group_addresses, joinState=None, refCount=1, expected=True ): """ Verify upstream inbound interface is updated correctly @@ -708,6 +710,7 @@ def verify_upstream_iif( * `group_addresses`: IGMP group address * `joinState`: upstream join state * `refCount`: refCount value + * `expected` : expected results from API, by-default True Usage ----- @@ -845,7 +848,7 @@ def verify_upstream_iif( @retry(attempts=6, wait=2, return_is_str=True) -def verify_join_state_and_timer(tgen, dut, iif, src_address, group_addresses): +def verify_join_state_and_timer(tgen, dut, iif, src_address, group_addresses, expected=True): """ Verify join state is updated correctly and join timer is running with the help of "show ip pim upstream" cli @@ -857,6 +860,7 @@ def verify_join_state_and_timer(tgen, dut, iif, src_address, group_addresses): * `iif`: inbound interface * `src_address`: source address * `group_addresses`: IGMP group address + * `expected` : expected results from API, by-default True Usage ----- @@ -964,7 +968,7 @@ def verify_join_state_and_timer(tgen, dut, iif, src_address, group_addresses): @retry(attempts=41, wait=2, return_is_dict=True) def verify_ip_mroutes( - tgen, dut, src_address, group_addresses, iif, oil, return_uptime=False, mwait=0 + tgen, dut, src_address, group_addresses, iif, oil, return_uptime=False, mwait=0, expected=True ): """ Verify ip mroutes and make sure (*, G)/(S, G) is present in mroutes @@ -980,7 +984,7 @@ def verify_ip_mroutes( * `oil`: Outgoing interface * `return_uptime`: If True, return uptime dict, default is False * `mwait`: Wait time, default is 0 - + * `expected` : expected results from API, by-default True Usage ----- @@ -1161,7 +1165,7 @@ def verify_ip_mroutes( @retry(attempts=31, wait=2, return_is_str=True) def verify_pim_rp_info( - tgen, topo, dut, group_addresses, oif=None, rp=None, source=None, iamrp=None + tgen, topo, dut, group_addresses, oif=None, rp=None, source=None, iamrp=None, expected=True ): """ Verify pim rp info by running "show ip pim rp-info" cli @@ -1176,6 +1180,7 @@ def verify_pim_rp_info( * `rp`: RP address * `source`: Source of RP * `iamrp`: User defined RP + * `expected` : expected results from API, by-default True Usage ----- @@ -1317,7 +1322,7 @@ def verify_pim_rp_info( @retry(attempts=31, wait=2, return_is_str=True) def verify_pim_state( - tgen, dut, iif, oil, group_addresses, src_address=None, installed_fl=None + tgen, dut, iif, oil, group_addresses, src_address=None, installed_fl=None, expected=True ): """ Verify pim state by running "show ip pim state" cli @@ -1331,6 +1336,7 @@ def verify_pim_state( * `group_addresses`: IGMP group address * `src_address`: source address, default = None * installed_fl` : Installed flag + * `expected` : expected results from API, by-default True Usage ----- @@ -1485,7 +1491,7 @@ def verify_pim_interface_traffic(tgen, input_dict): @retry(attempts=21, wait=2, return_is_str=True) -def verify_pim_interface(tgen, topo, dut, interface=None, interface_ip=None): +def verify_pim_interface(tgen, topo, dut, interface=None, interface_ip=None, expected=True): """ Verify all PIM interface are up and running, config is verified using "show ip pim interface" cli @@ -1497,6 +1503,7 @@ def verify_pim_interface(tgen, topo, dut, interface=None, interface_ip=None): * `dut` : device under test * `interface` : interface name * `interface_ip` : interface ip address + * `expected` : expected results from API, by-default True Usage ----- @@ -1791,7 +1798,7 @@ def clear_ip_igmp_interfaces(tgen, dut): @retry(attempts=10, wait=2, return_is_str=True) -def clear_ip_mroute_verify(tgen, dut): +def clear_ip_mroute_verify(tgen, dut, expected=True): """ Clear ip mroute by running "clear ip mroute" cli and verify mroutes are up again after mroute clear @@ -1800,6 +1807,8 @@ def clear_ip_mroute_verify(tgen, dut): ---------- * `tgen`: topogen object * `dut`: Device Under Test + * `expected` : expected results from API, by-default True + Usage ----- @@ -2165,7 +2174,7 @@ def find_rp_from_bsrp_info(tgen, dut, bsr, grp=None): @retry(attempts=6, wait=2, return_is_str=True) -def verify_pim_grp_rp_source(tgen, topo, dut, grp_addr, rp_source, rpadd=None): +def verify_pim_grp_rp_source(tgen, topo, dut, grp_addr, rp_source, rpadd=None, expected=True): """ Verify pim rp info by running "show ip pim rp-info" cli @@ -2177,6 +2186,7 @@ def verify_pim_grp_rp_source(tgen, topo, dut, grp_addr, rp_source, rpadd=None): * `grp_addr`: IGMP group address * 'rp_source': source from which rp installed * 'rpadd': rp address + * `expected` : expected results from API, by-default True Usage ----- @@ -2267,7 +2277,7 @@ def verify_pim_grp_rp_source(tgen, topo, dut, grp_addr, rp_source, rpadd=None): @retry(attempts=31, wait=2, return_is_str=True) -def verify_pim_bsr(tgen, topo, dut, bsr_ip): +def verify_pim_bsr(tgen, topo, dut, bsr_ip, expected=True): """ Verify all PIM interface are up and running, config is verified using "show ip pim interface" cli @@ -2278,6 +2288,7 @@ def verify_pim_bsr(tgen, topo, dut, bsr_ip): * `topo` : json file data * `dut` : device under test * 'bsr' : bsr ip to be verified + * `expected` : expected results from API, by-default True Usage ----- @@ -2322,7 +2333,7 @@ def verify_pim_bsr(tgen, topo, dut, bsr_ip): @retry(attempts=31, wait=2, return_is_str=True) -def verify_ip_pim_upstream_rpf(tgen, topo, dut, interface, group_addresses, rp=None): +def verify_ip_pim_upstream_rpf(tgen, topo, dut, interface, group_addresses, rp=None, expected=True): """ Verify IP PIM upstream rpf, config is verified using "show ip pim neighbor" cli @@ -2336,6 +2347,7 @@ def verify_ip_pim_upstream_rpf(tgen, topo, dut, interface, group_addresses, rp=N * `group_addresses` : list of group address for which upstream info needs to be checked * `rp` : RP address + * `expected` : expected results from API, by-default True Usage ----- @@ -2519,7 +2531,7 @@ def enable_disable_pim_bsm(tgen, router, intf, enable=True): @retry(attempts=31, wait=2, return_is_str=True) -def verify_ip_pim_join(tgen, topo, dut, interface, group_addresses, src_address=None): +def verify_ip_pim_join(tgen, topo, dut, interface, group_addresses, src_address=None, expected=True): """ Verify ip pim join by running "show ip pim join" cli @@ -2531,6 +2543,7 @@ def verify_ip_pim_join(tgen, topo, dut, interface, group_addresses, src_address= * `interface`: interface name, from which PIM join would come * `group_addresses`: IGMP group address * `src_address`: Source address + * `expected` : expected results from API, by-default True Usage ----- @@ -2609,7 +2622,7 @@ def verify_ip_pim_join(tgen, topo, dut, interface, group_addresses, src_address= @retry(attempts=31, wait=2, return_is_dict=True) -def verify_igmp_config(tgen, input_dict, stats_return=False): +def verify_igmp_config(tgen, input_dict, stats_return=False, expected=True): """ Verify igmp interface details, verifying following configs: timerQueryInterval @@ -2623,6 +2636,7 @@ def verify_igmp_config(tgen, input_dict, stats_return=False): * `input_dict` : Input dict data, required to verify timer * `stats_return`: If user wants API to return statistics + * `expected` : expected results from API, by-default True Usage ----- @@ -2898,7 +2912,7 @@ def verify_igmp_config(tgen, input_dict, stats_return=False): @retry(attempts=31, wait=2, return_is_str=True) -def verify_pim_config(tgen, input_dict): +def verify_pim_config(tgen, input_dict, expected=True): """ Verify pim interface details, verifying following configs: drPriority @@ -2912,6 +2926,7 @@ def verify_pim_config(tgen, input_dict): * `tgen`: topogen object * `input_dict` : Input dict data, required to verify timer + * `expected` : expected results from API, by-default True Usage ----- @@ -3023,7 +3038,7 @@ def verify_pim_config(tgen, input_dict): @retry(attempts=21, wait=2, return_is_dict=True) -def verify_multicast_traffic(tgen, input_dict, return_traffic=False): +def verify_multicast_traffic(tgen, input_dict, return_traffic=False, expected=True): """ Verify multicast traffic by running "show multicast traffic count json" cli @@ -3034,6 +3049,8 @@ def verify_multicast_traffic(tgen, input_dict, return_traffic=False): * `input_dict(dict)`: defines DUT, what and for which interfaces traffic needs to be verified * `return_traffic`: returns traffic stats + * `expected` : expected results from API, by-default True + Usage ----- input_dict = { @@ -3264,7 +3281,7 @@ def get_refCount_for_mroute(tgen, dut, iif, src_address, group_addresses): @retry(attempts=21, wait=2, return_is_str=True) -def verify_multicast_flag_state(tgen, dut, src_address, group_addresses, flag): +def verify_multicast_flag_state(tgen, dut, src_address, group_addresses, flag, expected=True): """ Verify flag state for mroutes and make sure (*, G)/(S, G) are having coorect flags by running "show ip mroute" cli @@ -3276,6 +3293,7 @@ def verify_multicast_flag_state(tgen, dut, src_address, group_addresses, flag): * `src_address`: source address * `group_addresses`: IGMP group address * `flag`: flag state, needs to be verified + * `expected` : expected results from API, by-default True Usage ----- @@ -3358,7 +3376,7 @@ def verify_multicast_flag_state(tgen, dut, src_address, group_addresses, flag): @retry(attempts=21, wait=2, return_is_str=True) -def verify_igmp_interface(tgen, topo, dut, igmp_iface, interface_ip): +def verify_igmp_interface(tgen, topo, dut, igmp_iface, interface_ip, expected=True): """ Verify all IGMP interface are up and running, config is verified using "show ip igmp interface" cli @@ -3370,6 +3388,7 @@ def verify_igmp_interface(tgen, topo, dut, igmp_iface, interface_ip): * `dut` : device under test * `igmp_iface` : interface name * `interface_ip` : interface ip address + * `expected` : expected results from API, by-default True Usage ----- diff --git a/tests/topotests/lib/topogen.py b/tests/topotests/lib/topogen.py index 553f2bc6cf..ade5933504 100644 --- a/tests/topotests/lib/topogen.py +++ b/tests/topotests/lib/topogen.py @@ -222,6 +222,22 @@ class Topogen(object): self.peern += 1 return self.gears[name] + def add_host(self, name, ip, defaultRoute): + """ + Adds a new host to the topology. This function has the following + parameters: + * `ip`: the peer address (e.g. '1.2.3.4/24') + * `defaultRoute`: the peer default route (e.g. 'via 1.2.3.1') + """ + if name is None: + name = "host{}".format(self.peern) + if name in self.gears: + raise KeyError("host already exists") + + self.gears[name] = TopoHost(self, name, ip=ip, defaultRoute=defaultRoute) + self.peern += 1 + return self.gears[name] + def add_link(self, node1, node2, ifname1=None, ifname2=None): """ Creates a connection between node1 and node2. The nodes can be the @@ -641,6 +657,8 @@ class TopoRouter(TopoGear): # Try to find relevant old logfiles in /tmp and delete them map(os.remove, glob.glob("{}/{}/*.log".format(self.logdir, self.name))) + # Remove old valgrind files + map(os.remove, glob.glob("{}/{}.valgrind.*".format(self.logdir, self.name))) # Remove old core files map(os.remove, glob.glob("{}/{}/*.dmp".format(self.logdir, self.name))) diff --git a/tests/topotests/lib/topotest.py b/tests/topotests/lib/topotest.py index 2a5bd17361..d1f60bfe0d 100644 --- a/tests/topotests/lib/topotest.py +++ b/tests/topotests/lib/topotest.py @@ -1454,6 +1454,8 @@ class Router(Node): gdb_breakpoints = g_extra_config["gdb_breakpoints"] gdb_daemons = g_extra_config["gdb_daemons"] gdb_routers = g_extra_config["gdb_routers"] + valgrind_extra = g_extra_config["valgrind_extra"] + valgrind_memleaks = g_extra_config["valgrind_memleaks"] bundle_data = "" @@ -1503,7 +1505,14 @@ class Router(Node): ) + "/var/run/{}/snmpd.pid -x /etc/frr/agentx".format(self.routertype) else: binary = os.path.join(self.daemondir, daemon) + cmdenv = "ASAN_OPTIONS=log_path={0}.asan".format(daemon) + if valgrind_memleaks: + this_dir = os.path.dirname(os.path.abspath(os.path.realpath(__file__))) + supp_file = os.path.abspath(os.path.join(this_dir, "../../../tools/valgrind.supp")) + cmdenv += " /usr/bin/valgrind --num-callers=50 --log-file={1}/{2}.valgrind.{0}.%p --leak-check=full --suppressions={3}".format(daemon, self.logdir, self.name, supp_file) + if valgrind_extra: + cmdenv += "--gen-suppressions=all --expensive-definedness-checks=yes" cmdopt = "{} --log file:{}.log --log-level debug".format( daemon_opts, daemon ) diff --git a/tests/topotests/msdp_mesh_topo1/__init__.py b/tests/topotests/msdp_mesh_topo1/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/msdp_mesh_topo1/__init__.py diff --git a/tests/topotests/msdp_mesh_topo1/r1/bgpd.conf b/tests/topotests/msdp_mesh_topo1/r1/bgpd.conf new file mode 100644 index 0000000000..953d90aa03 --- /dev/null +++ b/tests/topotests/msdp_mesh_topo1/r1/bgpd.conf @@ -0,0 +1,7 @@ +router bgp 65000 + neighbor 10.254.254.2 remote-as 65000 + neighbor 10.254.254.2 update-source 10.254.254.1 + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/msdp_mesh_topo1/r1/ospfd.conf b/tests/topotests/msdp_mesh_topo1/r1/ospfd.conf new file mode 100644 index 0000000000..c1adbd5440 --- /dev/null +++ b/tests/topotests/msdp_mesh_topo1/r1/ospfd.conf @@ -0,0 +1,8 @@ +interface r1-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +router ospf + network 192.168.1.0/24 area 0.0.0.0 + redistribute connected +! diff --git a/tests/topotests/msdp_mesh_topo1/r1/pimd.conf b/tests/topotests/msdp_mesh_topo1/r1/pimd.conf new file mode 100644 index 0000000000..49341efa57 --- /dev/null +++ b/tests/topotests/msdp_mesh_topo1/r1/pimd.conf @@ -0,0 +1,15 @@ +interface lo + ip pim + ip pim use-source 10.254.254.1 +! +interface r1-eth0 + ip pim +! +interface r1-eth1 + ip pim + ip igmp +! +ip pim rp 10.254.254.1 +ip msdp mesh-group mg-1 source 10.254.254.1 +ip msdp mesh-group mg-1 member 10.254.254.2 +ip msdp mesh-group mg-1 member 10.254.254.3 diff --git a/tests/topotests/msdp_mesh_topo1/r1/zebra.conf b/tests/topotests/msdp_mesh_topo1/r1/zebra.conf new file mode 100644 index 0000000000..42c850f00f --- /dev/null +++ b/tests/topotests/msdp_mesh_topo1/r1/zebra.conf @@ -0,0 +1,11 @@ +ip forwarding +! +interface lo + ip address 10.254.254.1/32 +! +interface r1-eth0 + ip address 192.168.1.2/24 +! +interface r1-eth1 + ip address 192.168.10.1/24 +! diff --git a/tests/topotests/msdp_mesh_topo1/r2/bgpd.conf b/tests/topotests/msdp_mesh_topo1/r2/bgpd.conf new file mode 100644 index 0000000000..f442efc60f --- /dev/null +++ b/tests/topotests/msdp_mesh_topo1/r2/bgpd.conf @@ -0,0 +1,10 @@ +router bgp 65000 + neighbor pg-1 peer-group + neighbor pg-1 update-source 10.254.254.1 + neighbor pg-1 remote-as 65000 + neighbor 10.254.254.1 peer-group pg-1 + neighbor 10.254.254.3 peer-group pg-1 + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/msdp_mesh_topo1/r2/ospfd.conf b/tests/topotests/msdp_mesh_topo1/r2/ospfd.conf new file mode 100644 index 0000000000..9e9ac5fb2e --- /dev/null +++ b/tests/topotests/msdp_mesh_topo1/r2/ospfd.conf @@ -0,0 +1,13 @@ +interface r2-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +interface r2-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +router ospf + network 192.168.1.0/24 area 0.0.0.0 + network 192.168.2.0/24 area 0.0.0.0 + redistribute connected +! diff --git a/tests/topotests/msdp_mesh_topo1/r2/pimd.conf b/tests/topotests/msdp_mesh_topo1/r2/pimd.conf new file mode 100644 index 0000000000..9005263ed7 --- /dev/null +++ b/tests/topotests/msdp_mesh_topo1/r2/pimd.conf @@ -0,0 +1,14 @@ +interface lo + ip pim + ip pim use-source 10.254.254.2 +! +interface r2-eth0 + ip pim +! +interface r2-eth1 + ip pim +! +ip pim rp 10.254.254.2 +ip msdp mesh-group mg-1 source 10.254.254.2 +ip msdp mesh-group mg-1 member 10.254.254.1 +ip msdp mesh-group mg-1 member 10.254.254.3 diff --git a/tests/topotests/msdp_mesh_topo1/r2/zebra.conf b/tests/topotests/msdp_mesh_topo1/r2/zebra.conf new file mode 100644 index 0000000000..6b26194218 --- /dev/null +++ b/tests/topotests/msdp_mesh_topo1/r2/zebra.conf @@ -0,0 +1,11 @@ +ip forwarding +! +interface lo + ip address 10.254.254.2/32 +! +interface r2-eth0 + ip address 192.168.1.1/24 +! +interface r2-eth1 + ip address 192.168.2.1/24 +! diff --git a/tests/topotests/msdp_mesh_topo1/r3/bgpd.conf b/tests/topotests/msdp_mesh_topo1/r3/bgpd.conf new file mode 100644 index 0000000000..6c3f89ad97 --- /dev/null +++ b/tests/topotests/msdp_mesh_topo1/r3/bgpd.conf @@ -0,0 +1,7 @@ +router bgp 65000 + neighbor 192.168.2.1 remote-as 65000 + neighbor 192.168.2.1 update-source 10.254.254.3 + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/msdp_mesh_topo1/r3/ospfd.conf b/tests/topotests/msdp_mesh_topo1/r3/ospfd.conf new file mode 100644 index 0000000000..7b7b1abe62 --- /dev/null +++ b/tests/topotests/msdp_mesh_topo1/r3/ospfd.conf @@ -0,0 +1,8 @@ +interface r3-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +router ospf + network 192.168.2.0/24 area 0.0.0.0 + redistribute connected +! diff --git a/tests/topotests/msdp_mesh_topo1/r3/pimd.conf b/tests/topotests/msdp_mesh_topo1/r3/pimd.conf new file mode 100644 index 0000000000..30e1148561 --- /dev/null +++ b/tests/topotests/msdp_mesh_topo1/r3/pimd.conf @@ -0,0 +1,15 @@ +interface lo + ip pim + ip pim use-source 10.254.254.3 +! +interface r3-eth0 + ip pim +! +interface r3-eth1 + ip pim + ip igmp +! +ip pim rp 10.254.254.3 +ip msdp mesh-group mg-1 source 10.254.254.3 +ip msdp mesh-group mg-1 member 10.254.254.1 +ip msdp mesh-group mg-1 member 10.254.254.2 diff --git a/tests/topotests/msdp_mesh_topo1/r3/zebra.conf b/tests/topotests/msdp_mesh_topo1/r3/zebra.conf new file mode 100644 index 0000000000..a8a15f3c0f --- /dev/null +++ b/tests/topotests/msdp_mesh_topo1/r3/zebra.conf @@ -0,0 +1,11 @@ +ip forwarding +! +interface lo + ip address 10.254.254.3/32 +! +interface r3-eth0 + ip address 192.168.2.2/24 +! +interface r3-eth1 + ip address 192.168.30.1/24 +! diff --git a/tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.dot b/tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.dot new file mode 100644 index 0000000000..8792e2c7bb --- /dev/null +++ b/tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.dot @@ -0,0 +1,88 @@ +## 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="msdp_mesh_topo1"; + + # Routers + 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, + ]; + h1 [ + shape=doubleoctagon + label="h1", + fillcolor="#4f4f4f", + style=filled, + ]; + h2 [ + shape=doubleoctagon + label="h2", + fillcolor="#4f4f4f", + style=filled, + ]; + + # Switches + s1 [ + shape=oval, + label="sw1\n192.168.1.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + s2 [ + shape=oval, + label="sw2\n192.168.2.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + s3 [ + shape=oval, + label="sw3\n192.168.10.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + s4 [ + shape=oval, + label="sw3\n192.168.30.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + + # Connections + r1 -- s1 [label="eth0\n.2"]; + r2 -- s1 [label="eth0\n.1"]; + + r2 -- s2 [label="eth1\n.1"]; + r3 -- s2 [label="eth0\n.2"]; + + r1 -- s3 [label="eth1\n.1"]; + h1 -- s3 [label="eth0\n.2"]; + + r3 -- s4 [label="eth1\n.1"]; + h2 -- s4 [label="eth0\n.2"]; +} diff --git a/tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.png b/tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.png Binary files differnew file mode 100644 index 0000000000..9a15b8b088 --- /dev/null +++ b/tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.png diff --git a/tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.py b/tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.py new file mode 100644 index 0000000000..719ead091c --- /dev/null +++ b/tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.py @@ -0,0 +1,296 @@ +#!/usr/bin/env python + +# +# test_msdp_mesh_topo1.py +# Part of NetDEF Topology Tests +# +# Copyright (C) 2021 by +# Network Device Education Foundation, Inc. ("NetDEF") +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_msdp_mesh_topo1.py: Test the FRR PIM MSDP mesh groups. +""" + +import os +import sys +import json +from functools import partial +import pytest +import socket + +# 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 + +pytestmark = [pytest.mark.bgpd, pytest.mark.ospfd, pytest.mark.pimd] + +# +# Test global variables: +# They are used to handle communicating with external application. +# +APP_SOCK_PATH = '/tmp/topotests/apps.sock' +HELPER_APP_PATH = os.path.join(CWD, "../lib/mcast-tester.py") +app_listener = None +app_clients = {} + +def listen_to_applications(): + "Start listening socket to connect with applications." + # Remove old socket. + try: + os.unlink(APP_SOCK_PATH) + except OSError: + pass + + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM, 0) + sock.bind(APP_SOCK_PATH) + sock.listen(10) + global app_listener + app_listener = sock + +def accept_host(host): + "Accept connection from application running in hosts." + global app_listener, app_clients + conn = app_listener.accept() + app_clients[host] = { + 'fd': conn[0], + 'address': conn[1] + } + +def close_applications(): + "Signal applications to stop and close all sockets." + global app_listener, app_clients + + # Close listening socket. + app_listener.close() + + # Remove old socket. + try: + os.unlink(APP_SOCK_PATH) + except OSError: + pass + + # Close all host connections. + for host in ["h1", "h2"]: + if app_clients.get(host) is None: + continue + app_clients["h1"]["fd"].close() + + +class MSDPMeshTopo1(Topo): + "Test topology builder" + + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # Create 3 routers + for routern in range(1, 4): + 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"]) + + # Create stub networks for multicast traffic. + tgen.add_host("h1", "192.168.10.2/24", "192.168.10.1") + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["h1"]) + + tgen.add_host("h2", "192.168.30.2/24", "192.168.30.1") + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["h2"]) + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(MSDPMeshTopo1, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + for rname, router in router_list.items(): + daemon_file = "{}/{}/zebra.conf".format(CWD, rname) + if os.path.isfile(daemon_file): + router.load_config(TopoRouter.RD_ZEBRA, daemon_file) + + daemon_file = "{}/{}/bgpd.conf".format(CWD, rname) + if os.path.isfile(daemon_file): + router.load_config(TopoRouter.RD_BGP, daemon_file) + + daemon_file = "{}/{}/ospfd.conf".format(CWD, rname) + if os.path.isfile(daemon_file): + router.load_config(TopoRouter.RD_OSPF, daemon_file) + + daemon_file = "{}/{}/pimd.conf".format(CWD, rname) + if os.path.isfile(daemon_file): + router.load_config(TopoRouter.RD_PIM, daemon_file) + + # Initialize all routers. + tgen.start_router() + + # Start applications socket. + listen_to_applications() + + +def test_wait_ospf_convergence(): + "Wait for OSPF to converge" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("waiting for protocols to converge") + + def expect_loopback_route(router, iptype, route, proto): + "Wait until route is present on RIB for protocol." + logger.info("waiting route {} in {}".format(route, router)) + test_func = partial( + topotest.router_json_cmp, + tgen.gears[router], + "show {} route json".format(iptype), + {route: [{"protocol": proto}]} + ) + _, result = topotest.run_and_expect(test_func, None, count=40, wait=1) + assertmsg = '"{}" OSPF convergence failure'.format(router) + assert result is None, assertmsg + + # Wait for R1 <-> R2 convergence. + expect_loopback_route("r1", "ip", "10.254.254.2/32", "ospf") + # Wait for R1 <-> R3 convergence. + expect_loopback_route("r1", "ip", "10.254.254.3/32", "ospf") + + # Wait for R2 <-> R1 convergence. + expect_loopback_route("r2", "ip", "10.254.254.1/32", "ospf") + # Wait for R2 <-> R3 convergence. + expect_loopback_route("r2", "ip", "10.254.254.3/32", "ospf") + + # Wait for R3 <-> R1 convergence. + expect_loopback_route("r3", "ip", "10.254.254.1/32", "ospf") + # Wait for R3 <-> R2 convergence. + expect_loopback_route("r3", "ip", "10.254.254.2/32", "ospf") + + +def test_wait_msdp_convergence(): + "Wait for MSDP to converge" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("test MSDP convergence") + + tgen.gears["h1"].run("{} --send='0.7' '{}' '{}' '{}' &".format( + HELPER_APP_PATH, APP_SOCK_PATH, '229.0.1.10', 'h1-eth0')) + accept_host("h1") + + tgen.gears["h2"].run("{} '{}' '{}' '{}' &".format( + HELPER_APP_PATH, APP_SOCK_PATH, '229.0.1.10', 'h2-eth0')) + accept_host("h2") + + def expect_msdp_peer(router, peer, sa_count=0): + "Expect MSDP peer connection to be established with SA amount." + logger.info("waiting MSDP connection from peer {} on router {}".format(peer, router)) + test_func = partial( + topotest.router_json_cmp, + tgen.gears[router], + "show ip msdp peer json", + {peer: {"state": "established", "saCount": sa_count}} + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assertmsg = '"{}" MSDP connection failure'.format(router) + assert result is None, assertmsg + + # R1 peers. + expect_msdp_peer("r1", "10.254.254.2") + expect_msdp_peer("r1", "10.254.254.3") + + # R2 peers. + expect_msdp_peer("r2", "10.254.254.1", 1) + expect_msdp_peer("r2", "10.254.254.3") + + # R3 peers. + expect_msdp_peer("r3", "10.254.254.1", 1) + expect_msdp_peer("r3", "10.254.254.2") + + +def test_msdp_sa_configuration(): + "Expect the multicast traffic SA to be created" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("test MSDP SA") + + def expect_msdp_sa(router, source, group, local, rp, spt_setup): + "Expect MSDP SA." + logger.info("waiting MSDP SA on router {}".format(router)) + test_func = partial( + topotest.router_json_cmp, + tgen.gears[router], + "show ip msdp sa json", + {group: {source: {"local": local, "rp": rp, "sptSetup": spt_setup}}} + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assertmsg = '"{}" MSDP SA failure'.format(router) + assert result is None, assertmsg + + source = "192.168.10.2" + group = "229.0.1.10" + rp = "10.254.254.1" + + # R1 SA. + expect_msdp_sa("r1", source, group, "yes", "-", "-") + + # R2 SA. + expect_msdp_sa("r2", source, group, "no", rp, "no") + + # R3 peers. + expect_msdp_sa("r3", source, group, "no", rp, "yes") + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + close_applications() + tgen.stop_topology() + + +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/ospf6_topo1/r1/show_ipv6_route.ref b/tests/topotests/ospf6_topo1/r1/show_ipv6_route.ref index a2ddf7c5ae..96489b0756 100644 --- a/tests/topotests/ospf6_topo1/r1/show_ipv6_route.ref +++ b/tests/topotests/ospf6_topo1/r1/show_ipv6_route.ref @@ -4,6 +4,6 @@ O>* fc00:3:3:3::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX O>* fc00:4:4:4::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX O fc00:a:a:a::/64 [110/10] is directly connected, r1-sw5, weight 1, XX:XX:XX O>* fc00:b:b:b::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX -O>* fc00:2222:2222:2222::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX -O>* fc00:3333:3333:3333::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX +O>* fc00:2222:2222:2222::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX +O>* fc00:3333:3333:3333::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX O>* fc00:4444:4444:4444::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX diff --git a/tests/topotests/ospf6_topo1/r2/show_ipv6_route.ref b/tests/topotests/ospf6_topo1/r2/show_ipv6_route.ref index 1f642b1b22..78c1ad8830 100644 --- a/tests/topotests/ospf6_topo1/r2/show_ipv6_route.ref +++ b/tests/topotests/ospf6_topo1/r2/show_ipv6_route.ref @@ -4,7 +4,7 @@ O>* fc00:3:3:3::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX O>* fc00:4:4:4::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX O fc00:a:a:a::/64 [110/10] is directly connected, r2-sw5, weight 1, XX:XX:XX O>* fc00:b:b:b::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX -O>* fc00:1111:1111:1111::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX -O>* fc00:3333:3333:3333::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX +O>* fc00:1111:1111:1111::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX +O>* fc00:3333:3333:3333::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX O>* fc00:4444:4444:4444::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX diff --git a/tests/topotests/ospf6_topo1/r3/show_ipv6_route.ref b/tests/topotests/ospf6_topo1/r3/show_ipv6_route.ref index 8e3afa583a..dc0acbe0c5 100644 --- a/tests/topotests/ospf6_topo1/r3/show_ipv6_route.ref +++ b/tests/topotests/ospf6_topo1/r3/show_ipv6_route.ref @@ -4,7 +4,7 @@ O fc00:3:3:3::/64 [110/10] is directly connected, r3-stubnet, weight 1, XX:XX: O>* fc00:4:4:4::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw6, weight 1, XX:XX:XX O fc00:a:a:a::/64 [110/10] is directly connected, r3-sw5, weight 1, XX:XX:XX O fc00:b:b:b::/64 [110/10] is directly connected, r3-sw6, weight 1, XX:XX:XX -O>* fc00:1111:1111:1111::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, weight 1, XX:XX:XX -O>* fc00:2222:2222:2222::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, weight 1, XX:XX:XX -O>* fc00:4444:4444:4444::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw6, weight 1, XX:XX:XX +O>* fc00:1111:1111:1111::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, weight 1, XX:XX:XX +O>* fc00:2222:2222:2222::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, weight 1, XX:XX:XX +O>* fc00:4444:4444:4444::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw6, weight 1, XX:XX:XX diff --git a/tests/topotests/ospf6_topo1/r4/show_ipv6_route.ref b/tests/topotests/ospf6_topo1/r4/show_ipv6_route.ref index 0df652ffb3..730fd9f2d5 100644 --- a/tests/topotests/ospf6_topo1/r4/show_ipv6_route.ref +++ b/tests/topotests/ospf6_topo1/r4/show_ipv6_route.ref @@ -6,4 +6,4 @@ O>* fc00:a:a:a::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX O fc00:b:b:b::/64 [110/10] is directly connected, r4-sw6, weight 1, XX:XX:XX O>* fc00:1111:1111:1111::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX O>* fc00:2222:2222:2222::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX -O>* fc00:3333:3333:3333::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX +O>* fc00:3333:3333:3333::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX diff --git a/tests/topotests/ospf6_topo1_vrf/README.md b/tests/topotests/ospf6_topo1_vrf/README.md index 3ed0b8fbe2..18bca5c54f 100644 --- a/tests/topotests/ospf6_topo1_vrf/README.md +++ b/tests/topotests/ospf6_topo1_vrf/README.md @@ -54,10 +54,12 @@ Simplified `R1` config (R1 is similar) hostname r1 ! interface r1-stubnet vrf r1-cust1 + ipv6 ospf6 area 0.0.0.0 ipv6 address fc00:1:1:1::1/64 ipv6 ospf6 network broadcast ! interface r1-sw5 vrf r1-cust1 + ipv6 ospf6 area 0.0.0.0 ipv6 address fc00:a:a:a::1/64 ipv6 ospf6 network broadcast ! @@ -65,8 +67,6 @@ Simplified `R1` config (R1 is similar) router-id 10.0.0.1 log-adjacency-changes detail redistribute static - interface r1-stubnet area 0.0.0.0 - interface r1-sw5 area 0.0.0.0 ! ipv6 route fc00:1111:1111:1111::/64 fc00:1:1:1::1234 vrf r1-cust1 @@ -75,14 +75,17 @@ Simplified `R3` config hostname r3 ! interface r3-stubnet vrf r3-cust1 + ipv6 ospf6 area 0.0.0.0 ipv6 address fc00:3:3:3::3/64 ipv6 ospf6 network broadcast ! interface r3-sw5 vrf r3-cust1 + ipv6 ospf6 area 0.0.0.0 ipv6 address fc00:a:a:a::3/64 ipv6 ospf6 network broadcast ! interface r3-sw6 vrf r3-cust1 + ipv6 ospf6 area 0.0.0.1 ipv6 address fc00:b:b:b::3/64 ipv6 ospf6 network broadcast ! @@ -90,9 +93,6 @@ Simplified `R3` config router-id 10.0.0.3 log-adjacency-changes detail redistribute static - interface r3-stubnet area 0.0.0.0 - interface r3-sw5 area 0.0.0.0 - interface r3-sw6 area 0.0.0.1 ! ipv6 route fc00:3333:3333:3333::/64 fc00:3:3:3::1234 vrf r3-cust1 diff --git a/tests/topotests/ospf6_topo1_vrf/r1/ospf6d.conf b/tests/topotests/ospf6_topo1_vrf/r1/ospf6d.conf index ed480354e4..83bdfb7c81 100644 --- a/tests/topotests/ospf6_topo1_vrf/r1/ospf6d.conf +++ b/tests/topotests/ospf6_topo1_vrf/r1/ospf6d.conf @@ -9,12 +9,14 @@ debug ospf6 neighbor debug ospf6 route table debug ospf6 flooding ! -interface r1-stubnet vrf r1-cust1 +interface r1-stubnet + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 network broadcast ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 10 ! -interface r1-sw5 vrf r1-cust1 +interface r1-sw5 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 network broadcast ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 10 @@ -23,8 +25,6 @@ router ospf6 vrf r1-cust1 ospf6 router-id 10.0.0.1 log-adjacency-changes detail redistribute static - interface r1-stubnet area 0.0.0.0 - interface r1-sw5 area 0.0.0.0 ! line vty exec-timeout 0 0 diff --git a/tests/topotests/ospf6_topo1_vrf/r1/show_ipv6_vrf_route.ref b/tests/topotests/ospf6_topo1_vrf/r1/show_ipv6_vrf_route.ref index a2ddf7c5ae..96489b0756 100644 --- a/tests/topotests/ospf6_topo1_vrf/r1/show_ipv6_vrf_route.ref +++ b/tests/topotests/ospf6_topo1_vrf/r1/show_ipv6_vrf_route.ref @@ -4,6 +4,6 @@ O>* fc00:3:3:3::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX O>* fc00:4:4:4::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX O fc00:a:a:a::/64 [110/10] is directly connected, r1-sw5, weight 1, XX:XX:XX O>* fc00:b:b:b::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX -O>* fc00:2222:2222:2222::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX -O>* fc00:3333:3333:3333::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX +O>* fc00:2222:2222:2222::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX +O>* fc00:3333:3333:3333::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX O>* fc00:4444:4444:4444::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX diff --git a/tests/topotests/ospf6_topo1_vrf/r2/ospf6d.conf b/tests/topotests/ospf6_topo1_vrf/r2/ospf6d.conf index 485771e7d5..7fd01aa0cc 100644 --- a/tests/topotests/ospf6_topo1_vrf/r2/ospf6d.conf +++ b/tests/topotests/ospf6_topo1_vrf/r2/ospf6d.conf @@ -9,12 +9,14 @@ debug ospf6 neighbor debug ospf6 route table debug ospf6 flooding ! -interface r2-stubnet vrf r2-cust1 +interface r2-stubnet + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 network broadcast ipv6 ospf6 dead-interval 10 ipv6 ospf6 hello-interval 2 ! -interface r2-sw5 vrf r2-cust1 +interface r2-sw5 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 network broadcast ipv6 ospf6 dead-interval 10 ipv6 ospf6 hello-interval 2 @@ -23,8 +25,6 @@ router ospf6 vrf r2-cust1 ospf6 router-id 10.0.0.2 log-adjacency-changes detail redistribute static - interface r2-stubnet area 0.0.0.0 - interface r2-sw5 area 0.0.0.0 ! line vty exec-timeout 0 0 diff --git a/tests/topotests/ospf6_topo1_vrf/r2/show_ipv6_vrf_route.ref b/tests/topotests/ospf6_topo1_vrf/r2/show_ipv6_vrf_route.ref index 3289619414..4c390f7cd6 100644 --- a/tests/topotests/ospf6_topo1_vrf/r2/show_ipv6_vrf_route.ref +++ b/tests/topotests/ospf6_topo1_vrf/r2/show_ipv6_vrf_route.ref @@ -4,6 +4,6 @@ O>* fc00:3:3:3::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX O>* fc00:4:4:4::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX O fc00:a:a:a::/64 [110/10] is directly connected, r2-sw5, weight 1, XX:XX:XX O>* fc00:b:b:b::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX -O>* fc00:1111:1111:1111::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX -O>* fc00:3333:3333:3333::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX +O>* fc00:1111:1111:1111::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX +O>* fc00:3333:3333:3333::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX O>* fc00:4444:4444:4444::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX diff --git a/tests/topotests/ospf6_topo1_vrf/r3/ospf6d.conf b/tests/topotests/ospf6_topo1_vrf/r3/ospf6d.conf index f5837bf6fd..df5aed3a6a 100644 --- a/tests/topotests/ospf6_topo1_vrf/r3/ospf6d.conf +++ b/tests/topotests/ospf6_topo1_vrf/r3/ospf6d.conf @@ -9,17 +9,20 @@ debug ospf6 neighbor debug ospf6 route table debug ospf6 flooding ! -interface r3-stubnet vrf r3-cust1 +interface r3-stubnet + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 network broadcast ipv6 ospf6 dead-interval 10 ipv6 ospf6 hello-interval 2 ! -interface r3-sw5 vrf r3-cust1 +interface r3-sw5 + ipv6 ospf6 area 0.0.0.0 ipv6 ospf6 network broadcast ipv6 ospf6 dead-interval 10 ipv6 ospf6 hello-interval 2 ! -interface r3-sw6 vrf r3-cust1 +interface r3-sw6 + ipv6 ospf6 area 0.0.0.1 ipv6 ospf6 network broadcast ipv6 ospf6 dead-interval 10 ipv6 ospf6 hello-interval 2 @@ -28,9 +31,6 @@ router ospf6 vrf r3-cust1 ospf6 router-id 10.0.0.3 log-adjacency-changes detail redistribute static - interface r3-stubnet area 0.0.0.0 - interface r3-sw5 area 0.0.0.0 - interface r3-sw6 area 0.0.0.1 ! line vty exec-timeout 0 0 diff --git a/tests/topotests/ospf6_topo1_vrf/r3/show_ipv6_vrf_route.ref b/tests/topotests/ospf6_topo1_vrf/r3/show_ipv6_vrf_route.ref index ac713190ff..989213f963 100644 --- a/tests/topotests/ospf6_topo1_vrf/r3/show_ipv6_vrf_route.ref +++ b/tests/topotests/ospf6_topo1_vrf/r3/show_ipv6_vrf_route.ref @@ -4,6 +4,6 @@ O fc00:3:3:3::/64 [110/10] is directly connected, r3-stubnet, weight 1, XX:XX: O>* fc00:4:4:4::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw6, weight 1, XX:XX:XX O fc00:a:a:a::/64 [110/10] is directly connected, r3-sw5, weight 1, XX:XX:XX O fc00:b:b:b::/64 [110/10] is directly connected, r3-sw6, weight 1, XX:XX:XX -O>* fc00:1111:1111:1111::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, weight 1, XX:XX:XX -O>* fc00:2222:2222:2222::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, weight 1, XX:XX:XX -O>* fc00:4444:4444:4444::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw6, weight 1, XX:XX:XX +O>* fc00:1111:1111:1111::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, weight 1, XX:XX:XX +O>* fc00:2222:2222:2222::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, weight 1, XX:XX:XX +O>* fc00:4444:4444:4444::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw6, weight 1, XX:XX:XX diff --git a/tests/topotests/ospf6_topo1_vrf/r4/ospf6d.conf b/tests/topotests/ospf6_topo1_vrf/r4/ospf6d.conf index ab67d06ff4..465defb40f 100644 --- a/tests/topotests/ospf6_topo1_vrf/r4/ospf6d.conf +++ b/tests/topotests/ospf6_topo1_vrf/r4/ospf6d.conf @@ -9,12 +9,14 @@ debug ospf6 neighbor debug ospf6 route table debug ospf6 flooding ! -interface r4-stubnet vrf r4-cust1 +interface r4-stubnet + ipv6 ospf6 area 0.0.0.1 ipv6 ospf6 network broadcast ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 10 ! -interface r4-sw6 vrf r4-cust1 +interface r4-sw6 + ipv6 ospf6 area 0.0.0.1 ipv6 ospf6 network broadcast ipv6 ospf6 hello-interval 2 ipv6 ospf6 dead-interval 10 @@ -23,8 +25,6 @@ router ospf6 vrf r4-cust1 ospf6 router-id 10.0.0.4 log-adjacency-changes detail redistribute static - interface r4-stubnet area 0.0.0.1 - interface r4-sw6 area 0.0.0.1 ! line vty exec-timeout 0 0 diff --git a/tests/topotests/ospf6_topo1_vrf/r4/show_ipv6_vrf_route.ref b/tests/topotests/ospf6_topo1_vrf/r4/show_ipv6_vrf_route.ref index 0df652ffb3..730fd9f2d5 100644 --- a/tests/topotests/ospf6_topo1_vrf/r4/show_ipv6_vrf_route.ref +++ b/tests/topotests/ospf6_topo1_vrf/r4/show_ipv6_vrf_route.ref @@ -6,4 +6,4 @@ O>* fc00:a:a:a::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX O fc00:b:b:b::/64 [110/10] is directly connected, r4-sw6, weight 1, XX:XX:XX O>* fc00:1111:1111:1111::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX O>* fc00:2222:2222:2222::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX -O>* fc00:3333:3333:3333::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX +O>* fc00:3333:3333:3333::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX diff --git a/tests/topotests/ospf_topo1/r1/ospf6route.txt b/tests/topotests/ospf_topo1/r1/ospf6route.txt index 1bfd6942ea..d01511c0ee 100644 --- a/tests/topotests/ospf_topo1/r1/ospf6route.txt +++ b/tests/topotests/ospf_topo1/r1/ospf6route.txt @@ -1,13 +1,13 @@ *N IA 2001:db8:1::/64 :: r1-eth0 00:02:11 *N IA 2001:db8:2::/64 fe80::b038:bcff:fe27:e2d6 r1-eth1 00:02:06 - N E1 2001:db8:2::/64 fe80::b038:bcff:fe27:e2d6 r1-eth1 00:02:06 + N E2 2001:db8:2::/64 fe80::b038:bcff:fe27:e2d6 r1-eth1 00:02:06 *N IA 2001:db8:3::/64 :: r1-eth1 00:02:11 - N E1 2001:db8:3::/64 fe80::50b7:d8ff:fe5f:8ff0 r1-eth1 00:02:06 - N E1 2001:db8:3::/64 fe80::b038:bcff:fe27:e2d6 r1-eth1 00:02:06 + N E2 2001:db8:3::/64 fe80::50b7:d8ff:fe5f:8ff0 r1-eth1 00:02:06 + N E2 2001:db8:3::/64 fe80::b038:bcff:fe27:e2d6 r1-eth1 00:02:06 *N IA 2001:db8:100::/64 fe80::50b7:d8ff:fe5f:8ff0 r1-eth1 00:02:06 - N E1 2001:db8:100::/64 fe80::50b7:d8ff:fe5f:8ff0 r1-eth1 00:02:06 + N E2 2001:db8:100::/64 fe80::50b7:d8ff:fe5f:8ff0 r1-eth1 00:02:06 *N IE 2001:db8:200::/64 fe80::50b7:d8ff:fe5f:8ff0 r1-eth1 00:02:06 - N E1 2001:db8:200::/64 fe80::50b7:d8ff:fe5f:8ff0 r1-eth1 00:02:06 - N E1 2001:db8:200::/64 fe80::50b7:d8ff:fe5f:8ff0 r1-eth1 00:02:04 + N E2 2001:db8:200::/64 fe80::50b7:d8ff:fe5f:8ff0 r1-eth1 00:02:06 + N E2 2001:db8:200::/64 fe80::50b7:d8ff:fe5f:8ff0 r1-eth1 00:02:04 *N IE 2001:db8:300::/64 fe80::50b7:d8ff:fe5f:8ff0 r1-eth1 00:02:04 - N E1 2001:db8:300::/64 fe80::50b7:d8ff:fe5f:8ff0 r1-eth1 00:02:04 + N E2 2001:db8:300::/64 fe80::50b7:d8ff:fe5f:8ff0 r1-eth1 00:02:04 diff --git a/tests/topotests/ospf_topo1/r1/ospf6route_down.txt b/tests/topotests/ospf_topo1/r1/ospf6route_down.txt index 1ce96c86c0..57113d049f 100644 --- a/tests/topotests/ospf_topo1/r1/ospf6route_down.txt +++ b/tests/topotests/ospf_topo1/r1/ospf6route_down.txt @@ -1,5 +1,5 @@ *N IA 2001:db8:1::/64 :: r1-eth0 00:01:51 *N IA 2001:db8:2::/64 fe80::281a:23ff:fe22:8a40 r1-eth1 00:00:52 - N E1 2001:db8:2::/64 fe80::281a:23ff:fe22:8a40 r1-eth1 00:00:52 + N E2 2001:db8:2::/64 fe80::281a:23ff:fe22:8a40 r1-eth1 00:00:52 *N IA 2001:db8:3::/64 :: r1-eth1 00:00:52 - N E1 2001:db8:3::/64 fe80::281a:23ff:fe22:8a40 r1-eth1 00:00:52 + N E2 2001:db8:3::/64 fe80::281a:23ff:fe22:8a40 r1-eth1 00:00:52 diff --git a/tests/topotests/ospf_topo1/r1/ospf6route_ecmp.txt b/tests/topotests/ospf_topo1/r1/ospf6route_ecmp.txt index 4df6e5ec00..48e9209a04 100644 --- a/tests/topotests/ospf_topo1/r1/ospf6route_ecmp.txt +++ b/tests/topotests/ospf_topo1/r1/ospf6route_ecmp.txt @@ -1,13 +1,13 @@ *N IA 2001:db8:1::/64 :: r1-eth0 00:06:13 *N IA 2001:db8:2::/64 fe80::e8bb:62ff:fee8:7022 r1-eth1 00:06:08 - N E1 2001:db8:2::/64 fe80::e8bb:62ff:fee8:7022 r1-eth1 00:06:08 + N E2 2001:db8:2::/64 fe80::e8bb:62ff:fee8:7022 r1-eth1 00:06:08 *N IA 2001:db8:3::/64 :: r1-eth1 00:06:13 - N E1 2001:db8:3::/64 fe80::400f:dff:fe35:a1e7 r1-eth1 00:06:08 + N E2 2001:db8:3::/64 fe80::400f:dff:fe35:a1e7 r1-eth1 00:06:08 fe80::e8bb:62ff:fee8:7022 r1-eth1 *N IA 2001:db8:100::/64 fe80::400f:dff:fe35:a1e7 r1-eth1 00:06:08 - N E1 2001:db8:100::/64 fe80::400f:dff:fe35:a1e7 r1-eth1 00:06:08 + N E2 2001:db8:100::/64 fe80::400f:dff:fe35:a1e7 r1-eth1 00:06:08 *N IE 2001:db8:200::/64 fe80::400f:dff:fe35:a1e7 r1-eth1 00:06:08 - N E1 2001:db8:200::/64 fe80::400f:dff:fe35:a1e7 r1-eth1 00:06:08 - N E1 2001:db8:200::/64 fe80::400f:dff:fe35:a1e7 r1-eth1 00:06:07 + N E2 2001:db8:200::/64 fe80::400f:dff:fe35:a1e7 r1-eth1 00:06:08 + N E2 2001:db8:200::/64 fe80::400f:dff:fe35:a1e7 r1-eth1 00:06:07 *N IE 2001:db8:300::/64 fe80::400f:dff:fe35:a1e7 r1-eth1 00:06:07 - N E1 2001:db8:300::/64 fe80::400f:dff:fe35:a1e7 r1-eth1 00:06:07 + N E2 2001:db8:300::/64 fe80::400f:dff:fe35:a1e7 r1-eth1 00:06:07 diff --git a/tests/topotests/ospf_topo1/r2/ospf6route.txt b/tests/topotests/ospf_topo1/r2/ospf6route.txt index 7d3ce5b207..71c84d2ebd 100644 --- a/tests/topotests/ospf_topo1/r2/ospf6route.txt +++ b/tests/topotests/ospf_topo1/r2/ospf6route.txt @@ -1,13 +1,13 @@ *N IA 2001:db8:1::/64 fe80::b49b:4cff:fe80:4e87 r2-eth1 00:03:34 - N E1 2001:db8:1::/64 fe80::b49b:4cff:fe80:4e87 r2-eth1 00:03:34 + N E2 2001:db8:1::/64 fe80::b49b:4cff:fe80:4e87 r2-eth1 00:03:34 *N IA 2001:db8:2::/64 :: r2-eth0 00:03:39 *N IA 2001:db8:3::/64 :: r2-eth1 00:03:34 - N E1 2001:db8:3::/64 fe80::b49b:4cff:fe80:4e87 r2-eth1 00:03:34 - N E1 2001:db8:3::/64 fe80::50b7:d8ff:fe5f:8ff0 r2-eth1 00:03:34 + N E2 2001:db8:3::/64 fe80::b49b:4cff:fe80:4e87 r2-eth1 00:03:34 + N E2 2001:db8:3::/64 fe80::50b7:d8ff:fe5f:8ff0 r2-eth1 00:03:34 *N IA 2001:db8:100::/64 fe80::50b7:d8ff:fe5f:8ff0 r2-eth1 00:03:34 - N E1 2001:db8:100::/64 fe80::50b7:d8ff:fe5f:8ff0 r2-eth1 00:03:34 + N E2 2001:db8:100::/64 fe80::50b7:d8ff:fe5f:8ff0 r2-eth1 00:03:34 *N IE 2001:db8:200::/64 fe80::50b7:d8ff:fe5f:8ff0 r2-eth1 00:03:34 - N E1 2001:db8:200::/64 fe80::50b7:d8ff:fe5f:8ff0 r2-eth1 00:03:34 - N E1 2001:db8:200::/64 fe80::50b7:d8ff:fe5f:8ff0 r2-eth1 00:03:32 + N E2 2001:db8:200::/64 fe80::50b7:d8ff:fe5f:8ff0 r2-eth1 00:03:34 + N E2 2001:db8:200::/64 fe80::50b7:d8ff:fe5f:8ff0 r2-eth1 00:03:32 *N IE 2001:db8:300::/64 fe80::50b7:d8ff:fe5f:8ff0 r2-eth1 00:03:32 - N E1 2001:db8:300::/64 fe80::50b7:d8ff:fe5f:8ff0 r2-eth1 00:03:32 + N E2 2001:db8:300::/64 fe80::50b7:d8ff:fe5f:8ff0 r2-eth1 00:03:32 diff --git a/tests/topotests/ospf_topo1/r2/ospf6route_down.txt b/tests/topotests/ospf_topo1/r2/ospf6route_down.txt index acfffc9f1c..a1f041218b 100644 --- a/tests/topotests/ospf_topo1/r2/ospf6route_down.txt +++ b/tests/topotests/ospf_topo1/r2/ospf6route_down.txt @@ -1,5 +1,5 @@ *N IA 2001:db8:1::/64 fe80::fc0b:daff:fe31:6791 r2-eth1 00:06:19 - N E1 2001:db8:1::/64 fe80::fc0b:daff:fe31:6791 r2-eth1 00:06:19 + N E2 2001:db8:1::/64 fe80::fc0b:daff:fe31:6791 r2-eth1 00:06:19 *N IA 2001:db8:2::/64 :: r2-eth0 00:07:17 *N IA 2001:db8:3::/64 :: r2-eth1 00:06:27 - N E1 2001:db8:3::/64 fe80::fc0b:daff:fe31:6791 r2-eth1 00:06:19 + N E2 2001:db8:3::/64 fe80::fc0b:daff:fe31:6791 r2-eth1 00:06:19 diff --git a/tests/topotests/ospf_topo1/r2/ospf6route_ecmp.txt b/tests/topotests/ospf_topo1/r2/ospf6route_ecmp.txt index f58b501e31..0c06d234cf 100644 --- a/tests/topotests/ospf_topo1/r2/ospf6route_ecmp.txt +++ b/tests/topotests/ospf_topo1/r2/ospf6route_ecmp.txt @@ -1,13 +1,13 @@ *N IA 2001:db8:1::/64 fe80::98cd:28ff:fe5e:3d93 r2-eth1 00:07:04 - N E1 2001:db8:1::/64 fe80::98cd:28ff:fe5e:3d93 r2-eth1 00:07:04 + N E2 2001:db8:1::/64 fe80::98cd:28ff:fe5e:3d93 r2-eth1 00:07:04 *N IA 2001:db8:2::/64 :: r2-eth0 00:07:09 *N IA 2001:db8:3::/64 :: r2-eth1 00:07:04 - N E1 2001:db8:3::/64 fe80::400f:dff:fe35:a1e7 r2-eth1 00:07:04 + N E2 2001:db8:3::/64 fe80::400f:dff:fe35:a1e7 r2-eth1 00:07:04 fe80::98cd:28ff:fe5e:3d93 r2-eth1 *N IA 2001:db8:100::/64 fe80::400f:dff:fe35:a1e7 r2-eth1 00:07:04 - N E1 2001:db8:100::/64 fe80::400f:dff:fe35:a1e7 r2-eth1 00:07:04 + N E2 2001:db8:100::/64 fe80::400f:dff:fe35:a1e7 r2-eth1 00:07:04 *N IE 2001:db8:200::/64 fe80::400f:dff:fe35:a1e7 r2-eth1 00:07:04 - N E1 2001:db8:200::/64 fe80::400f:dff:fe35:a1e7 r2-eth1 00:07:04 - N E1 2001:db8:200::/64 fe80::400f:dff:fe35:a1e7 r2-eth1 00:07:03 + N E2 2001:db8:200::/64 fe80::400f:dff:fe35:a1e7 r2-eth1 00:07:04 + N E2 2001:db8:200::/64 fe80::400f:dff:fe35:a1e7 r2-eth1 00:07:03 *N IE 2001:db8:300::/64 fe80::400f:dff:fe35:a1e7 r2-eth1 00:07:03 - N E1 2001:db8:300::/64 fe80::400f:dff:fe35:a1e7 r2-eth1 00:07:03 + N E2 2001:db8:300::/64 fe80::400f:dff:fe35:a1e7 r2-eth1 00:07:03 diff --git a/tests/topotests/ospf_topo1/r3/ospf6route.txt b/tests/topotests/ospf_topo1/r3/ospf6route.txt index b123c42650..69c99b4fd1 100644 --- a/tests/topotests/ospf_topo1/r3/ospf6route.txt +++ b/tests/topotests/ospf_topo1/r3/ospf6route.txt @@ -1,12 +1,12 @@ *N IA 2001:db8:1::/64 fe80::b49b:4cff:fe80:4e87 r3-eth0 00:04:03 - N E1 2001:db8:1::/64 fe80::b49b:4cff:fe80:4e87 r3-eth0 00:04:03 + N E2 2001:db8:1::/64 fe80::b49b:4cff:fe80:4e87 r3-eth0 00:04:03 *N IA 2001:db8:2::/64 fe80::b038:bcff:fe27:e2d6 r3-eth0 00:04:03 - N E1 2001:db8:2::/64 fe80::b038:bcff:fe27:e2d6 r3-eth0 00:04:03 + N E2 2001:db8:2::/64 fe80::b038:bcff:fe27:e2d6 r3-eth0 00:04:03 *N IA 2001:db8:3::/64 :: r3-eth0 00:04:08 - N E1 2001:db8:3::/64 fe80::b49b:4cff:fe80:4e87 r3-eth0 00:04:03 - N E1 2001:db8:3::/64 fe80::b038:bcff:fe27:e2d6 r3-eth0 00:04:03 + N E2 2001:db8:3::/64 fe80::b49b:4cff:fe80:4e87 r3-eth0 00:04:03 + N E2 2001:db8:3::/64 fe80::b038:bcff:fe27:e2d6 r3-eth0 00:04:03 *N IA 2001:db8:100::/64 :: r3-eth1 00:04:08 *N IA 2001:db8:200::/64 :: r3-eth2 00:04:05 - N E1 2001:db8:200::/64 fe80::78e0:deff:feb1:ec0 r3-eth2 00:04:00 + N E2 2001:db8:200::/64 fe80::78e0:deff:feb1:ec0 r3-eth2 00:04:00 *N IA 2001:db8:300::/64 fe80::78e0:deff:feb1:ec0 r3-eth2 00:04:00 - N E1 2001:db8:300::/64 fe80::78e0:deff:feb1:ec0 r3-eth2 00:04:00 + N E2 2001:db8:300::/64 fe80::78e0:deff:feb1:ec0 r3-eth2 00:04:00 diff --git a/tests/topotests/ospf_topo1/r3/ospf6route_down.txt b/tests/topotests/ospf_topo1/r3/ospf6route_down.txt index ed69a8376b..645ee0bfc4 100644 --- a/tests/topotests/ospf_topo1/r3/ospf6route_down.txt +++ b/tests/topotests/ospf_topo1/r3/ospf6route_down.txt @@ -1,5 +1,5 @@ *N IA 2001:db8:100::/64 :: r3-eth1 00:08:06 *N IA 2001:db8:200::/64 :: r3-eth2 00:08:04 - N E1 2001:db8:200::/64 fe80::80a6:c3ff:fea9:88be r3-eth2 00:07:59 + N E2 2001:db8:200::/64 fe80::80a6:c3ff:fea9:88be r3-eth2 00:07:59 *N IA 2001:db8:300::/64 fe80::80a6:c3ff:fea9:88be r3-eth2 00:07:59 - N E1 2001:db8:300::/64 fe80::80a6:c3ff:fea9:88be r3-eth2 00:07:59 + N E2 2001:db8:300::/64 fe80::80a6:c3ff:fea9:88be r3-eth2 00:07:59 diff --git a/tests/topotests/ospf_topo1/r3/ospf6route_ecmp.txt b/tests/topotests/ospf_topo1/r3/ospf6route_ecmp.txt index 54e575adcb..ecd51be4ef 100644 --- a/tests/topotests/ospf_topo1/r3/ospf6route_ecmp.txt +++ b/tests/topotests/ospf_topo1/r3/ospf6route_ecmp.txt @@ -1,12 +1,12 @@ *N IA 2001:db8:1::/64 fe80::98cd:28ff:fe5e:3d93 r3-eth0 00:08:58 - N E1 2001:db8:1::/64 fe80::98cd:28ff:fe5e:3d93 r3-eth0 00:08:58 + N E2 2001:db8:1::/64 fe80::98cd:28ff:fe5e:3d93 r3-eth0 00:08:58 *N IA 2001:db8:2::/64 fe80::e8bb:62ff:fee8:7022 r3-eth0 00:08:58 - N E1 2001:db8:2::/64 fe80::e8bb:62ff:fee8:7022 r3-eth0 00:08:58 + N E2 2001:db8:2::/64 fe80::e8bb:62ff:fee8:7022 r3-eth0 00:08:58 *N IA 2001:db8:3::/64 :: r3-eth0 00:09:03 - N E1 2001:db8:3::/64 fe80::98cd:28ff:fe5e:3d93 r3-eth0 00:08:58 + N E2 2001:db8:3::/64 fe80::98cd:28ff:fe5e:3d93 r3-eth0 00:08:58 fe80::e8bb:62ff:fee8:7022 r3-eth0 *N IA 2001:db8:100::/64 :: r3-eth1 00:09:03 *N IA 2001:db8:200::/64 :: r3-eth2 00:09:02 - N E1 2001:db8:200::/64 fe80::d0dc:aff:fec5:5973 r3-eth2 00:08:57 + N E2 2001:db8:200::/64 fe80::d0dc:aff:fec5:5973 r3-eth2 00:08:57 *N IA 2001:db8:300::/64 fe80::d0dc:aff:fec5:5973 r3-eth2 00:08:57 - N E1 2001:db8:300::/64 fe80::d0dc:aff:fec5:5973 r3-eth2 00:08:57 + N E2 2001:db8:300::/64 fe80::d0dc:aff:fec5:5973 r3-eth2 00:08:57 diff --git a/tests/topotests/ospf_topo1/r4/ospf6route.txt b/tests/topotests/ospf_topo1/r4/ospf6route.txt index ceeee2cac8..3a4f5efdf0 100644 --- a/tests/topotests/ospf_topo1/r4/ospf6route.txt +++ b/tests/topotests/ospf_topo1/r4/ospf6route.txt @@ -1,13 +1,13 @@ *N IE 2001:db8:1::/64 fe80::987b:baff:fe8a:c864 r4-eth0 00:04:25 - N E1 2001:db8:1::/64 fe80::987b:baff:fe8a:c864 r4-eth0 00:04:25 + N E2 2001:db8:1::/64 fe80::987b:baff:fe8a:c864 r4-eth0 00:04:25 *N IE 2001:db8:2::/64 fe80::987b:baff:fe8a:c864 r4-eth0 00:04:25 - N E1 2001:db8:2::/64 fe80::987b:baff:fe8a:c864 r4-eth0 00:04:25 + N E2 2001:db8:2::/64 fe80::987b:baff:fe8a:c864 r4-eth0 00:04:25 *N IE 2001:db8:3::/64 fe80::987b:baff:fe8a:c864 r4-eth0 00:04:25 - N E1 2001:db8:3::/64 fe80::987b:baff:fe8a:c864 r4-eth0 00:04:25 - N E1 2001:db8:3::/64 fe80::987b:baff:fe8a:c864 r4-eth0 00:04:25 - N E1 2001:db8:3::/64 fe80::987b:baff:fe8a:c864 r4-eth0 00:04:25 + N E2 2001:db8:3::/64 fe80::987b:baff:fe8a:c864 r4-eth0 00:04:25 + N E2 2001:db8:3::/64 fe80::987b:baff:fe8a:c864 r4-eth0 00:04:25 + N E2 2001:db8:3::/64 fe80::987b:baff:fe8a:c864 r4-eth0 00:04:25 *N IE 2001:db8:100::/64 fe80::987b:baff:fe8a:c864 r4-eth0 00:04:25 - N E1 2001:db8:100::/64 fe80::987b:baff:fe8a:c864 r4-eth0 00:04:25 + N E2 2001:db8:100::/64 fe80::987b:baff:fe8a:c864 r4-eth0 00:04:25 *N IA 2001:db8:200::/64 :: r4-eth0 00:04:30 - N E1 2001:db8:200::/64 fe80::987b:baff:fe8a:c864 r4-eth0 00:04:25 + N E2 2001:db8:200::/64 fe80::987b:baff:fe8a:c864 r4-eth0 00:04:25 *N IA 2001:db8:300::/64 :: r4-eth1 00:04:30 diff --git a/tests/topotests/ospf_topo1/r4/ospf6route_down.txt b/tests/topotests/ospf_topo1/r4/ospf6route_down.txt index 4ad636dd98..165f8dbdf8 100644 --- a/tests/topotests/ospf_topo1/r4/ospf6route_down.txt +++ b/tests/topotests/ospf_topo1/r4/ospf6route_down.txt @@ -1,5 +1,5 @@ *N IE 2001:db8:100::/64 fe80::b44b:a1ff:fe48:3d69 r4-eth0 00:01:45 - N E1 2001:db8:100::/64 fe80::b44b:a1ff:fe48:3d69 r4-eth0 00:01:45 + N E2 2001:db8:100::/64 fe80::b44b:a1ff:fe48:3d69 r4-eth0 00:01:45 *N IA 2001:db8:200::/64 :: r4-eth0 00:01:50 - N E1 2001:db8:200::/64 fe80::b44b:a1ff:fe48:3d69 r4-eth0 00:01:45 + N E2 2001:db8:200::/64 fe80::b44b:a1ff:fe48:3d69 r4-eth0 00:01:45 *N IA 2001:db8:300::/64 :: r4-eth1 00:01:50 diff --git a/tests/topotests/ospf_topo1/r4/ospf6route_ecmp.txt b/tests/topotests/ospf_topo1/r4/ospf6route_ecmp.txt index b5cb10b72b..d0d72a876f 100644 --- a/tests/topotests/ospf_topo1/r4/ospf6route_ecmp.txt +++ b/tests/topotests/ospf_topo1/r4/ospf6route_ecmp.txt @@ -1,12 +1,12 @@ *N IE 2001:db8:1::/64 fe80::78fe:fcff:fe51:9afc r4-eth0 00:09:13 - N E1 2001:db8:1::/64 fe80::78fe:fcff:fe51:9afc r4-eth0 00:09:13 + N E2 2001:db8:1::/64 fe80::78fe:fcff:fe51:9afc r4-eth0 00:09:13 *N IE 2001:db8:2::/64 fe80::78fe:fcff:fe51:9afc r4-eth0 00:09:13 - N E1 2001:db8:2::/64 fe80::78fe:fcff:fe51:9afc r4-eth0 00:09:13 + N E2 2001:db8:2::/64 fe80::78fe:fcff:fe51:9afc r4-eth0 00:09:13 *N IE 2001:db8:3::/64 fe80::78fe:fcff:fe51:9afc r4-eth0 00:09:13 - N E1 2001:db8:3::/64 fe80::78fe:fcff:fe51:9afc r4-eth0 00:09:13 - N E1 2001:db8:3::/64 fe80::78fe:fcff:fe51:9afc r4-eth0 00:09:13 + N E2 2001:db8:3::/64 fe80::78fe:fcff:fe51:9afc r4-eth0 00:09:13 + N E2 2001:db8:3::/64 fe80::78fe:fcff:fe51:9afc r4-eth0 00:09:13 *N IE 2001:db8:100::/64 fe80::78fe:fcff:fe51:9afc r4-eth0 00:09:13 - N E1 2001:db8:100::/64 fe80::78fe:fcff:fe51:9afc r4-eth0 00:09:13 + N E2 2001:db8:100::/64 fe80::78fe:fcff:fe51:9afc r4-eth0 00:09:13 *N IA 2001:db8:200::/64 :: r4-eth0 00:09:17 - N E1 2001:db8:200::/64 fe80::78fe:fcff:fe51:9afc r4-eth0 00:09:13 + N E2 2001:db8:200::/64 fe80::78fe:fcff:fe51:9afc r4-eth0 00:09:13 *N IA 2001:db8:300::/64 :: r4-eth1 00:09:18 diff --git a/tests/topotests/ospfv3_basic_functionality/ospfv3_rte_calc.json b/tests/topotests/ospfv3_basic_functionality/ospfv3_rte_calc.json new file mode 100644 index 0000000000..3669b3a554 --- /dev/null +++ b/tests/topotests/ospfv3_basic_functionality/ospfv3_rte_calc.json @@ -0,0 +1,173 @@ +{ + "feature": [ + "bgp" + ], + "address_types": [ + "ipv6" + ], + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv6": "2001:db8:f::", + "v6mask": 128 + }, + "routers": { + "r0": { + "links": { + "lo": { + "ipv6": "auto", + "type": "loopback" + }, + "r1": { + "ipv6": "auto", + "ospf6": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv6": "auto", + "ospf6": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv6": "auto", + "ospf6": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf6": { + "router_id": "100.1.1.0", + "neighbors": { + "r1": {}, + "r2": {} + } + } + }, + "r1": { + "links": { + "lo": { + "ipv6": "auto", + "type": "loopback" + }, + "r0": { + "ipv6": "auto", + "ospf6": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv6": "auto", + "ospf6": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv6": "auto", + "ospf6": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf6": { + "router_id": "100.1.1.1", + "neighbors": { + "r0": {}, + "r2": {}, + "r3": {} + } + } + }, + "r2": { + "links": { + "lo": { + "ipv6": "auto", + "type": "loopback" + }, + "r0": { + "ipv6": "auto", + "ospf6": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r1": { + "ipv6": "auto", + "ospf6": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv6": "auto", + "ospf6": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf6": { + "router_id": "100.1.1.2", + "neighbors": { + "r1": {}, + "r0": {}, + "r3": {} + } + } + }, + "r3": { + "links": { + "lo": { + "ipv6": "auto", + "type": "loopback" + }, + "r0": { + "ipv6": "auto" + }, + "r1": { + "ipv6": "auto", + "ospf6": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv6": "auto", + "ospf6": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf6": { + "router_id": "100.1.1.3", + "neighbors": { + "r1": {}, + "r2": {} + } + } + } + } +}
\ No newline at end of file diff --git a/tests/topotests/ospfv3_basic_functionality/ospfv3_single_area.json b/tests/topotests/ospfv3_basic_functionality/ospfv3_single_area.json new file mode 100644 index 0000000000..d93eb1f217 --- /dev/null +++ b/tests/topotests/ospfv3_basic_functionality/ospfv3_single_area.json @@ -0,0 +1,190 @@ +{ + "address_types": [ + "ipv6" + ], + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv6": "2001:db8:f::", + "v6mask": 128 + }, + "routers": { + "r0": { + "links": { + "lo": { + "ipv6": "auto", + "type": "loopback" + }, + "r1": { + "ipv6": "auto", + "ospf6": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv6": "auto", + "ospf6": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv6": "auto", + "ospf6": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-point" + } + } + }, + "ospf6": { + "router_id": "100.1.1.0", + "neighbors": { + "r1": {}, + "r2": {}, + "r3": {} + } + } + }, + "r1": { + "links": { + "lo": { + "ipv6": "auto", + "type": "loopback" + }, + "r0": { + "ipv6": "auto", + "ospf6": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv6": "auto", + "ospf6": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv6": "auto", + "ospf6": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3-link0": { + "ipv6": "auto", + "description": "DummyIntftoR3" + } + }, + "ospf6": { + "router_id": "100.1.1.1", + "neighbors": { + "r0": {}, + "r2": {}, + "r3": {} + } + } + }, + "r2": { + "links": { + "lo": { + "ipv6": "auto", + "type": "loopback" + }, + "r0": { + "ipv6": "auto", + "ospf6": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r1": { + "ipv6": "auto", + "ospf6": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv6": "auto", + "ospf6": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf6": { + "router_id": "100.1.1.2", + "neighbors": { + "r1": {}, + "r0": {}, + "r3": {} + } + } + }, + "r3": { + "links": { + "lo": { + "ipv6": "auto", + "type": "loopback" + }, + "r0": { + "ipv6": "auto", + "ospf6": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4, + "network": "point-to-point" + } + }, + "r1": { + "ipv6": "auto", + "ospf6": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv6": "auto", + "ospf6": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r1-link0": { + "ipv6": "auto", + "description": "DummyIntftoR1", + "ospf6": { + "area": "0.0.0.0" + } + } + }, + "ospf6": { + "router_id": "1.0.4.17", + "neighbors": { + "r0": {}, + "r1": {}, + "r2": {} + } + } + } + } +}
\ No newline at end of file diff --git a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_rte_calc.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_rte_calc.py new file mode 100644 index 0000000000..4aa71bfb16 --- /dev/null +++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_rte_calc.py @@ -0,0 +1,374 @@ +#!/usr/bin/python +# +# Copyright (c) 2021 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. +# ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + + +"""OSPF Basic Functionality Automation.""" +import os +import sys +import time +import pytest +import json +from copy import deepcopy +from ipaddress import IPv4Address +from lib.topotest import frr_unicode + +# 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 +from lib.bgp import verify_bgp_convergence, create_router_bgp + +# 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, + get_frr_ipv6_linklocal, +) + +from lib.topolog import logger +from lib.topojson import build_topo_from_json, build_config_from_json + +from lib.ospf import ( + verify_ospf6_neighbor, + config_ospf_interface, + clear_ospf, + verify_ospf6_rib, + create_router_ospf, + verify_ospf6_interface, + verify_ospf6_database, + config_ospf6_interface, +) + +from ipaddress import IPv6Address + +# Global variables +topo = None + +# Reading the data from JSON File for topology creation +jsonFile = "{}/ospfv3_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 = { + "ipv6": [ + "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": ["2::1/128", "2::2/128", "2::3/128", "2::4/128", "2::5/128"], +} +TOPOOLOGY = """ + Please view in a fixed-width font such as Courier. + +---+ A1 +---+ + +R1 +------------+R2 | + +-+-+- +--++ + | -- -- | + | -- A0 -- | + A0| ---- | + | ---- | A2 + | -- -- | + | -- -- | + +-+-+- +-+-+ + +R0 +-------------+R3 | + +---+ A3 +---+ +""" + +TESTCASES = """ +1. OSPF Cost - verifying ospf interface cost functionality +""" + + +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_ospf6_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 get_llip(onrouter, intf): + """ + API to get the link local ipv6 address of a perticular interface + + Parameters + ---------- + * `fromnode`: Source node + * `tonode` : interface for which link local ip needs to be returned. + + Usage + ----- + result = get_llip('r1', 'r2-link0') + + Returns + ------- + 1) link local ipv6 address from the interface. + 2) errormsg - when link local ip not found. + """ + tgen = get_topogen() + intf = topo["routers"][onrouter]["links"][intf]["interface"] + llip = get_frr_ipv6_linklocal(tgen, onrouter, intf) + if llip: + logger.info("llip ipv6 address to be set as NH is %s", llip) + return llip + return None + + +def get_glipv6(onrouter, intf): + """ + API to get the global ipv6 address of a perticular interface + + Parameters + ---------- + * `onrouter`: Source node + * `intf` : interface for which link local ip needs to be returned. + + Usage + ----- + result = get_glipv6('r1', 'r2-link0') + + Returns + ------- + 1) global ipv6 address from the interface. + 2) errormsg - when link local ip not found. + """ + glipv6 = (topo["routers"][onrouter]["links"][intf]["ipv6"]).split("/")[0] + if glipv6: + logger.info("Global ipv6 address to be set as NH is %s", glipv6) + return glipv6 + return None + + +def red_static(dut, config=True): + """Local def for Redstribute static routes inside ospf.""" + global topo + tgen = get_topogen() + if config: + ospf_red = {dut: {"ospf6": {"redistribute": [{"redist_type": "static"}]}}} + else: + ospf_red = { + dut: { + "ospf6": { + "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: {"ospf6": {"redistribute": [{"redist_type": "connected"}]}}} + else: + ospf_red = { + dut: { + "ospf6": { + "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_ospfv3_cost_tc52_p0(request): + """OSPF Cost - verifying ospf interface cost functionality""" + 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 cost as 20 on interface between R0 and R1. " + "Configure ospf cost as 30 between interface between R0 and R2." + ) + + r0_ospf_cost = { + "r0": {"links": {"r1": {"ospf6": {"cost": 20}}, "r2": {"ospf6": {"cost": 30}}}} + } + result = config_ospf6_interface(tgen, topo, r0_ospf_cost) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that cost is updated in the ospf interface between" + " r0 and r1 as 30 and r0 and r2 as 20" + ) + dut = "r0" + result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=r0_ospf_cost) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Swap the costs between interfaces on r0, between r0 and r1 to 30" + ", r0 and r2 to 20" + ) + + r0_ospf_cost = { + "r0": {"links": {"r1": {"ospf6": {"cost": 30}}, "r2": {"ospf6": {"cost": 20}}}} + } + result = config_ospf6_interface(tgen, topo, r0_ospf_cost) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that cost is updated in the ospf interface between r0 " + "and r1 as 30 and r0 and r2 as 20." + ) + result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=r0_ospf_cost) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step(" Un configure cost from the interface r0 - r1.") + + r0_ospf_cost = { + "r0": {"links": {"r1": {"ospf6": {"cost": 30, "del_action": True}}}} + } + result = config_ospf6_interface(tgen, topo, r0_ospf_cost) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + input_dict = { + "r0": {"links": {"r1": {"ospf6": {"cost": 10}}, "r2": {"ospf6": {"cost": 20}}}} + } + step( + "Verify that cost is updated in the ospf interface between r0" + " and r1 as 10 and r0 and r2 as 20." + ) + + result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step(" Un configure cost from the interface r0 - r2.") + + r0_ospf_cost = { + "r0": {"links": {"r2": {"ospf6": {"cost": 20, "del_action": True}}}} + } + result = config_ospf6_interface(tgen, topo, r0_ospf_cost) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that cost is updated in the ospf interface between r0" + "and r1 as 10 and r0 and r2 as 10" + ) + + input_dict = { + "r0": {"links": {"r1": {"ospf6": {"cost": 10}}, "r2": {"ospf6": {"cost": 10}}}} + } + result = verify_ospf6_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/ospfv3_basic_functionality/test_ospfv3_single_area.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_single_area.py new file mode 100644 index 0000000000..a84f1a1eb6 --- /dev/null +++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_single_area.py @@ -0,0 +1,417 @@ +#!/usr/bin/python + +# +# Copyright (c) 2021 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. +# ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + + +"""OSPF Basic Functionality Automation.""" +import os +import sys +import time +import pytest +import json +from copy import deepcopy +from ipaddress import IPv4Address +from lib.topotest import frr_unicode + +# 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_ospf6_neighbor, + config_ospf_interface, + clear_ospf, + verify_ospf6_rib, + create_router_ospf, + verify_ospf6_interface, + verify_ospf6_database, + config_ospf6_interface, +) + +from ipaddress import IPv6Address + +# Global variables +topo = None + +# Reading the data from JSON File for topology creation +jsonFile = "{}/ospfv3_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. +5. Verify NFSM events when ospf nbr changes with different MTU values. + """ + + +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_ospf6_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_ospfv3_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": {"ospf6": {"ospf6Enabled": True}}}}} + result = verify_ospf6_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": { + "ipv6": topo["routers"]["r0"]["links"]["r3"]["ipv6"], + "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"]["ipv6"] + topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv6"] = str( + IPv6Address(frr_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": { + "ospf6": { + "internetAddress": [ + { + "type": "inet6", + "address": topo_modify_change_ip["routers"]["r0"][ + "links" + ]["r3"]["ipv6"].split("/")[0], + } + ], + } + } + } + } + } + result = verify_ospf6_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"]["ipv6"] + mask = topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv6"] + step("Delete the ip address") + topo1 = { + "r0": { + "links": { + "r3": { + "ipv6": 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"]["ipv6"] + topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv6"] = str( + IPv6Address(frr_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": { + "ospf6": { + "internetAddress": [ + { + "type": "inet6", + "address": topo_modify_change_ip["routers"]["r0"][ + "links" + ]["r3"]["ipv6"].split("/")[0], + } + ], + } + } + } + } + } + result = verify_ospf6_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": { + "ipv6": topo_modify_change_ip["routers"]["r0"]["links"]["r3"][ + "ipv6" + ], + "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"], + "ospf6": {"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"], + "ospf6": {"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": {"ospf6": {"ospf6Enabled": True}}}}} + result = verify_ospf6_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"], + "ospf6": {"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"], + "ospf6": {"area": "0.0.0.0"}, + } + } + } + } + + 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": {"ospf6": {"ospf6Enabled": True}}}}} + result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("verify the all neighbors are up after clearing the process.") + for rtr in topo["routers"]: + clear_ospf(tgen, rtr, ospf="ospf6") + + ospf_covergence = verify_ospf6_neighbor(tgen, topo) + 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/tools/frr-reload.py b/tools/frr-reload.py index 448ab79ead..7cb8a2e729 100755 --- a/tools/frr-reload.py +++ b/tools/frr-reload.py @@ -595,10 +595,12 @@ end "ip ", "ipv6 ", "log ", + "mac access-list ", "mpls lsp", "mpls label", "no ", "password ", + "pbr ", "ptm-enable", "router-id ", "service ", diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index 6c3863132d..7af9148a8e 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -771,6 +771,7 @@ void vrrp_vty_init(void) install_node(&debug_node); install_node(&interface_node); install_node(&vrrp_node); + vrf_cmd_init(NULL, &vrrp_privs); if_cmd_init(); install_element(VIEW_NODE, &vrrp_vrid_show_cmd); diff --git a/vtysh/vtysh.h b/vtysh/vtysh.h index 87f1f67443..71f672554b 100644 --- a/vtysh/vtysh.h +++ b/vtysh/vtysh.h @@ -56,7 +56,7 @@ DECLARE_MGROUP(MVTYSH); #define VTYSH_ACL VTYSH_BFDD|VTYSH_BABELD|VTYSH_BGPD|VTYSH_EIGRPD|VTYSH_ISISD|VTYSH_FABRICD|VTYSH_LDPD|VTYSH_NHRPD|VTYSH_OSPF6D|VTYSH_OSPFD|VTYSH_PBRD|VTYSH_PIMD|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_VRRPD|VTYSH_ZEBRA #define VTYSH_RMAP VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_EIGRPD|VTYSH_FABRICD #define VTYSH_INTERFACE VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_PBRD|VTYSH_FABRICD|VTYSH_VRRPD -#define VTYSH_VRF VTYSH_ZEBRA|VTYSH_PIMD|VTYSH_STATICD +#define VTYSH_VRF VTYSH_INTERFACE|VTYSH_STATICD #define VTYSH_KEYS VTYSH_RIPD|VTYSH_EIGRPD /* Daemons who can process nexthop-group configs */ #define VTYSH_NH_GROUP VTYSH_PBRD|VTYSH_SHARPD diff --git a/yang/frr-pim.yang b/yang/frr-pim.yang index 70adb37b26..4c4819ac25 100644 --- a/yang/frr-pim.yang +++ b/yang/frr-pim.yang @@ -174,26 +174,36 @@ module frr-pim { "Enable ssmpingd operation."; } - container msdp-mesh-group { - presence - "Configure MSDP mesh-group."; + list msdp-mesh-groups { + key "name"; + description + "RFC 3618 Section 10.2. MSDP mesh-group semantics - leaf mesh-group-name { - type string; + Groups multiple MSDP peers to reduce SA flooding typically used + in intra-domain settings."; + + leaf name { + type string { + length 1..64; + } description - "MSDP mesh group name."; + "The mesh group name."; } - leaf-list member-ip { + leaf source { type inet:ip-address; description - "Peer ip address."; + "Source IP address for the TCP connections."; } - leaf source-ip { - type inet:ip-address; - description - "Source ip address for the TCP connection."; + list members { + key "address"; + + leaf address { + type inet:ip-address; + description + "Peer member IP address."; + } } } diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index 06aaa706dc..d5969ab9bb 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -2848,7 +2848,7 @@ stream_failure: static void zread_table_manager_request(ZAPI_HANDLER_ARGS) { - /* to avoid sending other messages like ZERBA_INTERFACE_UP */ + /* to avoid sending other messages like ZEBRA_INTERFACE_UP */ if (hdr->command == ZEBRA_TABLE_MANAGER_CONNECT) zread_table_manager_connect(client, msg, zvrf_id(zvrf)); else { @@ -2856,7 +2856,7 @@ static void zread_table_manager_request(ZAPI_HANDLER_ARGS) if (!client->proto) { flog_err( EC_ZEBRA_TM_ALIENS, - "Got table request from an unidentified client"); + "Got SRv6 request from an unidentified client"); return; } if (hdr->command == ZEBRA_GET_TABLE_CHUNK) diff --git a/zebra/zebra_mlag.c b/zebra/zebra_mlag.c index 3b0c75151b..40a2c94e2a 100644 --- a/zebra/zebra_mlag.c +++ b/zebra/zebra_mlag.c @@ -111,7 +111,7 @@ void zebra_mlag_process_mlag_data(uint8_t *data, uint32_t len) struct stream *s = NULL; int msg_type = 0; - s = stream_new(ZEBRA_MAX_PACKET_SIZ); + s = stream_new(ZEBRA_MLAG_BUF_LIMIT); /* * Place holder we need the message type first */ @@ -1081,7 +1081,7 @@ int zebra_mlag_protobuf_decode_message(struct stream *s, uint8_t *data, case ZEBRA_MLAG__HEADER__MESSAGE_TYPE__ZEBRA_MLAG_MROUTE_ADD_BULK: { ZebraMlagMrouteAddBulk *Bulk_msg = NULL; ZebraMlagMrouteAdd *msg = NULL; - size_t i; + size_t i, length_spot; Bulk_msg = zebra_mlag_mroute_add_bulk__unpack( NULL, hdr->data.len, hdr->data.data); @@ -1093,10 +1093,17 @@ int zebra_mlag_protobuf_decode_message(struct stream *s, uint8_t *data, stream_putw(s, (Bulk_msg->n_mroute_add * sizeof(struct mlag_mroute_add))); /* No. of msgs in Batch */ - stream_putw(s, Bulk_msg->n_mroute_add); + length_spot = stream_putw(s, Bulk_msg->n_mroute_add); /* Actual Data */ for (i = 0; i < Bulk_msg->n_mroute_add; i++) { + if (STREAM_SIZE(s) + < VRF_NAMSIZ + 22 + INTERFACE_NAMSIZ) { + zlog_warn( + "We have received more messages than we can parse at this point in time: %zu", + Bulk_msg->n_mroute_add); + break; + } msg = Bulk_msg->mroute_add[i]; @@ -1116,13 +1123,16 @@ int zebra_mlag_protobuf_decode_message(struct stream *s, uint8_t *data, else stream_put(s, NULL, INTERFACE_NAMSIZ); } + + stream_putw_at(s, length_spot, i + 1); + zebra_mlag_mroute_add_bulk__free_unpacked(Bulk_msg, NULL); } break; case ZEBRA_MLAG__HEADER__MESSAGE_TYPE__ZEBRA_MLAG_MROUTE_DEL_BULK: { ZebraMlagMrouteDelBulk *Bulk_msg = NULL; ZebraMlagMrouteDel *msg = NULL; - size_t i; + size_t i, length_spot; Bulk_msg = zebra_mlag_mroute_del_bulk__unpack( NULL, hdr->data.len, hdr->data.data); @@ -1134,10 +1144,16 @@ int zebra_mlag_protobuf_decode_message(struct stream *s, uint8_t *data, stream_putw(s, (Bulk_msg->n_mroute_del * sizeof(struct mlag_mroute_del))); /* No. of msgs in Batch */ - stream_putw(s, Bulk_msg->n_mroute_del); + length_spot = stream_putw(s, Bulk_msg->n_mroute_del); /* Actual Data */ for (i = 0; i < Bulk_msg->n_mroute_del; i++) { + if (STREAM_SIZE(s) + < VRF_NAMSIZ + 16 + INTERFACE_NAMSIZ) { + zlog_warn( + "We have received more messages than we can parse at this time"); + break; + } msg = Bulk_msg->mroute_del[i]; @@ -1154,6 +1170,9 @@ int zebra_mlag_protobuf_decode_message(struct stream *s, uint8_t *data, else stream_put(s, NULL, INTERFACE_NAMSIZ); } + + stream_putw_at(s, length_spot, i + 1); + zebra_mlag_mroute_del_bulk__free_unpacked(Bulk_msg, NULL); } break; diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c index a4382441c8..41d55c2e6c 100644 --- a/zebra/zebra_rnh.c +++ b/zebra/zebra_rnh.c @@ -1367,7 +1367,6 @@ int zebra_send_rnh_update(struct rnh *rnh, struct zserv *client, stream_putw_at(s, 0, stream_get_endp(s)); client->nh_last_upd_time = monotime(NULL); - client->last_write_cmd = cmd; return zserv_send_message(client, s); failure: diff --git a/zebra/zebra_srte.c b/zebra/zebra_srte.c index 98158ecc12..6dd60af9fb 100644 --- a/zebra/zebra_srte.c +++ b/zebra/zebra_srte.c @@ -161,7 +161,6 @@ static int zebra_sr_policy_notify_update_client(struct zebra_sr_policy *policy, stream_putw_at(s, 0, stream_get_endp(s)); client->nh_last_upd_time = monotime(NULL); - client->last_write_cmd = ZEBRA_NEXTHOP_UPDATE; return zserv_send_message(client, s); failure: diff --git a/zebra/zebra_srv6.c b/zebra/zebra_srv6.c index 5664a29682..b11331a180 100644 --- a/zebra/zebra_srv6.c +++ b/zebra/zebra_srv6.c @@ -181,13 +181,13 @@ assign_srv6_locator_chunk(uint8_t proto, loc->status_up = false; chunk = srv6_locator_chunk_alloc(); - chunk->proto = 0; + chunk->proto = NO_PROTO; listnode_add(loc->chunks, chunk); zebra_srv6_locator_add(loc); } for (ALL_LIST_ELEMENTS_RO((struct list *)loc->chunks, node, chunk)) { - if (chunk->proto != 0 && chunk->proto != proto) + if (chunk->proto != NO_PROTO && chunk->proto != proto) continue; chunk_found = true; break; @@ -199,6 +199,8 @@ assign_srv6_locator_chunk(uint8_t proto, } chunk->proto = proto; + chunk->instance = instance; + chunk->session_id = session_id; return loc; } diff --git a/zebra/zserv.c b/zebra/zserv.c index 0bf4d8ece2..1d94fcae6b 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -1070,8 +1070,14 @@ static void zebra_show_client_detail(struct vty *vty, struct zserv *client) 0, client->redist_v4_del_cnt); vty_out(vty, "Redist:v6 %-12u%-12u%-12u\n", client->redist_v6_add_cnt, 0, client->redist_v6_del_cnt); + vty_out(vty, "VRF %-12u%-12u%-12u\n", client->vrfadd_cnt, 0, + client->vrfdel_cnt); vty_out(vty, "Connected %-12u%-12u%-12u\n", client->ifadd_cnt, 0, client->ifdel_cnt); + vty_out(vty, "Interface %-12u%-12u%-12u\n", client->ifup_cnt, 0, + client->ifdown_cnt); + vty_out(vty, "Intf Addr %-12u%-12u%-12u\n", + client->connected_rt_add_cnt, 0, client->connected_rt_del_cnt); vty_out(vty, "BFD peer %-12u%-12u%-12u\n", client->bfd_peer_add_cnt, client->bfd_peer_upd8_cnt, client->bfd_peer_del_cnt); vty_out(vty, "NHT v4 %-12u%-12u%-12u\n", @@ -1080,20 +1086,17 @@ static void zebra_show_client_detail(struct vty *vty, struct zserv *client) client->v6_nh_watch_add_cnt, 0, client->v6_nh_watch_rem_cnt); vty_out(vty, "VxLAN SG %-12u%-12u%-12u\n", client->vxlan_sg_add_cnt, 0, client->vxlan_sg_del_cnt); - vty_out(vty, "Interface Up Notifications: %u\n", client->ifup_cnt); - vty_out(vty, "Interface Down Notifications: %u\n", client->ifdown_cnt); - vty_out(vty, "VNI add notifications: %u\n", client->vniadd_cnt); - vty_out(vty, "VNI delete notifications: %u\n", client->vnidel_cnt); - vty_out(vty, "L3-VNI add notifications: %u\n", client->l3vniadd_cnt); - vty_out(vty, "L3-VNI delete notifications: %u\n", client->l3vnidel_cnt); - vty_out(vty, "MAC-IP add notifications: %u\n", client->macipadd_cnt); - vty_out(vty, "MAC-IP delete notifications: %u\n", client->macipdel_cnt); - vty_out(vty, "ES add notifications: %u\n", client->local_es_add_cnt); - vty_out(vty, "ES delete notifications: %u\n", client->local_es_del_cnt); - vty_out(vty, "ES-EVI add notifications: %u\n", - client->local_es_evi_add_cnt); - vty_out(vty, "ES-EVI delete notifications: %u\n", - client->local_es_evi_del_cnt); + vty_out(vty, "VNI %-12u%-12u%-12u\n", client->vniadd_cnt, 0, + client->vnidel_cnt); + vty_out(vty, "L3-VNI %-12u%-12u%-12u\n", client->l3vniadd_cnt, 0, + client->l3vnidel_cnt); + vty_out(vty, "MAC-IP %-12u%-12u%-12u\n", client->macipadd_cnt, 0, + client->macipdel_cnt); + vty_out(vty, "ES %-12u%-12u%-12u\n", client->local_es_add_cnt, + 0, client->local_es_del_cnt); + vty_out(vty, "ES-EVI %-12u%-12u%-12u\n", + client->local_es_evi_add_cnt, 0, client->local_es_evi_del_cnt); + vty_out(vty, "Errors: %u\n", client->error_cnt); TAILQ_FOREACH (info, &client->gr_info_queue, gr_info) { vty_out(vty, "VRF : %s\n", vrf_id_to_name(info->vrf_id)); |
