diff options
504 files changed, 58923 insertions, 4750 deletions
diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000000..e0ea542fd2 --- /dev/null +++ b/.flake8 @@ -0,0 +1,3 @@ +[flake8] +max-line-length = 88 +extend-ignore = E203
\ No newline at end of file diff --git a/.github/workflows/conflicts.yml b/.github/workflows/conflicts.yml index 3f51e7f4de..100f557e02 100644 --- a/.github/workflows/conflicts.yml +++ b/.github/workflows/conflicts.yml @@ -1,4 +1,4 @@ -name: Add a conflict label is PR needs to rebase +name: Add a conflict label if PR needs to rebase on: push: diff --git a/.gitignore b/.gitignore index fb40ee52fe..3dd6a44409 100644 --- a/.gitignore +++ b/.gitignore @@ -87,6 +87,7 @@ {arch} build .cache +.dir-locals.el .msg .rebase-* *~ diff --git a/.isort.cfg b/.isort.cfg new file mode 100644 index 0000000000..f238bf7ea1 --- /dev/null +++ b/.isort.cfg @@ -0,0 +1,2 @@ +[settings] +profile = black @@ -2,5 +2,8 @@ init-hook="import sys; sys.path.insert(0, '..')" signature-mutators=common_config.retry,retry +[FORMAT] +max-line-length = 88 + [MESSAGES CONTROL] disable=I,C,R,W diff --git a/alpine/APKBUILD.in b/alpine/APKBUILD.in index fef7a61ccc..fd3c02f47e 100644 --- a/alpine/APKBUILD.in +++ b/alpine/APKBUILD.in @@ -18,7 +18,8 @@ makedepends="ncurses-dev net-snmp-dev gawk texinfo perl ncurses-libs ncurses-terminfo ncurses-terminfo-base patch pax-utils pcre2 perl pkgconf python3 python3-dev readline readline-dev sqlite-libs pcre2-dev squashfs-tools sudo tar texinfo xorriso xz-libs py-pip rtrlib rtrlib-dev - py3-sphinx elfutils elfutils-dev libyang-dev" + py3-sphinx elfutils elfutils-dev libyang-dev protobuf-c-compiler protobuf-c-dev + lua5.3-dev lua5.3" checkdepends="pytest py-setuptools" install="$pkgname.pre-install $pkgname.pre-deinstall $pkgname.post-deinstall" subpackages="$pkgname-dev $pkgname-doc $pkgname-dbg" @@ -47,7 +48,8 @@ build() { --enable-vty-group=frrvty \ --enable-user=$_user \ --enable-group=$_user \ - --enable-pcre2posix + --enable-pcre2posix \ + --enable-scripting make -j $(nproc) } diff --git a/babeld/babeld.c b/babeld/babeld.c index 4ce92c5204..f4c932971e 100644 --- a/babeld/babeld.c +++ b/babeld/babeld.c @@ -444,37 +444,39 @@ babel_fill_with_next_timeout(struct timeval *tv) #if (defined NO_DEBUG) #define printIfMin(a,b,c,d) #else -#define printIfMin(a,b,c,d) \ - if (UNLIKELY(debug & BABEL_DEBUG_TIMEOUT)) {printIfMin(a,b,c,d);} - - struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); - struct interface *ifp = NULL; +#define printIfMin(a, b, c, d) \ + if (unlikely(debug & BABEL_DEBUG_TIMEOUT)) { \ + printIfMin(a, b, c, d); \ + } - *tv = check_neighbours_timeout; - printIfMin(tv, 0, "check_neighbours_timeout", NULL); - timeval_min_sec(tv, expiry_time); - printIfMin(tv, 1, "expiry_time", NULL); - timeval_min_sec(tv, source_expiry_time); - printIfMin(tv, 1, "source_expiry_time", NULL); - timeval_min(tv, &resend_time); - printIfMin(tv, 1, "resend_time", NULL); - FOR_ALL_INTERFACES(vrf, ifp) { - babel_interface_nfo *babel_ifp = NULL; - if(!if_up(ifp)) - continue; - babel_ifp = babel_get_if_nfo(ifp); - timeval_min(tv, &babel_ifp->flush_timeout); - printIfMin(tv, 1, "flush_timeout", ifp->name); - timeval_min(tv, &babel_ifp->hello_timeout); - printIfMin(tv, 1, "hello_timeout", ifp->name); - timeval_min(tv, &babel_ifp->update_timeout); - printIfMin(tv, 1, "update_timeout", ifp->name); - timeval_min(tv, &babel_ifp->update_flush_timeout); - printIfMin(tv, 1, "update_flush_timeout",ifp->name); - } - timeval_min(tv, &unicast_flush_timeout); - printIfMin(tv, 1, "unicast_flush_timeout", NULL); - printIfMin(tv, 2, NULL, NULL); + struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); + struct interface *ifp = NULL; + + *tv = check_neighbours_timeout; + printIfMin(tv, 0, "check_neighbours_timeout", NULL); + timeval_min_sec(tv, expiry_time); + printIfMin(tv, 1, "expiry_time", NULL); + timeval_min_sec(tv, source_expiry_time); + printIfMin(tv, 1, "source_expiry_time", NULL); + timeval_min(tv, &resend_time); + printIfMin(tv, 1, "resend_time", NULL); + FOR_ALL_INTERFACES (vrf, ifp) { + babel_interface_nfo *babel_ifp = NULL; + if (!if_up(ifp)) + continue; + babel_ifp = babel_get_if_nfo(ifp); + timeval_min(tv, &babel_ifp->flush_timeout); + printIfMin(tv, 1, "flush_timeout", ifp->name); + timeval_min(tv, &babel_ifp->hello_timeout); + printIfMin(tv, 1, "hello_timeout", ifp->name); + timeval_min(tv, &babel_ifp->update_timeout); + printIfMin(tv, 1, "update_timeout", ifp->name); + timeval_min(tv, &babel_ifp->update_flush_timeout); + printIfMin(tv, 1, "update_flush_timeout", ifp->name); + } + timeval_min(tv, &unicast_flush_timeout); + printIfMin(tv, 1, "unicast_flush_timeout", NULL); + printIfMin(tv, 2, NULL, NULL); #undef printIfMin #endif } diff --git a/babeld/babeld.h b/babeld/babeld.h index 6c51af48a8..619550f651 100644 --- a/babeld/babeld.h +++ b/babeld/babeld.h @@ -26,12 +26,8 @@ Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek #if defined(__GNUC__) && (__GNUC__ >= 3) #define ATTRIBUTE(x) __attribute__ (x) -#define LIKELY(_x) __builtin_expect(!!(_x), 1) -#define UNLIKELY(_x) __builtin_expect(!!(_x), 0) #else #define ATTRIBUTE(x) /**/ -#define LIKELY(_x) !!(_x) -#define UNLIKELY(_x) !!(_x) #endif #if defined(__GNUC__) && (__GNUC__ >= 4) && (__GNUC_MINOR__ >= 3) diff --git a/babeld/util.h b/babeld/util.h index 5e5843543a..8535d4dd6a 100644 --- a/babeld/util.h +++ b/babeld/util.h @@ -122,10 +122,11 @@ extern const unsigned char v4prefix[16]; #define BABEL_DEBUG_ROUTE (1 << 5) #define BABEL_DEBUG_ALL (0xFFFF) -#define debugf(level, ...) \ -do { \ -if(UNLIKELY(debug & level)) zlog_debug(__VA_ARGS__); \ -} while(0) +#define debugf(level, ...) \ + do { \ + if (unlikely(debug & level)) \ + zlog_debug(__VA_ARGS__); \ + } while (0) #endif /* NO_DEBUG */ diff --git a/bgpd/bgp_bfd.c b/bgpd/bgp_bfd.c index a51bcb1a1a..d1ddfd0460 100644 --- a/bgpd/bgp_bfd.c +++ b/bgpd/bgp_bfd.c @@ -55,7 +55,7 @@ static void bfd_session_status_update(struct bfd_session_params *bsp, } peer->last_reset = PEER_DOWN_BFD_DOWN; - /* draft-ietf-idr-bfd-subcode */ + /* rfc9384 */ if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) bgp_notify_send(peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_BFD_DOWN); diff --git a/bgpd/bgp_bmp.c b/bgpd/bgp_bmp.c index 73dbf4ab2b..baf164679c 100644 --- a/bgpd/bgp_bmp.c +++ b/bgpd/bgp_bmp.c @@ -389,13 +389,13 @@ static struct stream *bmp_peerstate(struct peer *peer, bool down) /* Local Port, Remote Port */ if (peer->su_local->sa.sa_family == AF_INET6) - stream_putw(s, peer->su_local->sin6.sin6_port); + stream_putw(s, htons(peer->su_local->sin6.sin6_port)); else if (peer->su_local->sa.sa_family == AF_INET) - stream_putw(s, peer->su_local->sin.sin_port); + stream_putw(s, htons(peer->su_local->sin.sin_port)); if (peer->su_remote->sa.sa_family == AF_INET6) - stream_putw(s, peer->su_remote->sin6.sin6_port); + stream_putw(s, htons(peer->su_remote->sin6.sin6_port)); else if (peer->su_remote->sa.sa_family == AF_INET) - stream_putw(s, peer->su_remote->sin.sin_port); + stream_putw(s, htons(peer->su_remote->sin.sin_port)); static const uint8_t dummy_open[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c index 541da9ecd6..dc1905868b 100644 --- a/bgpd/bgp_ecommunity.c +++ b/bgpd/bgp_ecommunity.c @@ -503,6 +503,8 @@ static const char *ecommunity_gettoken(const char *str, uint8_t ecomm_type; char buf[INET_ADDRSTRLEN + 1]; struct ecommunity_val *eval = (struct ecommunity_val *)eval_ptr; + uint64_t tmp_as = 0; + /* Skip white space. */ while (isspace((unsigned char)*p)) { p++; @@ -581,9 +583,18 @@ static const char *ecommunity_gettoken(const char *str, goto error; endptr++; - as = strtoul(endptr, &endptr, 10); - if (*endptr != '\0' || as == BGP_AS4_MAX) + errno = 0; + tmp_as = strtoul(endptr, &endptr, 10); + /* 'unsigned long' is a uint64 on 64-bit + * systems, and uint32 on 32-bit systems. So for + * 64-bit we can just directly check the value + * against BGP_AS4_MAX/UINT32_MAX, and for + * 32-bit we can check for errno (set to ERANGE + * upon overflow). + */ + if (*endptr != '\0' || tmp_as == BGP_AS4_MAX || errno) goto error; + as = (as_t)tmp_as; memcpy(buf, p, (limit - p)); buf[limit - p] = '\0'; @@ -625,9 +636,19 @@ static const char *ecommunity_gettoken(const char *str, goto error; } else { /* ASN */ - as = strtoul(buf, &endptr, 10); - if (*endptr != '\0' || as == BGP_AS4_MAX) + errno = 0; + tmp_as = strtoul(buf, &endptr, 10); + /* 'unsigned long' is a uint64 on 64-bit + * systems, and uint32 on 32-bit systems. So for + * 64-bit we can just directly check the value + * against BGP_AS4_MAX/UINT32_MAX, and for + * 32-bit we can check for errno (set to ERANGE + * upon overflow). + */ + if (*endptr != '\0' || tmp_as > BGP_AS4_MAX || + errno) goto error; + as = (as_t)tmp_as; } } else if (*p == '.') { if (separator) diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index 78e96d130c..159cb33fee 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -1513,14 +1513,9 @@ static int update_evpn_type5_route_entry(struct bgp *bgp_evpn, struct bgp_path_info *tmp_pi = NULL; *route_changed = 0; - /* locate the local route entry if any */ - for (tmp_pi = bgp_dest_get_bgp_path_info(dest); tmp_pi; - tmp_pi = tmp_pi->next) { - if (tmp_pi->peer == bgp_evpn->peer_self - && tmp_pi->type == ZEBRA_ROUTE_BGP - && tmp_pi->sub_type == BGP_ROUTE_STATIC) - local_pi = tmp_pi; - } + + /* See if this is an update of an existing route, or a new add. */ + local_pi = bgp_evpn_route_get_local_path(bgp_evpn, dest); /* * create a new route entry if one doesn't exist. @@ -5679,7 +5674,7 @@ void bgp_evpn_encode_prefix(struct stream *s, const struct prefix *p, } int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr, - struct bgp_nlri *packet, int withdraw) + struct bgp_nlri *packet, bool withdraw) { uint8_t *pnt; uint8_t *lim; @@ -6235,6 +6230,14 @@ int bgp_evpn_local_l3vni_add(vni_t l3vni, vrf_id_t vrf_id, l3vni); return -1; } + + if (CHECK_FLAG(bgp_evpn->flags, BGP_FLAG_DELETE_IN_PROGRESS)) { + flog_err(EC_BGP_NO_DFLT, + "Cannot process L3VNI %u ADD - EVPN BGP instance is shutting down", + l3vni); + return -1; + } + as = bgp_evpn->as; /* if the BGP vrf instance doesn't exist - create one */ @@ -6377,6 +6380,13 @@ int bgp_evpn_local_l3vni_del(vni_t l3vni, vrf_id_t vrf_id) return -1; } + if (CHECK_FLAG(bgp_evpn->flags, BGP_FLAG_DELETE_IN_PROGRESS)) { + flog_err(EC_BGP_NO_DFLT, + "Cannot process L3VNI %u ADD - EVPN BGP instance is shutting down", + l3vni); + return -1; + } + /* Remove remote routes from BGT VRF even if BGP_VRF_AUTO is configured, * bgp_delete would not remove/decrement bgp_path_info of the ip_prefix * routes. This will uninstalling the routes from zebra and decremnt the diff --git a/bgpd/bgp_evpn.h b/bgpd/bgp_evpn.h index 1dce4820d6..a034bfbd7e 100644 --- a/bgpd/bgp_evpn.h +++ b/bgpd/bgp_evpn.h @@ -150,7 +150,7 @@ extern void bgp_evpn_encode_prefix(struct stream *s, const struct prefix *p, struct attr *attr, bool addpath_capable, uint32_t addpath_tx_id); extern int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr, - struct bgp_nlri *packet, int withdraw); + struct bgp_nlri *packet, bool withdraw); extern int bgp_evpn_import_route(struct bgp *bgp, afi_t afi, safi_t safi, const struct prefix *p, struct bgp_path_info *ri); diff --git a/bgpd/bgp_evpn_mh.c b/bgpd/bgp_evpn_mh.c index d821d4d582..efadda17b8 100644 --- a/bgpd/bgp_evpn_mh.c +++ b/bgpd/bgp_evpn_mh.c @@ -1071,7 +1071,8 @@ void update_type1_routes_for_evi(struct bgp *bgp, struct bgpevpn *vpn) continue; /* Update EAD-ES */ - bgp_evpn_ead_es_route_update(bgp, es); + if (bgp_evpn_local_es_is_active(es)) + bgp_evpn_ead_es_route_update(bgp, es); /* Update EAD-EVI */ if (CHECK_FLAG(es->flags, BGP_EVPNES_ADV_EVI)) { @@ -2457,6 +2458,7 @@ static void bgp_evpn_es_json_vtep_fill(json_object *json_vteps, { json_object *json_vtep_entry; json_object *json_flags; + char alg_buf[EVPN_DF_ALG_STR_LEN]; json_vtep_entry = json_object_new_object(); @@ -2473,8 +2475,10 @@ static void bgp_evpn_es_json_vtep_fill(json_object *json_vteps, if (es_vtep->flags & BGP_EVPNES_VTEP_ESR) { json_object_int_add(json_vtep_entry, "dfPreference", es_vtep->df_pref); - json_object_int_add(json_vtep_entry, "dfAlgorithm", - es_vtep->df_pref); + json_object_string_add( + json_vtep_entry, "dfAlgorithm", + evpn_es_df_alg2str(es_vtep->df_alg, alg_buf, + sizeof(alg_buf))); } } @@ -2608,6 +2612,9 @@ static void bgp_evpn_es_show_entry_detail(struct vty *vty, listcount(es->macip_global_path_list)); json_object_int_add(json, "inconsistentVniVtepCount", es->incons_evi_vtep_cnt); + if (es->flags & BGP_EVPNES_LOCAL) + json_object_int_add(json, "localEsDfPreference", + es->df_pref); if (listcount(es->es_vtep_list)) { json_vteps = json_object_new_array(); for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list, node, diff --git a/bgpd/bgp_flowspec.c b/bgpd/bgp_flowspec.c index f9debe43cd..70bdbaf035 100644 --- a/bgpd/bgp_flowspec.c +++ b/bgpd/bgp_flowspec.c @@ -82,7 +82,7 @@ static int bgp_fs_nlri_validate(uint8_t *nlri_content, uint32_t len, } int bgp_nlri_parse_flowspec(struct peer *peer, struct attr *attr, - struct bgp_nlri *packet, int withdraw) + struct bgp_nlri *packet, bool withdraw) { uint8_t *pnt; uint8_t *lim; @@ -98,6 +98,13 @@ int bgp_nlri_parse_flowspec(struct peer *peer, struct attr *attr, afi = packet->afi; safi = packet->safi; + /* + * All other AFI/SAFI's treat no attribute as a implicit + * withdraw. Flowspec should as well. + */ + if (!attr) + withdraw = true; + if (packet->length >= FLOWSPEC_NLRI_SIZELIMIT_EXTENDED) { flog_err(EC_BGP_FLOWSPEC_PACKET, "BGP flowspec nlri length maximum reached (%u)", diff --git a/bgpd/bgp_flowspec.h b/bgpd/bgp_flowspec.h index 58fc1775ab..bc2f00511e 100644 --- a/bgpd/bgp_flowspec.h +++ b/bgpd/bgp_flowspec.h @@ -15,7 +15,7 @@ #define BGP_FLOWSPEC_NLRI_STRING_MAX 512 extern int bgp_nlri_parse_flowspec(struct peer *peer, struct attr *attr, - struct bgp_nlri *packet, int withdraw); + struct bgp_nlri *packet, bool withdraw); extern void bgp_flowspec_vty_init(void); diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index c08f95cdf4..63168f1e7a 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -1103,7 +1103,7 @@ leak_update(struct bgp *to_bgp, struct bgp_dest *bn, struct bgp_path_info *new; struct bgp_path_info_extra *extra; uint32_t num_sids = 0; - void *parent = source_bpi; + struct bgp_path_info *parent = source_bpi; if (new_attr->srv6_l3vpn || new_attr->srv6_vpn) num_sids = 1; @@ -1311,7 +1311,7 @@ leak_update(struct bgp *to_bgp, struct bgp_dest *bn, new->extra->parent = bgp_path_info_lock(parent); bgp_dest_lock_node( - (struct bgp_dest *)((struct bgp_path_info *)parent)->net); + (struct bgp_dest *)parent->net); if (bgp_orig) new->extra->bgp_orig = bgp_lock(bgp_orig); if (nexthop_orig) diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index 1c76828a2f..9469a0778f 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -331,7 +331,7 @@ static void bgp_update_explicit_eors(struct peer *peer) * calling safi function and for evpn, passed as parameter */ int bgp_nlri_parse(struct peer *peer, struct attr *attr, - struct bgp_nlri *packet, int mp_withdraw) + struct bgp_nlri *packet, bool mp_withdraw) { switch (packet->safi) { case SAFI_UNICAST: diff --git a/bgpd/bgp_packet.h b/bgpd/bgp_packet.h index c072bbc235..04bdb81849 100644 --- a/bgpd/bgp_packet.h +++ b/bgpd/bgp_packet.h @@ -42,26 +42,28 @@ DECLARE_HOOK(bgp_packet_send, } while (0) /* Packet send and receive function prototypes. */ -extern void bgp_keepalive_send(struct peer *); -extern void bgp_open_send(struct peer *); -extern void bgp_notify_send(struct peer *, uint8_t, uint8_t); -extern void bgp_notify_send_with_data(struct peer *, uint8_t, uint8_t, - uint8_t *, size_t); +extern void bgp_keepalive_send(struct peer *peer); +extern void bgp_open_send(struct peer *peer); +extern void bgp_notify_send(struct peer *peer, uint8_t code, uint8_t sub_code); +extern void bgp_notify_send_with_data(struct peer *peer, uint8_t code, + uint8_t sub_code, uint8_t *data, + size_t datalen); void bgp_notify_io_invalid(struct peer *peer, uint8_t code, uint8_t sub_code, uint8_t *data, size_t datalen); extern void bgp_route_refresh_send(struct peer *peer, afi_t afi, safi_t safi, uint8_t orf_type, uint8_t when_to_refresh, int remove, uint8_t subtype); -extern void bgp_capability_send(struct peer *, afi_t, safi_t, int, int); +extern void bgp_capability_send(struct peer *peer, afi_t afi, safi_t safi, + int capabilty_code, int action); -extern int bgp_capability_receive(struct peer *, bgp_size_t); +extern int bgp_capability_receive(struct peer *peer, bgp_size_t length); -extern int bgp_nlri_parse(struct peer *, struct attr *, struct bgp_nlri *, - int mp_withdraw); +extern int bgp_nlri_parse(struct peer *peer, struct attr *attr, + struct bgp_nlri *nlri, bool mp_withdraw); -extern void bgp_update_restarted_peers(struct peer *); -extern void bgp_update_implicit_eors(struct peer *); -extern void bgp_check_update_delay(struct bgp *); +extern void bgp_update_restarted_peers(struct peer *peer); +extern void bgp_update_implicit_eors(struct peer *peer); +extern void bgp_check_update_delay(struct bgp *peer); extern int bgp_packet_set_marker(struct stream *s, uint8_t type); extern void bgp_packet_set_size(struct stream *s); diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index a23bffd4ae..7809d9b0a9 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -3070,7 +3070,9 @@ static void bgp_process_evpn_route_injection(struct bgp *bgp, afi_t afi, * the IMPLICIT_NULL label. This is pretty specialized: it's only called * in a path where we basically _know_ this is a BGP-LU route. */ -static bool bgp_lu_need_imp_null(const struct bgp_path_info *new_select) +static bool bgp_lu_need_null_label(struct bgp *bgp, + const struct bgp_path_info *new_select, + afi_t afi, mpls_label_t *label) { /* Certain types get imp null; so do paths where the nexthop is * not labeled. @@ -3078,13 +3080,22 @@ static bool bgp_lu_need_imp_null(const struct bgp_path_info *new_select) if (new_select->sub_type == BGP_ROUTE_STATIC || new_select->sub_type == BGP_ROUTE_AGGREGATE || new_select->sub_type == BGP_ROUTE_REDISTRIBUTE) + goto need_null_label; + else if (new_select->extra && + bgp_is_valid_label(&new_select->extra->label[0])) + return false; +need_null_label: + if (label == NULL) return true; - else if (new_select->extra == NULL || - !bgp_is_valid_label(&new_select->extra->label[0])) - /* TODO -- should be configurable? */ - return true; + if (!!CHECK_FLAG(bgp->flags, BGP_FLAG_LU_EXPLICIT_NULL)) + /* Disable PHP : explicit-null */ + *label = afi == AFI_IP ? MPLS_LABEL_IPV4_EXPLICIT_NULL + : MPLS_LABEL_IPV6_EXPLICIT_NULL; else - return false; + /* Enforced PHP popping: implicit-null */ + *label = MPLS_LABEL_IMPLICIT_NULL; + + return true; } /* @@ -3113,6 +3124,7 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_dest *dest, struct bgp_path_info *old_select; struct bgp_path_info_pair old_and_new; int debug = 0; + mpls_label_t mpls_label_null; if (CHECK_FLAG(bgp->flags, BGP_FLAG_DELETE_IN_PROGRESS)) { if (dest) @@ -3167,7 +3179,7 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_dest *dest, * Right now, since we only deal with per-prefix labels, it is not * necessary to do this upon changes to best path. Exceptions: * - label index has changed -> recalculate resulting label - * - path_info sub_type changed -> switch to/from implicit-null + * - path_info sub_type changed -> switch to/from null label value * - no valid label (due to removed static label binding) -> get new one */ if (bgp->allocate_mpls_labels[afi][safi]) { @@ -3176,11 +3188,12 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_dest *dest, || bgp_label_index_differs(new_select, old_select) || new_select->sub_type != old_select->sub_type || !bgp_is_valid_label(&dest->local_label)) { - /* Enforced penultimate hop popping: - * implicit-null for local routes, aggregate - * and redistributed routes + /* control label imposition for local routes, + * aggregate and redistributed routes */ - if (bgp_lu_need_imp_null(new_select)) { + mpls_label_null = MPLS_LABEL_IMPLICIT_NULL; + if (bgp_lu_need_null_label(bgp, new_select, afi, + &mpls_label_null)) { if (CHECK_FLAG( dest->flags, BGP_NODE_REGISTERED_FOR_LABEL) @@ -3189,8 +3202,7 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_dest *dest, BGP_NODE_LABEL_REQUESTED)) bgp_unregister_for_label(dest); dest->local_label = mpls_lse_encode( - MPLS_LABEL_IMPLICIT_NULL, 0, 0, - 1); + mpls_label_null, 0, 0, 1); bgp_set_valid_label(&dest->local_label); } else bgp_register_for_label(dest, @@ -4053,17 +4065,24 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, if (has_valid_label) assert(label != NULL); - /* Update overlay index of the attribute */ - if (afi == AFI_L2VPN && evpn) - memcpy(&attr->evpn_overlay, evpn, - sizeof(struct bgp_route_evpn)); /* When peer's soft reconfiguration enabled. Record input packet in Adj-RIBs-In. */ - if (!soft_reconfig - && CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_SOFT_RECONFIG) - && peer != bgp->peer_self) + if (!soft_reconfig && + CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_SOFT_RECONFIG) && + peer != bgp->peer_self) { + /* + * If the trigger is not from soft_reconfig and if + * PEER_FLAG_SOFT_RECONFIG is enabled for the peer, then attr + * will not be interned. In which case, it is ok to update the + * attr->evpn_overlay, so that, this can be stored in adj_in. + */ + if ((afi == AFI_L2VPN) && evpn) { + memcpy(&attr->evpn_overlay, evpn, + sizeof(struct bgp_route_evpn)); + } bgp_adj_in_set(dest, peer, attr, addpath_id); + } /* Update permitted loop count */ if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_ALLOWAS_IN)) @@ -4191,6 +4210,15 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, } new_attr = *attr; + /* + * If bgp_update is called with soft_reconfig set then + * attr is interned. In this case, do not overwrite the + * attr->evpn_overlay with evpn directly. Instead memcpy + * evpn to new_atr.evpn_overlay before it is interned. + */ + if (soft_reconfig && (afi == AFI_L2VPN) && evpn) + memcpy(&new_attr.evpn_overlay, evpn, + sizeof(struct bgp_route_evpn)); /* Apply incoming route-map. * NB: new_attr may now contain newly allocated values from route-map @@ -11892,12 +11920,14 @@ void route_vty_out_detail_header(struct vty *vty, struct bgp *bgp, vty_out(vty, "BGP routing table entry for %s%s%pFX, version %" PRIu64 "\n", - ((safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP) + (((safi == SAFI_MPLS_VPN || + safi == SAFI_ENCAP) && + prd) ? prefix_rd2str(prd, buf1, sizeof(buf1), bgp->asnotation) : ""), - safi == SAFI_MPLS_VPN ? ":" : "", p, + safi == SAFI_MPLS_VPN && prd ? ":" : "", p, dest->version); } else { diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index 4faf4a9f15..32ecb083be 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -461,7 +461,8 @@ route_match_ip_address(void *rule, const struct prefix *prefix, void *object) if (prefix->family == AF_INET) { alist = access_list_lookup(AFI_IP, (char *)rule); if (alist == NULL) { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + if (unlikely(CHECK_FLAG(rmap_debug, + DEBUG_ROUTEMAP_DETAIL))) zlog_debug( "%s: Access-List Specified: %s does not exist defaulting to NO_MATCH", __func__, (char *)rule); @@ -521,7 +522,8 @@ route_match_ip_next_hop(void *rule, const struct prefix *prefix, void *object) alist = access_list_lookup(AFI_IP, (char *)rule); if (alist == NULL) { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + if (unlikely(CHECK_FLAG(rmap_debug, + DEBUG_ROUTEMAP_DETAIL))) zlog_debug( "%s: Access-List Specified: %s does not exist defaulting to NO_MATCH", __func__, (char *)rule); @@ -581,7 +583,8 @@ route_match_ip_route_source(void *rule, const struct prefix *pfx, void *object) alist = access_list_lookup(AFI_IP, (char *)rule); if (alist == NULL) { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + if (unlikely(CHECK_FLAG(rmap_debug, + DEBUG_ROUTEMAP_DETAIL))) zlog_debug( "%s: Access-List Specified: %s does not exist defaulting to NO_MATCH", __func__, (char *)rule); @@ -676,7 +679,7 @@ route_match_address_prefix_list(void *rule, afi_t afi, plist = prefix_list_lookup(afi, (char *)rule); if (plist == NULL) { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))) zlog_debug( "%s: Prefix List %s specified does not exist defaulting to NO_MATCH", __func__, (char *)rule); @@ -737,7 +740,8 @@ route_match_ip_next_hop_prefix_list(void *rule, const struct prefix *prefix, plist = prefix_list_lookup(AFI_IP, (char *)rule); if (plist == NULL) { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + if (unlikely(CHECK_FLAG(rmap_debug, + DEBUG_ROUTEMAP_DETAIL))) zlog_debug( "%s: Prefix List %s specified does not exist defaulting to NO_MATCH", __func__, (char *)rule); @@ -786,7 +790,8 @@ route_match_ipv6_next_hop_prefix_list(void *rule, const struct prefix *prefix, plist = prefix_list_lookup(AFI_IP6, (char *)rule); if (!plist) { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + if (unlikely(CHECK_FLAG(rmap_debug, + DEBUG_ROUTEMAP_DETAIL))) zlog_debug( "%s: Prefix List %s specified does not exist defaulting to NO_MATCH", __func__, (char *)rule); @@ -891,7 +896,8 @@ route_match_ip_route_source_prefix_list(void *rule, const struct prefix *prefix, plist = prefix_list_lookup(AFI_IP, (char *)rule); if (plist == NULL) { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + if (unlikely(CHECK_FLAG(rmap_debug, + DEBUG_ROUTEMAP_DETAIL))) zlog_debug( "%s: Prefix List %s specified does not exist defaulting to NO_MATCH", __func__, (char *)rule); @@ -956,7 +962,7 @@ route_match_mac_address(void *rule, const struct prefix *prefix, void *object) alist = access_list_lookup(AFI_L2VPN, (char *)rule); if (alist == NULL) { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))) zlog_debug( "%s: Access-List Specified: %s does not exist defaulting to NO_MATCH", __func__, (char *)rule); @@ -964,7 +970,7 @@ route_match_mac_address(void *rule, const struct prefix *prefix, void *object) return RMAP_NOMATCH; } if (prefix->u.prefix_evpn.route_type != BGP_EVPN_MAC_IP_ROUTE) { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))) zlog_debug( "%s: Prefix %pFX is not a EVPN MAC IP ROUTE defaulting to NO_MATCH", __func__, prefix); @@ -3242,7 +3248,8 @@ route_match_ipv6_address(void *rule, const struct prefix *prefix, void *object) if (prefix->family == AF_INET6) { alist = access_list_lookup(AFI_IP6, (char *)rule); if (alist == NULL) { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + if (unlikely(CHECK_FLAG(rmap_debug, + DEBUG_ROUTEMAP_DETAIL))) zlog_debug( "%s: Access-List Specified: %s does not exist defaulting to NO_MATCH", __func__, (char *)rule); @@ -3299,7 +3306,8 @@ route_match_ipv6_next_hop(void *rule, const struct prefix *prefix, void *object) alist = access_list_lookup(AFI_IP6, (char *)rule); if (!alist) { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + if (unlikely(CHECK_FLAG(rmap_debug, + DEBUG_ROUTEMAP_DETAIL))) zlog_debug( "%s: Access-List Specified: %s does not exist defaulting to NO_MATCH", __func__, (char *)rule); diff --git a/bgpd/bgp_updgrp.c b/bgpd/bgp_updgrp.c index 72dca9f702..0b1e54916a 100644 --- a/bgpd/bgp_updgrp.c +++ b/bgpd/bgp_updgrp.c @@ -1961,15 +1961,8 @@ void update_group_adjust_peer(struct peer_af *paf) } updgrp = update_group_find(paf); - if (!updgrp) { + if (!updgrp) updgrp = update_group_create(paf); - if (!updgrp) { - flog_err(EC_BGP_UPDGRP_CREATE, - "couldn't create update group for peer %s", - paf->peer->host); - return; - } - } old_subgrp = paf->subgroup; @@ -1992,11 +1985,8 @@ void update_group_adjust_peer(struct peer_af *paf) } subgrp = update_subgroup_find(updgrp, paf); - if (!subgrp) { + if (!subgrp) subgrp = update_subgroup_create(updgrp); - if (!subgrp) - return; - } update_subgroup_add_peer(subgrp, paf, 1); if (BGP_DEBUG(update_groups, UPDATE_GROUPS)) diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index a436490ba1..de781d6b1e 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -1675,28 +1675,46 @@ DEFUN (no_router_bgp, for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, tmp_bgp)) { if (tmp_bgp->inst_type != BGP_INSTANCE_TYPE_VRF) continue; - if (CHECK_FLAG(tmp_bgp->af_flags[AFI_IP][SAFI_UNICAST], - BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT) || - CHECK_FLAG(tmp_bgp->af_flags[AFI_IP6][SAFI_UNICAST], - BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT) || - CHECK_FLAG(tmp_bgp->af_flags[AFI_IP][SAFI_UNICAST], - BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT) || - CHECK_FLAG(tmp_bgp->af_flags[AFI_IP6][SAFI_UNICAST], - BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT) || - CHECK_FLAG(tmp_bgp->af_flags[AFI_IP][SAFI_UNICAST], + if (CHECK_FLAG( + tmp_bgp->af_flags[AFI_IP] + [SAFI_UNICAST], + BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT) || + CHECK_FLAG( + tmp_bgp->af_flags[AFI_IP6] + [SAFI_UNICAST], + BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT) || + CHECK_FLAG( + tmp_bgp->af_flags[AFI_IP] + [SAFI_UNICAST], + BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT) || + CHECK_FLAG( + tmp_bgp->af_flags[AFI_IP6] + [SAFI_UNICAST], + BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT) || + CHECK_FLAG(tmp_bgp->af_flags[AFI_IP] + [SAFI_UNICAST], BGP_CONFIG_VRF_TO_VRF_EXPORT) || - CHECK_FLAG(tmp_bgp->af_flags[AFI_IP6][SAFI_UNICAST], + CHECK_FLAG(tmp_bgp->af_flags[AFI_IP6] + [SAFI_UNICAST], BGP_CONFIG_VRF_TO_VRF_EXPORT) || (bgp == bgp_get_evpn() && - (CHECK_FLAG(tmp_bgp->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADV_IPV4_UNICAST) || - CHECK_FLAG(tmp_bgp->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP) || - CHECK_FLAG(tmp_bgp->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADV_IPV6_UNICAST) || - CHECK_FLAG(tmp_bgp->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP))) || - (hashcount(tmp_bgp->vnihash))) { + (CHECK_FLAG( + tmp_bgp->af_flags[AFI_L2VPN] + [SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST) || + CHECK_FLAG( + tmp_bgp->af_flags[AFI_L2VPN] + [SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP) || + CHECK_FLAG( + tmp_bgp->af_flags[AFI_L2VPN] + [SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST) || + CHECK_FLAG( + tmp_bgp->af_flags[AFI_L2VPN] + [SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP))) || + (tmp_bgp->l3vni)) { vty_out(vty, "%% Cannot delete default BGP instance. Dependent VRF instances exist\n"); return CMD_WARNING_CONFIG_FAILED; @@ -2802,6 +2820,21 @@ DEFUN(no_bgp_ebgp_requires_policy, no_bgp_ebgp_requires_policy_cmd, return CMD_SUCCESS; } +DEFPY(bgp_lu_uses_explicit_null, bgp_lu_uses_explicit_null_cmd, + "[no] bgp labeled-unicast explicit-null", + NO_STR BGP_STR + "BGP Labeled-unicast options\n" + "Use explicit-null label values for local prefixes\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + + if (no) + UNSET_FLAG(bgp->flags, BGP_FLAG_LU_EXPLICIT_NULL); + else + SET_FLAG(bgp->flags, BGP_FLAG_LU_EXPLICIT_NULL); + return CMD_SUCCESS; +} + DEFUN(bgp_suppress_duplicates, bgp_suppress_duplicates_cmd, "bgp suppress-duplicates", BGP_STR @@ -18217,6 +18250,9 @@ int bgp_config_write(struct vty *vty) ? "" : "no "); + if (!!CHECK_FLAG(bgp->flags, BGP_FLAG_LU_EXPLICIT_NULL)) + vty_out(vty, " bgp labeled-unicast explicit-null\n"); + /* draft-ietf-idr-deprecate-as-set-confed-set */ if (bgp->reject_as_sets) vty_out(vty, " bgp reject-as-sets\n"); @@ -19136,6 +19172,9 @@ void bgp_vty_init(void) install_element(BGP_NODE, &bgp_ebgp_requires_policy_cmd); install_element(BGP_NODE, &no_bgp_ebgp_requires_policy_cmd); + /* bgp labeled-unicast explicit-null */ + install_element(BGP_NODE, &bgp_lu_uses_explicit_null_cmd); + /* bgp suppress-duplicates */ install_element(BGP_NODE, &bgp_suppress_duplicates_cmd); install_element(BGP_NODE, &no_bgp_suppress_duplicates_cmd); diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index a08a2870ee..b6491bf799 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -500,6 +500,8 @@ struct bgp { #define BGP_FLAG_HARD_ADMIN_RESET (1ULL << 31) /* Evaluate the AIGP attribute during the best path selection process */ #define BGP_FLAG_COMPARE_AIGP (1ULL << 32) +/* For BGP-LU, force local prefixes to use explicit-null label */ +#define BGP_FLAG_LU_EXPLICIT_NULL (1ULL << 33) /* BGP default address-families. * New peers inherit enabled afi/safis from bgp instance. diff --git a/bgpd/rfapi/rfapi.c b/bgpd/rfapi/rfapi.c index 8d6db9d775..67c70431bd 100644 --- a/bgpd/rfapi/rfapi.c +++ b/bgpd/rfapi/rfapi.c @@ -51,6 +51,8 @@ #include <execinfo.h> #endif /* HAVE_GLIBC_BACKTRACE */ +#define DEBUG_CLEANUP 0 + struct ethaddr rfapi_ethaddr0 = {{0}}; #define DEBUG_RFAPI_STR "RF API debugging/testing command\n" @@ -3677,11 +3679,36 @@ void rfapi_delete(struct bgp *bgp) { extern void rfp_clear_vnc_nve_all(void); /* can't fix correctly yet */ +#if DEBUG_CLEANUP + zlog_debug("%s: bgp %p", __func__, bgp); +#endif + /* * This clears queries and registered routes, and closes nves */ if (bgp->rfapi) rfp_clear_vnc_nve_all(); + + /* + * close any remaining descriptors + */ + struct rfapi *h = bgp->rfapi; + + if (h && h->descriptors.count) { + struct listnode *node, *nnode; + struct rfapi_descriptor *rfd; +#if DEBUG_CLEANUP + zlog_debug("%s: descriptor count %u", __func__, + h->descriptors.count); +#endif + for (ALL_LIST_ELEMENTS(&h->descriptors, node, nnode, rfd)) { +#if DEBUG_CLEANUP + zlog_debug("%s: closing rfd %p", __func__, rfd); +#endif + (void)rfapi_close(rfd); + } + } + bgp_rfapi_cfg_destroy(bgp, bgp->rfapi_cfg); bgp->rfapi_cfg = NULL; bgp_rfapi_destroy(bgp, bgp->rfapi); diff --git a/bgpd/rfapi/rfapi_rib.c b/bgpd/rfapi/rfapi_rib.c index be9d30768c..5784f95b27 100644 --- a/bgpd/rfapi/rfapi_rib.c +++ b/bgpd/rfapi/rfapi_rib.c @@ -40,6 +40,7 @@ #define DEBUG_PENDING_DELETE_ROUTE 0 #define DEBUG_NHL 0 #define DEBUG_RIB_SL_RD 0 +#define DEBUG_CLEANUP 0 /* forward decl */ #if DEBUG_NHL @@ -327,6 +328,11 @@ static void rfapiRibStartTimer(struct rfapi_descriptor *rfd, tcb = XCALLOC(MTYPE_RFAPI_RECENT_DELETE, sizeof(struct rfapi_rib_tcb)); } +#if DEBUG_CLEANUP + zlog_debug("%s: rfd %p, rn %p, ri %p, tcb %p", __func__, rfd, rn, ri, + tcb); +#endif + tcb->rfd = rfd; tcb->ri = ri; tcb->rn = rn; @@ -506,6 +512,16 @@ void rfapiRibClear(struct rfapi_descriptor *rfd) NULL, (void **)&ri)) { + if (ri->timer) { + struct rfapi_rib_tcb + *tcb; + + tcb = EVENT_ARG( + ri->timer); + EVENT_OFF(ri->timer); + XFREE(MTYPE_RFAPI_RECENT_DELETE, + tcb); + } rfapi_info_free(ri); skiplist_delete_first( (struct skiplist *) @@ -555,6 +571,9 @@ void rfapiRibFree(struct rfapi_descriptor *rfd) { afi_t afi; +#if DEBUG_CLEANUP + zlog_debug("%s: rfd %p", __func__, rfd); +#endif /* * NB rfd is typically detached from master list, so is not included diff --git a/bgpd/rfapi/rfapi_vty.c b/bgpd/rfapi/rfapi_vty.c index 2877c29c20..29698846c3 100644 --- a/bgpd/rfapi/rfapi_vty.c +++ b/bgpd/rfapi/rfapi_vty.c @@ -4888,6 +4888,7 @@ static int vnc_clear_vrf(struct vty *vty, struct bgp *bgp, const char *arg_vrf, clear_vnc_prefix(&cda); vty_out(vty, "Cleared %u out of %d prefixes.\n", cda.pfx_count, start_count); + print_cleared_stats(&cda); /* frees lists in cda */ return CMD_SUCCESS; } diff --git a/configure.ac b/configure.ac index f3968df2aa..79288c67e9 100644 --- a/configure.ac +++ b/configure.ac @@ -299,14 +299,20 @@ if test "$enable_scripting" = "yes"; then AX_LUA_HEADERS([], [ AC_MSG_ERROR([Lua 5.3 headers are required to build with Lua support. No other version is supported.]) ]) - AX_LUA_LIBS([ + PKG_CHECK_MODULES([LUA], [lua5.3], [ AC_DEFINE([HAVE_SCRIPTING], [1], [Have support for scripting]) - LIBS="$LIBS $LUA_LIB" + LIBS="$LIBS $LUA_LIBS" SCRIPTING=true ], [ - SCRIPTING=false - AC_MSG_ERROR([Lua 5.3 libraries are required to build with Lua support. No other version is supported.]) - ]) + AX_LUA_LIBS([ + AC_DEFINE([HAVE_SCRIPTING], [1], [Have support for scripting]) + LIBS="$LIBS $LUA_LIB" + SCRIPTING=true + ], [ + SCRIPTING=false + AC_MSG_ERROR([Lua 5.3 libraries are required to build with Lua support. No other version is supported.]) + ]) + ]) fi dnl the following flags go in CFLAGS rather than AC_CFLAGS since they make diff --git a/doc/developer/logging.rst b/doc/developer/logging.rst index b7021b69a1..52653d3768 100644 --- a/doc/developer/logging.rst +++ b/doc/developer/logging.rst @@ -502,6 +502,51 @@ General utility formats representation for a hexdump. Non-printable characters are replaced with a dot. +.. frrfmt:: %pIS (struct iso_address *) + + ([IS]o Network address) - Format ISO Network Address + + ``%pIS``: :frrfmtout:`01.0203.04O5` + ISO Network address is printed as separated byte. The number of byte of the + address is embeded in the `iso_net` structure. + + ``%pISl``: :frrfmtout:`01.0203.04O5.0607.0809.1011.1213.14` - long format to + print the long version of the ISO Network address which include the System + ID and the PSEUDO-ID of the IS-IS system + + Note that the `ISO_ADDR_STRLEN` define gives the total size of the string + that could be used in conjunction to snprintfrr. Use like:: + + char buf[ISO_ADDR_STRLEN]; + struct iso_address addr = {.addr_len = 4, .area_addr = {1, 2, 3, 4}}; + snprintfrr(buf, ISO_ADDR_STRLEN, "%pIS", &addr); + +.. frrfmt:: %pSY (uint8_t *) + + (IS-IS [SY]stem ID) - Format IS-IS System ID + + ``%pSY``: :frrfmtout:`0102.0304.0506` + +.. frrfmt:: %pPN (uint8_t *) + + (IS-IS [P]seudo [N]ode System ID) - Format IS-IS Pseudo Node System ID + + ``%pPN``: :frrfmtout:`0102.0304.0506.07` + +.. frrfmt:: %pLS (uint8_t *) + + (IS-IS [L]sp fragment [S]ystem ID) - Format IS-IS Pseudo System ID + + ``%pLS``: :frrfmtout:`0102.0304.0506.07-08` + + Note that the `ISO_SYSID_STRLEN` define gives the total size of the string + that could be used in conjunction to snprintfrr. Use like:: + + char buf[ISO_SYSID_STRLEN]; + uint8_t id[8] = {1, 2, 3, 4 , 5 , 6 , 7, 8}; + snprintfrr(buf, SYS_ID_SIZE, "%pSY", id); + + Integer formats ^^^^^^^^^^^^^^^ diff --git a/doc/developer/zebra.rst b/doc/developer/zebra.rst index cef53f1cbe..5f039758a5 100644 --- a/doc/developer/zebra.rst +++ b/doc/developer/zebra.rst @@ -218,8 +218,6 @@ Zebra Protocol Commands +------------------------------------+-------+ | ZEBRA_IMPORT_ROUTE_UNREGISTER | 27 | +------------------------------------+-------+ -| ZEBRA_IMPORT_CHECK_UPDATE | 28 | -+------------------------------------+-------+ | ZEBRA_BFD_DEST_REGISTER | 29 | +------------------------------------+-------+ | ZEBRA_BFD_DEST_DEREGISTER | 30 | diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index 946f0699f2..97d7ce6b75 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -2767,6 +2767,17 @@ happened automatically if local-role is set. value of his role (by setting local-role on his side). Otherwise, a Role Mismatch Notification will be sent. +Labeled unicast +--------------- + +*bgpd* supports labeled information, as per :rfc:`3107`. + +.. clicmd:: bgp labeled-unicast explicit-null + +By default, locally advertised prefixes use the `implicit-null` label to +encode in the outgoing NLRI. The following command uses the `explicit-null` +label value for all the BGP instances. + .. _bgp-l3vpn-vrfs: L3VPN VRFs diff --git a/doc/user/index.rst b/doc/user/index.rst index c02d10deda..4789677a9a 100644 --- a/doc/user/index.rst +++ b/doc/user/index.rst @@ -27,6 +27,7 @@ Basics grpc filter routemap + affinitymap ipv6 kernel snmp diff --git a/doc/user/isisd.rst b/doc/user/isisd.rst index 90c13d4f93..570b8bd182 100644 --- a/doc/user/isisd.rst +++ b/doc/user/isisd.rst @@ -68,6 +68,10 @@ writing, *isisd* does not support multiple ISIS processes. Log changes in adjacency state. +.. clicmd:: log-pdu-drops + + Log any dropped PDUs. + .. clicmd:: metric-style [narrow | transition | wide] Set old-style (ISO 10589) or new-style packet formats: @@ -312,12 +316,12 @@ Showing ISIS information Show the ISIS database globally, for a specific LSP id without or with details. -.. clicmd:: show isis topology [level-1|level-2] +.. clicmd:: show isis topology [level-1|level-2] [algorithm (128-255)] Show topology IS-IS paths to Intermediate Systems, globally, in area (level-1) or domain (level-2). -.. clicmd:: show isis route [level-1|level-2] [prefix-sid|backup] +.. clicmd:: show isis route [level-1|level-2] [prefix-sid|backup] [algorithm (128-255)] Show the ISIS routing table, as determined by the most recent SPF calculation. @@ -385,8 +389,7 @@ Traffic Engineering :ref:`ospf-traffic-engineering` - -.. _debugging-isis: +.. _isis-segment-routing: Segment Routing =============== @@ -419,7 +422,7 @@ Known limitations: MPLS dataplane. E.g. for Linux kernel, since version 4.13 the maximum value is 32. -.. clicmd:: segment-routing prefix <A.B.C.D/M|X:X::X:X/M> <absolute (16-1048575)|index (0-65535) [no-php-flag|explicit-null] [n-flag-clear] +.. clicmd:: segment-routing prefix <A.B.C.D/M|X:X::X:X/M> [algorithm (128-255)] <absolute (16-1048575)|index (0-65535) [no-php-flag|explicit-null] [n-flag-clear] prefix. The 'no-php-flag' means NO Penultimate Hop Popping that allows SR node to request to its neighbor to not pop the label. The 'explicit-null' @@ -428,10 +431,164 @@ Known limitations: clear the Node flag that is set by default for Prefix-SIDs associated to loopback addresses. This option is necessary to configure Anycast-SIDs. -.. clicmd:: show isis segment-routing node +.. clicmd:: show isis segment-routing node [algorithm (128-255)] Show detailed information about all learned Segment Routing Nodes. +.. _isis-flex-algo: + +Flex-Algos (Flex-Algo) +====================== + +*isisd* supports some features of +`RFC 9350 <https://tools.ietf.org/html/rfc9350>`_ on an MPLS Segment-Routing +dataplane. The compatibility has been tested against Cisco. + +IS-IS uses by default the `Shortest-Path-First` algorithm that basically +calculates paths based on the shortest total metric to the destinations. +Flex-Algo allows new algorithms to run in parallel to compute paths in different +manners, based on metrics (IGP metric or a new type of metrics such as Traffic +Engineering (TE) metric and minimum delay...) and constraints. New metric types +are not yet implemented but constraints are already operational. Constraints can +restrict paths to links with specific affinities or avoid links with specific +affinities. Combinations of these are also possible. + +The administrator can configure up to 128 Flex-Algos in an IS-IS area. +To do so, it defines a set of Flex-Algo Definitions (FAD) which +have the following characteristics: + +- a numeric identifier (ID) between 128 and 255 inclusive +- a set of constraints (basically, include or exclude a certain given set of + links, designated by a admin-group) +- the calculation type (only the `Shortest-Path-First` is currently supported) +- the metric type (only the IGP inherited metric type is currently supported) +- some additional flags (not supported for the moment). + +A subset of routers advertises the Flex-Algo Definitions (FAD) to the other +routers within an area. In order to use a common set of FADs, each router runs a +FAD election process for each locally configured algorithm, using the following +rules: + +- If a locally configured FAD is not advertised to the area, the router does not + participate in the particular flex algorithm. +- If a given flex algorithm is running, the participation in this particular + flex algorithm stops when its advertisements are over. +- A router includes its own FAD in the election process if and only if it is + advertised to the other routers. +- If only one router advertises the FAD, the FAD is elected. +- If several FADs are advertised with different priorities, the one with the + highest priority value is selected. +- If there are multiple advertisements of the FAD with the same highest + priority, the FAD of the router with the highest IS-IS system-ID is + selected. + +Routers only use the specifications of the elected FAD regardless of the locally +configured definitions. If a router does not support one of the FAD +characteristics, it stops participating in the Flex-Algo. + +For each running Flex-Algo, the Segment-Routing SIDs must be +configured with values unique to the algorithm. It allows routers to identify +which flex algorithm they must use for a given packet. + +The following commands configure Flex-Algo at the 'router isis' configuration +level. Segment-Routing prefixes must be configured for the Flex-Algo. + +.. clicmd:: flexible-algorithm (128-255) + + Add a Flex-Algo Definition (FAD) and enter the FAD configuration + level. The algorithm ID value is in the range of 128 to 255 inclusive. + +.. clicmd:: no flexible-algorithm (128-255) + + Unconfigure a Flex-Algo Definition. + +.. clicmd:: affinity-map NAME bit-position (0-255) + + Add the specified 'affinity-map'. Affinity-map definitions are used in + FADs and in interfaces admin-group definition. + + Affinity-maps format in advertisement TLVs use the extended admin-group + format defined in the RFC7308 section 2.2. The extended admin-group uses a + 256 bits field. If an affinity-map is set, the bit at the extended + admin-group 'bit-position' is set 1, else it is set to 0. + +The following commands configure Flex-Algo at the 'router isis' and +'flexible-algorithm (128-255)' configuration level. + +.. clicmd:: advertise-definition + + Advertise the current FAD to other IS-IS routers by using specific IS-IS + TLVs. By default, the definition is is not shared with other routers. + +Â Â A router can advertise a FAD without participating in the Flex-Algo. + +.. clicmd:: priority (0-255) + + Set the specified 'priority' in the current FAD advertisements . + +.. clicmd:: metric-type [igp|te|delay] + + Set the 'metric-type' for the current FAD. 'igp' is + the default value and refers to the classic 'Shortest-Path-First' algorithm. + If the 'te' or the 'delay' metric is selected, the value is advertised but + the flex algorithm is disabled locally because these types are not currently + supported. + +.. clicmd:: no metric-type + + Reset the 'metric-type' to the default 'igp' metric. + +.. clicmd:: affinity exclude-any NAME + + Add the specified affinity to the list of exclude-any affinities. The + Flex-Algo will compute paths that exclude the segments with any of + the specified affinities. + +.. clicmd:: no affinity exclude-any NAME + + Remove the specified affinity to the list of exclude-any affinities. + +.. clicmd:: affinity include-all NAME + + Add the specified affinity to the list of include-all affinities. The + Flex-Algo will compute paths that include the segments with all + the specified affinities. + +.. clicmd:: no affinity include-all NAME + + Remove the specified affinity to the list of include-all affinities. + +.. clicmd:: affinity include-any NAME + + Add the specified affinity to the list of include-any affinities. The + Flex-Algo will compute paths that include the segments with any of + the specified affinities. + +.. clicmd:: no affinity include-any NAME + + Remove the specified affinity to the list of include-any affinities. + +The following commands configure Flex-Algo at the 'interface' configuration +level. + +.. clicmd:: isis affinity flex-algo NAME + + Add the specified affinity to the interface. + +.. clicmd:: no isis affinity flex-algo NAME + + Remove the specified affinity from the interface. + +The following command show Flex-Algo information: + +.. clicmd:: show isis flex-algo [(128-255)] + + Show information about the elected FADs + +'show isis route', 'show isis topology' and 'show isis segment-routing node' +includes an 'algorithm (128-255)' optional argument. See +:ref:`showing-isis-information` and :ref:`isis-segment-routing`. + Debugging ISIS ============== diff --git a/doc/user/mgmtd.rst b/doc/user/mgmtd.rst index 6ea7e7891a..6614a568f8 100644 --- a/doc/user/mgmtd.rst +++ b/doc/user/mgmtd.rst @@ -70,14 +70,6 @@ Frontend Interface and MGMTd: database. - Data can be retrieved anytime using GET_CONFIG/GET_DATA API. - - Startup Database: - - - Consists of configuration data items only. - - This is a copy of Running database that is stored in persistent - storage and is used to load configurations on Running database during - MGMT daemon startup. - - Data cannot be edited/retrieved directly via Frontend interface. - - Operational Database: - Consists of non-configurational data items. @@ -313,8 +305,7 @@ MGMT Configuration commands .. clicmd:: mgmt commit apply This command commits any uncommited changes in the Candidate DB to the - Running DB. It also dumps a copy of the tree in JSON format into - frr_startup.json. + Running DB. .. clicmd:: mgmt commit check diff --git a/doc/user/ospfd.rst b/doc/user/ospfd.rst index 30d55f34a7..5171832604 100644 --- a/doc/user/ospfd.rst +++ b/doc/user/ospfd.rst @@ -310,6 +310,18 @@ To start OSPF process you have to specify the OSPF router. of packets to process before returning. The defult value of this parameter is 20. +.. clicmd:: socket buffer <send | recv | all> (1-4000000000) + + This command controls the ospf instance's socket buffer sizes. The + 'no' form resets one or both values to the default. + +.. clicmd:: no socket-per-interface + + Ordinarily, ospfd uses a socket per interface for sending + packets. This command disables those per-interface sockets, and + causes ospfd to use a single socket per ospf instance for sending + and receiving packets. + .. _ospf-area: Areas @@ -325,7 +337,6 @@ Areas announced to other areas. This command can be used only in ABR and ONLY router-LSAs (Type-1) and network-LSAs (Type-2) (i.e. LSAs with scope area) can be summarized. Type-5 AS-external-LSAs can't be summarized - their scope is AS. - Summarizing Type-7 AS-external-LSAs isn't supported yet by FRR. .. code-block:: frr @@ -431,6 +442,31 @@ Areas configured not to advertise forwarding addresses into the backbone to direct forwarded traffic to the NSSA ABR translator. +.. clicmd:: area A.B.C.D nssa default-information-originate [metric-type (1-2)] [metric (0-16777214)] + +.. clicmd:: area (0-4294967295) nssa default-information-originate [metric-type (1-2)] [metric (0-16777214)] + + NSSA ABRs and ASBRs can be configured with the `default-information-originate` + option to originate a Type-7 default route into the NSSA area. In the case + of NSSA ASBRs, the origination of the default route is conditioned to the + existence of a default route in the RIB that wasn't learned via the OSPF + protocol. + +.. clicmd:: area A.B.C.D nssa range A.B.C.D/M [<not-advertise|cost (0-16777215)>] + +.. clicmd:: area (0-4294967295) nssa range A.B.C.D/M [<not-advertise|cost (0-16777215)>] + + Summarize a group of external subnets into a single Type-7 LSA, which is + then translated to a Type-5 LSA and avertised to the backbone. + This command can only be used at the area boundary (NSSA ABR router). + + By default, the metric of the summary route is calculated as the highest + metric among the summarized routes. The `cost` option, however, can be used + to set an explicit metric. + + The `not-advertise` option, when present, prevents the summary route from + being advertised, effectively filtering the summarized routes. + .. clicmd:: area A.B.C.D default-cost (0-16777215) diff --git a/doc/user/overview.rst b/doc/user/overview.rst index 4a24fa52be..5ea33d62c9 100644 --- a/doc/user/overview.rst +++ b/doc/user/overview.rst @@ -424,6 +424,8 @@ BGP :t:`Extended Optional Parameters Length for BGP OPEN Message. E. Chen, J. Scudder. July 2021.` - :rfc:`9234` :t:`Route Leak Prevention and Detection Using Roles in UPDATE and OPEN Messages. A. Azimov, E. Bogomazov, R. Bush, K. Patel, K. Sriram. May 2022.` +- :rfc:`9384` + :t:`A BGP Cease NOTIFICATION Subcode for Bidirectional Forwarding Detection (BFD). J. Haas. March 2023.` OSPF ---- diff --git a/doc/user/pim.rst b/doc/user/pim.rst index 72471d7af0..d70c3c0e64 100644 --- a/doc/user/pim.rst +++ b/doc/user/pim.rst @@ -387,9 +387,14 @@ cause great confusion. .. clicmd:: show ip igmp [vrf NAME] join [json] Display IGMP static join information for a specific vrf. - If "vrf all" is provided, it displays information for all the vrfs present. + +.. index:: show ip igmp [vrf NAME$vrf_name] groups [INTERFACE$ifname [GROUP$grp_str]] [detail] [json$json] +.. clicmd:: show ip igmp [vrf NAME$vrf_name] groups [INTERFACE$ifname [GROUP$grp_str]] [detail] [json$json] -.. clicmd:: show ip igmp groups + Display IGMP static join information for all the vrfs present. + +.. index:: show ip igmp vrf all groups [GROUP$grp_str] [detail$detail] [json$json] +.. clicmd:: show ip igmp vrf all groups [GROUP$grp_str] [detail$detail] [json$json] Display IGMP groups information. diff --git a/doc/user/routemap.rst b/doc/user/routemap.rst index 9e4d7a611a..b7f5336564 100644 --- a/doc/user/routemap.rst +++ b/doc/user/routemap.rst @@ -305,10 +305,18 @@ Route Map Set Command .. clicmd:: set metric <[+|-](1-4294967295)|rtt|+rtt|-rtt> - Set the BGP attribute MED to a specific value. Use `+`/`-` to add or subtract - the specified value to/from the MED. Use `rtt` to set the MED to the round - trip time or `+rtt`/`-rtt` to add/subtract the round trip time to/from the - MED. + Set the route metric. When used with BGP, set the BGP attribute MED to a + specific value. Use `+`/`-` to add or subtract the specified value to/from + the existing/MED. Use `rtt` to set the MED to the round trip time or + `+rtt`/`-rtt` to add/subtract the round trip time to/from the MED. + +.. clicmd:: set min-metric <(0-4294967295)> + + Set the minimum meric for the route. + +.. clicmd:: set max-metric <(0-4294967295)> + + Set the maximum meric for the route. .. clicmd:: set aigp-metric <igp-metric|(1-4294967295)> diff --git a/docker/ubi-8/Dockerfile b/docker/ubi-8/Dockerfile deleted file mode 100644 index 1d1e8bdc6e..0000000000 --- a/docker/ubi-8/Dockerfile +++ /dev/null @@ -1,83 +0,0 @@ -# This stage builds an rpm from the source -FROM registry.access.redhat.com/ubi8/ubi:8.5 as ubi-8-builder - -RUN dnf -y update-minimal --security --sec-severity=Important --sec-severity=Critical - -RUN rpm --import https://www.centos.org/keys/RPM-GPG-KEY-CentOS-Official \ - && dnf config-manager --disableplugin subscription-manager --add-repo http://mirror.centos.org/centos/8-stream/BaseOS/x86_64/os \ - && dnf config-manager --disableplugin subscription-manager --add-repo http://mirror.centos.org/centos/8-stream/AppStream/x86_64/os \ - && dnf config-manager --disableplugin subscription-manager --add-repo http://mirror.centos.org/centos/8-stream/PowerTools/x86_64/os - -RUN dnf install -qy https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm \ - && dnf install --enablerepo=* -qy rpm-build git autoconf pcre-devel \ - systemd-devel automake libtool make readline-devel texinfo \ - net-snmp-devel pkgconfig groff pkgconfig json-c-devel pam-devel \ - bison flex python3-pytest c-ares-devel python3-devel python3-sphinx \ - libcap-devel platform-python-devel \ - https://ci1.netdef.org/artifact/LIBYANG-LIBYANGV2/shared/build-2/CentOS-8-x86_64-Packages/libyang2-2.0.0.10.g2eb910e4-1.el8.x86_64.rpm \ - https://ci1.netdef.org/artifact/LIBYANG-LIBYANGV2/shared/build-2/CentOS-8-x86_64-Packages/libyang2-devel-2.0.0.10.g2eb910e4-1.el8.x86_64.rpm \ - https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-00146/CentOS-7-x86_64-Packages/librtr-0.8.0-1.el7.x86_64.rpm \ - https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-00146/CentOS-7-x86_64-Packages/librtr-devel-0.8.0-1.el7.x86_64.rpm - - -COPY . /src - -ARG PKGVER - -RUN echo '%_smp_mflags %( echo "-j$(/usr/bin/getconf _NPROCESSORS_ONLN)"; )' >> /root/.rpmmacros \ - && cd /src \ - && ./bootstrap.sh \ - && ./configure \ - --enable-rpki \ - --enable-snmp=agentx \ - --enable-numeric-version \ - --with-pkg-extra-version="_palmetto_git$PKGVER" \ - && make dist \ - && cd / \ - && mkdir -p /rpmbuild/{SOURCES,SPECS} \ - && cp /src/frr*.tar.gz /rpmbuild/SOURCES \ - && cp /src/redhat/frr.spec /rpmbuild/SPECS \ - && rpmbuild \ - --define "_topdir /rpmbuild" \ - -ba /rpmbuild/SPECS/frr.spec - -# This stage installs frr from the rpm -FROM registry.access.redhat.com/ubi8/ubi:8.5 -RUN dnf -y update-minimal --security --sec-severity=Important --sec-severity=Critical -ARG FRR_IMAGE_TAG -ARG FRR_RELEASE -ARG FRR_NAME -ARG FRR_VENDOR -LABEL name=$FRR_NAME \ - vendor=$FRR_VENDOR \ - version=$FRR_IMAGE_TAG \ - release=$FRR_RELEASE - -RUN rpm --import https://www.centos.org/keys/RPM-GPG-KEY-CentOS-Official \ - && dnf config-manager --disableplugin subscription-manager --add-repo http://mirror.centos.org/centos/8-stream/BaseOS/x86_64/os \ - && dnf config-manager --disableplugin subscription-manager --add-repo http://mirror.centos.org/centos/8-stream/AppStream/x86_64/os - -RUN dnf install -qy https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm \ - && mkdir -p /pkgs/rpm \ - && dnf install --enablerepo=* -qy https://ci1.netdef.org/artifact/LIBYANG-LIBYANGV2/shared/build-2/CentOS-8-x86_64-Packages/libyang2-2.0.0.10.g2eb910e4-1.el8.x86_64.rpm \ - https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-00146/CentOS-7-x86_64-Packages/librtr-0.8.0-1.el7.x86_64.rpm - -COPY --from=ubi-8-builder /rpmbuild/RPMS/ /pkgs/rpm/ - -RUN dnf install -qy /pkgs/rpm/*/*.rpm \ - && rm -rf /pkgs \ -# Own the config / PID files - && mkdir -p /var/run/frr \ - && 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/ubi-8/docker-start /usr/lib/frr/docker-start -CMD ["/usr/lib/frr/docker-start"] diff --git a/docker/ubi8-minimal/Dockerfile b/docker/ubi8-minimal/Dockerfile new file mode 100644 index 0000000000..adb04219be --- /dev/null +++ b/docker/ubi8-minimal/Dockerfile @@ -0,0 +1,132 @@ +# This stage builds an rpm from the source +ARG UBI8_MINIMAL_VERSION +FROM registry.access.redhat.com/ubi8/ubi-minimal:${UBI8_MINIMAL_VERSION} as ubi8-minimal-builder + +RUN rpm --import https://repo.almalinux.org/almalinux/RPM-GPG-KEY-AlmaLinux-8 + +ADD docker/ubi8-minimal/almalinux.repo /etc/yum.repos.d/almalinux.repo + +# ubi8-minimal comes with broken tzdata package installed, so we need to remove them +# and later reinstall it again: https://bugzilla.redhat.com/show_bug.cgi?id=1668185 +RUN rpm --quiet -e --nodeps tzdata >/dev/null 2>&1 + +RUN microdnf --disableplugin=subscription-manager --setopt=install_weak_deps=0 install \ + autoconf \ + automake \ + bison \ + c-ares-devel \ + flex \ + git \ + groff \ + json-c-devel \ + libcap-devel \ + libssh-devel \ + libtool \ + make \ + net-snmp-devel \ + openssl \ + pam-devel \ + pcre-devel \ + pkgconfig \ + platform-python-devel \ + python3-devel \ + python3-pytest \ + python3-sphinx \ + readline-devel \ + rpm-build \ + systemd-devel \ + texinfo \ + tzdata \ + && microdnf --disableplugin=subscription-manager clean all + +RUN curl -sSL -o /tmp/libyang2.rpm https://ci1.netdef.org/artifact/LIBYANG-LIBYANGV2/shared/build-12/RedHat-8-x86_64-Packages/libyang2-2.0.7-1.el8.x86_64.rpm \ + && rpm -i /tmp/libyang2.rpm \ + && rm -f /tmp/libyang2.rpm + +RUN curl -sSL -o /tmp/libyang2-devel.rpm https://ci1.netdef.org/artifact/LIBYANG-LIBYANGV2/shared/build-12/RedHat-8-x86_64-Packages/libyang2-devel-2.0.7-1.el8.x86_64.rpm \ + && rpm -i /tmp/libyang2-devel.rpm \ + && rm -f /tmp/libyang2-devel.rpm + +RUN curl -sSL -o /tmp/librtr.rpm https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-149/RedHat-8-x86_64-Packages/librtr-0.8.0-1.el8.x86_64.rpm \ + && rpm -i /tmp/librtr.rpm \ + && rm -f /tmp/librtr.rpm + +RUN curl -sSL -o /tmp/librtr-devel.rpm https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-149/RedHat-8-x86_64-Packages/librtr-devel-0.8.0-1.el8.x86_64.rpm \ + && rpm -i /tmp/librtr-devel.rpm \ + && rm -f /tmp/librtr-devel.rpm + +COPY . /src + +ARG PKGVER + +RUN echo '%_smp_mflags %( echo "-j$(/usr/bin/getconf _NPROCESSORS_ONLN)"; )' >> /root/.rpmmacros \ + && cd /src \ + && ./bootstrap.sh \ + && ./configure \ + --enable-rpki \ + --enable-snmp=agentx \ + --enable-numeric-version \ + --with-pkg-extra-version="_git$PKGVER" \ + && make dist \ + && cd / \ + && mkdir -p /rpmbuild/{SOURCES,SPECS} \ + && cp /src/frr*.tar.gz /rpmbuild/SOURCES \ + && cp /src/redhat/frr.spec /rpmbuild/SPECS \ + && rpmbuild \ + --define "_topdir /rpmbuild" \ + -ba /rpmbuild/SPECS/frr.spec + +# This stage installs frr from the rpm +FROM registry.access.redhat.com/ubi8/ubi-minimal:${UBI8_MINIMAL_VERSION} +ARG FRR_IMAGE_TAG +ARG FRR_RELEASE +ARG FRR_NAME +ARG FRR_VENDOR +LABEL name=$FRR_NAME \ + vendor=$FRR_VENDOR \ + version=$FRR_IMAGE_TAG \ + release=$FRR_RELEASE + +ADD docker/ubi8-minimal/almalinux.repo /etc/yum.repos.d/almalinux.repo + +RUN rpm --import https://repo.almalinux.org/almalinux/RPM-GPG-KEY-AlmaLinux-8 + +RUN microdnf --disableplugin=subscription-manager --setopt=install_weak_deps=0 install \ + c-ares \ + initscripts \ + net-snmp-agent-libs \ + net-snmp-libs \ + openssl \ + python3 \ + shadow-utils \ + systemd \ + && microdnf --disableplugin=subscription-manager clean all + +RUN curl -sSL -o /tmp/libyang2.rpm https://ci1.netdef.org/artifact/LIBYANG-LIBYANGV2/shared/build-12/RedHat-8-x86_64-Packages/libyang2-2.0.7-1.el8.x86_64.rpm \ + && rpm -i /tmp/libyang2.rpm \ + && rm -f /tmp/libyang2.rpm + +RUN curl -sSL -o /tmp/librtr.rpm https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-149/RedHat-8-x86_64-Packages/librtr-0.8.0-1.el8.x86_64.rpm \ + && rpm -i /tmp/librtr.rpm \ + && rm -f /tmp/librtr.rpm + +COPY --from=ubi8-minimal-builder /rpmbuild/RPMS/ /pkgs/rpm/ + +# Install packages and create FRR files and folders. Be sure to own the config / PID files +RUN rpm -i /pkgs/rpm/x86_64/*.rpm \ + && rm -rf /pkgs \ + && rm -rf /mnt/rootfs/var/cache/* /mnt/rootfs/var/log/dnf* /mnt/rootfs/var/log/yum.* \ + && mkdir -p /var/run/frr \ + && chown -R frr:frr /etc/frr /var/run/frr + +# There is no package for tini, add it manually +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/ubi8-minimal/docker-start /usr/lib/frr/docker-start +CMD ["/usr/lib/frr/docker-start"] diff --git a/docker/ubi8-minimal/almalinux.repo b/docker/ubi8-minimal/almalinux.repo new file mode 100644 index 0000000000..9b9877b180 --- /dev/null +++ b/docker/ubi8-minimal/almalinux.repo @@ -0,0 +1,23 @@ +[AlmaLinux - baseos] +name=AlmaLinux $releasever - BaseOS +mirrorlist=https://mirrors.almalinux.org/mirrorlist/$releasever/baseos +# baseurl=https://repo.almalinux.org/almalinux/$releasever/BaseOS/$basearch/os/ +enabled=1 +gpgcheck=1 +countme=1 + +[AlmaLinux - appstream] +name=AlmaLinux $releasever - AppStream +mirrorlist=https://mirrors.almalinux.org/mirrorlist/$releasever/appstream +# baseurl=https://repo.almalinux.org/almalinux/$releasever/AppStream/$basearch/os/ +enabled=1 +gpgcheck=1 +countme=1 + +[AlmaLinux - powertools] +name=AlmaLinux $releasever - PowerTools +mirrorlist=https://mirrors.almalinux.org/mirrorlist/$releasever/powertools +# baseurl=https://repo.almalinux.org/almalinux/$releasever/PowerTools/$basearch/os/ +enabled=1 +gpgcheck=1 +countme=1 diff --git a/docker/ubi-8/build.sh b/docker/ubi8-minimal/build.sh index 0216636893..2aa45c9bf5 100755 --- a/docker/ubi-8/build.sh +++ b/docker/ubi8-minimal/build.sh @@ -5,37 +5,45 @@ set -e ## # Package version needs to be decimal ## -DISTRO=ubi-8 +DISTRO=ubi8-minimal + +UBI8_MINIMAL_VERSION=$1 +if [ -z "$UBI8_MINIMAL_VERSION" ]; then + UBI8_MINIMAL_VERSION="latest" +fi GITREV="$2" if [ -z "$GITREV" ];then GITREV="$(git rev-parse --short=10 HEAD)" fi -FRR_IMAGE_TAG="$1" +FRR_IMAGE_TAG="$3" if [ -z $FRR_IMAGE_TAG ];then - FRR_IMAGE_TAG="frr:ubi-8-$GITREV" + FRR_IMAGE_TAG="frr:ubi8-minimal-$GITREV" fi PKGVER="$(printf '%u\n' 0x$GITREV)" -FRR_RELEASE="$3" +FRR_RELEASE="$4" if [ -z $FRR_RELEASE ];then FRR_RELEASE=$(git describe --tags --abbrev=0) fi -FRR_NAME=$4 +FRR_NAME=$5 if [ -z $FRR_NAME ];then FRR_NAME=frr fi -FRR_VENDOR=$5 +FRR_VENDOR=$6 if [ -z $FRR_VENDOR ];then FRR_VENDOR=frr fi +DOCKERFILE_PATH="$(dirname $(realpath $0))/Dockerfile" + docker build \ --cache-from="frr:$DISTRO-builder-$GITREV" \ - --file=docker/$DISTRO/Dockerfile \ + --file="$DOCKERFILE_PATH" \ + --build-arg="UBI8_MINIMAL_VERSION=$UBI8_MINIMAL_VERSION" \ --build-arg="PKGVER=$PKGVER" \ --build-arg="FRR_IMAGE_TAG=$FRR_IMAGE_TAG" \ --build-arg="FRR_RELEASE=$FRR_RELEASE" \ diff --git a/docker/ubi-8/docker-start b/docker/ubi8-minimal/docker-start index d954142ab9..d954142ab9 100755 --- a/docker/ubi-8/docker-start +++ b/docker/ubi8-minimal/docker-start diff --git a/isisd/fabricd.c b/isisd/fabricd.c index 4fd39498a0..b229aa6717 100644 --- a/isisd/fabricd.c +++ b/isisd/fabricd.c @@ -207,10 +207,10 @@ struct fabricd *fabricd_new(struct isis_area *area) rv->area = area; rv->initial_sync_state = FABRICD_SYNC_PENDING; - rv->spftree = - isis_spftree_new(area, &area->lspdb[IS_LEVEL_2 - 1], - area->isis->sysid, ISIS_LEVEL2, SPFTREE_IPV4, - SPF_TYPE_FORWARD, F_SPFTREE_HOPCOUNT_METRIC); + rv->spftree = isis_spftree_new( + area, &area->lspdb[IS_LEVEL_2 - 1], area->isis->sysid, + ISIS_LEVEL2, SPFTREE_IPV4, SPF_TYPE_FORWARD, + F_SPFTREE_HOPCOUNT_METRIC, SR_ALGORITHM_SPF); rv->neighbors = skiplist_new(0, neighbor_entry_list_cmp, neighbor_entry_del_void); rv->neighbors_neighbors = hash_create(neighbor_entry_hash_key, @@ -273,8 +273,8 @@ void fabricd_initial_sync_hello(struct isis_circuit *circuit) if (IS_DEBUG_ADJ_PACKETS) zlog_debug( - "OpenFabric: Started initial synchronization with %s on %s", - sysid_print(circuit->u.p2p.neighbor->sysid), + "OpenFabric: Started initial synchronization with %pSY on %s", + circuit->u.p2p.neighbor->sysid, circuit->interface->name); } @@ -359,7 +359,9 @@ static uint8_t fabricd_calculate_fabric_tier(struct isis_area *area) return ISIS_TIER_UNDEFINED; } - zlog_info("OpenFabric: Found %s as furthest t0 from local system, dist == %u", rawlspid_print(furthest_t0->N.id), furthest_t0->d_N); + zlog_info( + "OpenFabric: Found %pLS as furthest t0 from local system, dist == %u", + furthest_t0->N.id, furthest_t0->d_N); struct isis_spftree *remote_tree = isis_run_hopcount_spf(area, furthest_t0->N.id, NULL); @@ -372,8 +374,9 @@ static uint8_t fabricd_calculate_fabric_tier(struct isis_area *area) isis_spftree_del(remote_tree); return ISIS_TIER_UNDEFINED; } else { - zlog_info("OpenFabric: Found %s as furthest from remote dist == %u", rawlspid_print(furthest_from_remote->N.id), - furthest_from_remote->d_N); + zlog_info( + "OpenFabric: Found %pLS as furthest from remote dist == %u", + furthest_from_remote->N.id, furthest_from_remote->d_N); } int64_t tier = furthest_from_remote->d_N - furthest_t0->d_N; diff --git a/isisd/isis_adjacency.c b/isisd/isis_adjacency.c index 1871078cc1..30b71537e0 100644 --- a/isisd/isis_adjacency.c +++ b/isisd/isis_adjacency.c @@ -283,6 +283,8 @@ void isis_adj_process_threeway(struct isis_adjacency *adj, } const char *isis_adj_name(const struct isis_adjacency *adj) { + static char buf[ISO_SYSID_STRLEN]; + if (!adj) return "NONE"; @@ -291,8 +293,9 @@ const char *isis_adj_name(const struct isis_adjacency *adj) dyn = dynhn_find_by_id(adj->circuit->isis, adj->sysid); if (dyn) return dyn->hostname; - else - return sysid_print(adj->sysid); + + snprintfrr(buf, sizeof(buf), "%pSY", adj->sysid); + return buf; } void isis_log_adj_change(struct isis_adjacency *adj, enum isis_adj_state old_state, @@ -439,9 +442,8 @@ void isis_adj_print(struct isis_adjacency *adj) if (dyn) zlog_debug("%s", dyn->hostname); - zlog_debug("SystemId %20s SNPA %s, level %d; Holding Time %d", - sysid_print(adj->sysid), snpa_print(adj->snpa), adj->level, - adj->hold_time); + zlog_debug("SystemId %20pSY SNPA %pSY, level %d; Holding Time %d", + adj->sysid, adj->snpa, adj->level, adj->hold_time); if (adj->ipv4_address_count) { zlog_debug("IPv4 Address(es):"); for (unsigned int i = 0; i < adj->ipv4_address_count; i++) @@ -530,7 +532,7 @@ void isis_adj_print_json(struct isis_adjacency *adj, struct json_object *json, time2string(adj->last_upd + adj->hold_time - now)); } - json_object_string_add(json, "snpa", snpa_print(adj->snpa)); + json_object_string_addf(json, "snpa", "%pSY", adj->snpa); } if (detail == ISIS_UI_LEVEL_DETAIL) { @@ -581,8 +583,7 @@ void isis_adj_print_json(struct isis_adjacency *adj, struct json_object *json, isis_mtid2str(adj->mt_set[i])); } } - json_object_string_add(iface_json, "snpa", - snpa_print(adj->snpa)); + json_object_string_addf(iface_json, "snpa", "%pSY", adj->snpa); if (adj->circuit && (adj->circuit->circ_type == CIRCUIT_T_BROADCAST)) { dyn = dynhn_find_by_id(adj->circuit->isis, adj->lanid); @@ -593,11 +594,8 @@ void isis_adj_print_json(struct isis_adjacency *adj, struct json_object *json, json_object_string_add(iface_json, "lan-id", buf); } else { - snprintfrr(buf, sizeof(buf), "%s-%02x", - sysid_print(adj->lanid), - adj->lanid[ISIS_SYS_ID_LEN]); - json_object_string_add(iface_json, "lan-id", - buf); + json_object_string_addf(iface_json, "lan-id", + "%pSY", adj->lanid); } json_object_int_add(iface_json, "lan-prio", @@ -626,12 +624,9 @@ void isis_adj_print_json(struct isis_adjacency *adj, struct json_object *json, area_addr_json); for (unsigned int i = 0; i < adj->area_address_count; i++) { - json_object_string_add( - area_addr_json, "isonet", - isonet_print(adj->area_addresses[i] - .area_addr, - adj->area_addresses[i] - .addr_len)); + json_object_string_addf( + area_addr_json, "isonet", "%pIS", + &adj->area_addresses[i]); } } if (adj->ipv4_address_count) { @@ -736,7 +731,7 @@ void isis_adj_print_vty(struct isis_adjacency *adj, struct vty *vty, + adj->hold_time - now); } else vty_out(vty, "- "); - vty_out(vty, "%-10s", snpa_print(adj->snpa)); + vty_out(vty, "%-10pSY", adj->snpa); vty_out(vty, "\n"); } @@ -780,7 +775,7 @@ void isis_adj_print_vty(struct isis_adjacency *adj, struct vty *vty, vty_out(vty, " %s\n", isis_mtid2str(adj->mt_set[i])); } - vty_out(vty, " SNPA: %s", snpa_print(adj->snpa)); + vty_out(vty, " SNPA: %pSY", adj->snpa); if (adj->circuit && (adj->circuit->circ_type == CIRCUIT_T_BROADCAST)) { dyn = dynhn_find_by_id(adj->circuit->isis, adj->lanid); @@ -788,9 +783,7 @@ void isis_adj_print_vty(struct isis_adjacency *adj, struct vty *vty, vty_out(vty, ", LAN id: %s.%02x", dyn->hostname, adj->lanid[ISIS_SYS_ID_LEN]); else - vty_out(vty, ", LAN id: %s.%02x", - sysid_print(adj->lanid), - adj->lanid[ISIS_SYS_ID_LEN]); + vty_out(vty, ", LAN id: %pPN", adj->lanid); vty_out(vty, "\n"); vty_out(vty, " LAN Priority: %u", @@ -811,11 +804,8 @@ void isis_adj_print_vty(struct isis_adjacency *adj, struct vty *vty, vty_out(vty, " Area Address(es):\n"); for (unsigned int i = 0; i < adj->area_address_count; i++) { - vty_out(vty, " %s\n", - isonet_print(adj->area_addresses[i] - .area_addr, - adj->area_addresses[i] - .addr_len)); + vty_out(vty, " %pIS\n", + &adj->area_addresses[i]); } } if (adj->ipv4_address_count) { diff --git a/isisd/isis_adjacency.h b/isisd/isis_adjacency.h index f02f7a68ea..c0c8e68145 100644 --- a/isisd/isis_adjacency.h +++ b/isisd/isis_adjacency.h @@ -69,7 +69,7 @@ struct isis_adjacency { struct isis_dis_record dis_record[DIS_RECORDS * ISIS_LEVELS]; enum isis_adj_state adj_state; /* adjacencyState */ enum isis_adj_usage adj_usage; /* adjacencyUsage */ - struct area_addr *area_addresses; /* areaAdressesOfNeighbour */ + struct iso_address *area_addresses; /* areaAdressesOfNeighbour */ unsigned int area_address_count; struct nlpids nlpids; /* protocols spoken ... */ struct in_addr *ipv4_addresses; diff --git a/isisd/isis_affinitymap.c b/isisd/isis_affinitymap.c new file mode 100644 index 0000000000..e681e820be --- /dev/null +++ b/isisd/isis_affinitymap.c @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* IS-IS affinity-map + * Copyright 2023 6WIND S.A. + */ + +#include <zebra.h> +#include "lib/if.h" +#include "lib/vrf.h" +#include "isisd/isisd.h" +#include "isisd/isis_affinitymap.h" + +#ifndef FABRICD + +static bool isis_affinity_map_check_use(const char *affmap_name) +{ + struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); + struct isis_area *area; + struct listnode *area_node, *fa_node; + struct flex_algo *fa; + struct affinity_map *map; + uint16_t pos; + + map = affinity_map_get(affmap_name); + pos = map->bit_position; + + for (ALL_LIST_ELEMENTS_RO(isis->area_list, area_node, area)) { + for (ALL_LIST_ELEMENTS_RO(area->flex_algos->flex_algos, fa_node, + fa)) { + if (admin_group_get(&fa->admin_group_exclude_any, + pos) || + admin_group_get(&fa->admin_group_include_any, + pos) || + admin_group_get(&fa->admin_group_include_all, pos)) + return true; + } + } + return false; +} + +static void isis_affinity_map_update(const char *affmap_name, uint16_t old_pos, + uint16_t new_pos) +{ + struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); + struct listnode *area_node, *fa_node; + struct isis_area *area; + struct flex_algo *fa; + bool changed; + + for (ALL_LIST_ELEMENTS_RO(isis->area_list, area_node, area)) { + changed = false; + for (ALL_LIST_ELEMENTS_RO(area->flex_algos->flex_algos, fa_node, + fa)) { + if (admin_group_get(&fa->admin_group_exclude_any, + old_pos)) { + admin_group_unset(&fa->admin_group_exclude_any, + old_pos); + admin_group_set(&fa->admin_group_exclude_any, + new_pos); + changed = true; + } + if (admin_group_get(&fa->admin_group_include_any, + old_pos)) { + admin_group_unset(&fa->admin_group_include_any, + old_pos); + admin_group_set(&fa->admin_group_include_any, + new_pos); + changed = true; + } + if (admin_group_get(&fa->admin_group_include_all, + old_pos)) { + admin_group_unset(&fa->admin_group_include_all, + old_pos); + admin_group_set(&fa->admin_group_include_all, + new_pos); + changed = true; + } + } + if (changed) + lsp_regenerate_schedule(area, area->is_type, 0); + } +} + +void isis_affinity_map_init(void) +{ + affinity_map_init(); + + affinity_map_set_check_use_hook(isis_affinity_map_check_use); + affinity_map_set_update_hook(isis_affinity_map_update); +} + +#endif /* ifndef FABRICD */ diff --git a/isisd/isis_affinitymap.h b/isisd/isis_affinitymap.h new file mode 100644 index 0000000000..c432e99f51 --- /dev/null +++ b/isisd/isis_affinitymap.h @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* IS-IS affinity-map header + * Copyright 2023 6WIND S.A. + */ + +#ifndef __ISIS_AFFINITYMAP_H__ +#define __ISIS_AFFINITYMAP_H__ + +#include "lib/affinitymap.h" + +#ifndef FABRICD + +#ifdef __cplusplus +extern "C" { +#endif + +extern void isis_affinity_map_init(void); + +#ifdef __cplusplus +} +#endif + +#endif /* ifndef FABRICD */ + +#endif /* __ISIS_AFFINITYMAP_H__ */ diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c index 124dcdd86d..feab451233 100644 --- a/isisd/isis_circuit.c +++ b/isisd/isis_circuit.c @@ -286,8 +286,7 @@ void isis_circuit_add_addr(struct isis_circuit *circuit, if (connected->address->family == AF_INET) { uint32_t addr = connected->address->u.prefix4.s_addr; addr = ntohl(addr); - if (IPV4_NET0(addr) || IPV4_NET127(addr) || IN_CLASSD(addr) - || IPV4_LINKLOCAL(addr)) + if (IPV4_NET0(addr) || IPV4_NET127(addr) || IN_CLASSD(addr)) return; for (ALL_LIST_ELEMENTS_RO(circuit->ip_addrs, node, ipv4)) @@ -696,10 +695,9 @@ int isis_circuit_up(struct isis_circuit *circuit) } #ifdef EXTREME_DEGUG if (IS_DEBUG_EVENTS) - zlog_debug("%s: if_id %d, isomtu %d snpa %s", __func__, - circuit->interface->ifindex, - ISO_MTU(circuit), - snpa_print(circuit->u.bc.snpa)); + zlog_debug("%s: if_id %d, isomtu %d snpa %pSY", + __func__, circuit->interface->ifindex, + ISO_MTU(circuit), circuit->u.bc.snpa); #endif /* EXTREME_DEBUG */ circuit->u.bc.adjdb[0] = list_new(); @@ -996,8 +994,8 @@ void isis_circuit_print_json(struct isis_circuit *circuit, json_object_string_add(iface_json, "level", circuit_t2string(circuit->is_type)); if (circuit->circ_type == CIRCUIT_T_BROADCAST) - json_object_string_add(iface_json, "snpa", - snpa_print(circuit->u.bc.snpa)); + json_object_string_addf(iface_json, "snpa", "%pSY", + circuit->u.bc.snpa); levels_json = json_object_new_array(); @@ -1123,8 +1121,7 @@ void isis_circuit_print_vty(struct isis_circuit *circuit, struct vty *vty, circuit_type2string(circuit->circ_type)); vty_out(vty, ", Level: %s", circuit_t2string(circuit->is_type)); if (circuit->circ_type == CIRCUIT_T_BROADCAST) - vty_out(vty, ", SNPA: %-10s", - snpa_print(circuit->u.bc.snpa)); + vty_out(vty, ", SNPA: %-10pSY", circuit->u.bc.snpa); vty_out(vty, "\n"); if (circuit->is_type & IS_LEVEL_1) { vty_out(vty, " Level-1 Information:\n"); diff --git a/isisd/isis_cli.c b/isisd/isis_cli.c index 7e1bb9255c..7c7a8d2389 100644 --- a/isisd/isis_cli.c +++ b/isisd/isis_cli.c @@ -23,6 +23,7 @@ #include "isisd/isis_misc.h" #include "isisd/isis_circuit.h" #include "isisd/isis_csm.h" +#include "isisd/isis_flex_algo.h" #include "isisd/isis_cli_clippy.c" @@ -1126,6 +1127,53 @@ void cli_show_isis_purge_origin(struct vty *vty, const struct lyd_node *dnode, vty_out(vty, " purge-originator\n"); } + +/* + * XPath: /frr-isisd:isis/instance/admin-group-send-zero + */ +DEFPY_YANG(isis_admin_group_send_zero, isis_admin_group_send_zero_cmd, + "[no] admin-group-send-zero", + NO_STR + "Allow sending the default admin-group value of 0x00000000.\n") +{ + nb_cli_enqueue_change(vty, "./admin-group-send-zero", NB_OP_MODIFY, + no ? "false" : "true"); + + return nb_cli_apply_changes(vty, NULL); +} + +void cli_show_isis_admin_group_send_zero(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults) +{ + if (!yang_dnode_get_bool(dnode, NULL)) + vty_out(vty, " no"); + vty_out(vty, " admin-group-send-zero\n"); +} + + +/* + * XPath: /frr-isisd:isis/instance/asla-legacy-flag + */ +DEFPY_HIDDEN(isis_asla_legacy_flag, isis_asla_legacy_flag_cmd, + "[no] asla-legacy-flag", + NO_STR "Set the legacy flag (aka. L-FLAG) in the ASLA Sub-TLV.\n") +{ + nb_cli_enqueue_change(vty, "./asla-legacy-flag", NB_OP_MODIFY, + no ? "false" : "true"); + + return nb_cli_apply_changes(vty, NULL); +} + +void cli_show_isis_asla_legacy_flag(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults) +{ + if (!yang_dnode_get_bool(dnode, NULL)) + vty_out(vty, " no"); + vty_out(vty, " asla-legacy-flag\n"); +} + /* * XPath: /frr-isisd:isis/instance/mpls-te */ @@ -1769,6 +1817,122 @@ void cli_show_isis_prefix_sid(struct vty *vty, const struct lyd_node *dnode, vty_out(vty, "\n"); } +#ifndef FABRICD +/* + * XPath: + * /frr-isisd:isis/instance/segment-routing/algorithm-prefix-sids/algorithm-prefix-sid + */ +DEFPY_YANG( + isis_sr_prefix_sid_algorithm, isis_sr_prefix_sid_algorithm_cmd, + "segment-routing prefix <A.B.C.D/M|X:X::X:X/M>$prefix\ + algorithm (128-255)$algorithm\ + <absolute$sid_type (16-1048575)$sid_value|index$sid_type (0-65535)$sid_value>\ + [<no-php-flag|explicit-null>$lh_behavior] [n-flag-clear$n_flag_clear]", + SR_STR + "Prefix SID\n" + "IPv4 Prefix\n" + "IPv6 Prefix\n" + "Algorithm Specific Prefix SID Configuration\n" + "Algorithm number\n" + "Specify the absolute value of Prefix Segment ID\n" + "The Prefix Segment ID value\n" + "Specify the index of Prefix Segment ID\n" + "The Prefix Segment ID index\n" + "Don't request Penultimate Hop Popping (PHP)\n" + "Upstream neighbor must replace prefix-sid with explicit null label\n" + "Not a node SID\n") +{ + nb_cli_enqueue_change(vty, ".", NB_OP_CREATE, NULL); + nb_cli_enqueue_change(vty, "./sid-value-type", NB_OP_MODIFY, sid_type); + nb_cli_enqueue_change(vty, "./sid-value", NB_OP_MODIFY, sid_value_str); + if (lh_behavior) { + const char *value; + + if (strmatch(lh_behavior, "no-php-flag")) + value = "no-php"; + else if (strmatch(lh_behavior, "explicit-null")) + value = "explicit-null"; + else + value = "php"; + + nb_cli_enqueue_change(vty, "./last-hop-behavior", NB_OP_MODIFY, + value); + } else + nb_cli_enqueue_change(vty, "./last-hop-behavior", NB_OP_MODIFY, + NULL); + nb_cli_enqueue_change(vty, "./n-flag-clear", NB_OP_MODIFY, + n_flag_clear ? "true" : "false"); + + return nb_cli_apply_changes( + vty, + "./segment-routing/algorithm-prefix-sids/algorithm-prefix-sid[prefix='%s'][algo='%s']", + prefix_str, algorithm_str); +} + +DEFPY_YANG( + no_isis_sr_prefix_algorithm_sid, no_isis_sr_prefix_sid_algorithm_cmd, + "no segment-routing prefix <A.B.C.D/M|X:X::X:X/M>$prefix\ + algorithm (128-255)$algorithm\ + [<absolute$sid_type (16-1048575)|index (0-65535)> [<no-php-flag|explicit-null>]]\ + [n-flag-clear]", + NO_STR SR_STR + "Prefix SID\n" + "IPv4 Prefix\n" + "IPv6 Prefix\n" + "Algorithm Specific Prefix SID Configuration\n" + "Algorithm number\n" + "Specify the absolute value of Prefix Segment ID\n" + "The Prefix Segment ID value\n" + "Specify the index of Prefix Segment ID\n" + "The Prefix Segment ID index\n" + "Don't request Penultimate Hop Popping (PHP)\n" + "Upstream neighbor must replace prefix-sid with explicit null label\n" + "Not a node SID\n") +{ + nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes( + vty, + "./segment-routing/algorithm-prefix-sids/algorithm-prefix-sid[prefix='%s'][algo='%s']", + prefix_str, algorithm_str); + return CMD_SUCCESS; +} +#endif /* ifndef FABRICD */ + +void cli_show_isis_prefix_sid_algorithm(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults) +{ + const char *prefix; + const char *lh_behavior; + const char *sid_value_type; + const char *sid_value; + bool n_flag_clear; + uint32_t algorithm; + + prefix = yang_dnode_get_string(dnode, "./prefix"); + sid_value_type = yang_dnode_get_string(dnode, "./sid-value-type"); + sid_value = yang_dnode_get_string(dnode, "./sid-value"); + algorithm = yang_dnode_get_uint32(dnode, "./algo"); + lh_behavior = yang_dnode_get_string(dnode, "./last-hop-behavior"); + n_flag_clear = yang_dnode_get_bool(dnode, "./n-flag-clear"); + + vty_out(vty, " segment-routing prefix %s", prefix); + vty_out(vty, " algorithm %u", algorithm); + if (strmatch(sid_value_type, "absolute")) + vty_out(vty, " absolute"); + else + vty_out(vty, " index"); + vty_out(vty, " %s", sid_value); + + if (strmatch(lh_behavior, "no-php")) + vty_out(vty, " no-php-flag"); + else if (strmatch(lh_behavior, "explicit-null")) + vty_out(vty, " explicit-null"); + if (n_flag_clear) + vty_out(vty, " n-flag-clear"); + vty_out(vty, "\n"); +} /* * XPath: /frr-isisd:isis/instance/fast-reroute/level-{1,2}/lfa/priority-limit @@ -2558,6 +2722,24 @@ void cli_show_ip_isis_circ_type(struct vty *vty, const struct lyd_node *dnode, } } +static int ag_change(struct vty *vty, int argc, struct cmd_token **argv, + const char *xpath, bool no, int start_idx) +{ + for (int i = start_idx; i < argc; i++) + nb_cli_enqueue_change(vty, xpath, + no ? NB_OP_DESTROY : NB_OP_CREATE, + argv[i]->arg); + return nb_cli_apply_changes(vty, NULL); +} + +static int ag_iter_cb(const struct lyd_node *dnode, void *arg) +{ + struct vty *vty = (struct vty *)arg; + + vty_out(vty, " %s", yang_dnode_get_string(dnode, ".")); + return YANG_ITER_CONTINUE; +} + /* * XPath: /frr-interface:lib/interface/frr-isisd:isis/network-type */ @@ -3026,6 +3208,26 @@ void cli_show_isis_log_adjacency(struct vty *vty, const struct lyd_node *dnode, } /* + * XPath: /frr-isisd:isis/instance/log-pdu-drops + */ +DEFPY_YANG(log_pdu_drops, log_pdu_drops_cmd, "[no] log-pdu-drops", + NO_STR "Log any dropped PDUs\n") +{ + nb_cli_enqueue_change(vty, "./log-pdu-drops", NB_OP_MODIFY, + no ? "false" : "true"); + + return nb_cli_apply_changes(vty, NULL); +} + +void cli_show_isis_log_pdu_drops(struct vty *vty, const struct lyd_node *dnode, + bool show_defaults) +{ + if (!yang_dnode_get_bool(dnode, NULL)) + vty_out(vty, " no"); + vty_out(vty, " log-pdu-drops\n"); +} + +/* * XPath: /frr-isisd:isis/instance/mpls/ldp-sync */ DEFPY(isis_mpls_ldp_sync, isis_mpls_ldp_sync_cmd, "mpls ldp-sync", @@ -3162,6 +3364,250 @@ void cli_show_isis_mpls_if_ldp_sync_holddown(struct vty *vty, yang_dnode_get_string(dnode, NULL)); } +DEFPY_YANG_NOSH(flex_algo, flex_algo_cmd, "flex-algo (128-255)$algorithm", + "Flexible Algorithm\n" + "Flexible Algorithm Number\n") +{ + int ret; + char xpath[XPATH_MAXLEN + 37]; + + snprintf(xpath, sizeof(xpath), + "%s/flex-algos/flex-algo[flex-algo='%ld']", VTY_CURR_XPATH, + algorithm); + + nb_cli_enqueue_change(vty, ".", NB_OP_CREATE, NULL); + + ret = nb_cli_apply_changes( + vty, "./flex-algos/flex-algo[flex-algo='%ld']", algorithm); + if (ret == CMD_SUCCESS) + VTY_PUSH_XPATH(ISIS_FLEX_ALGO_NODE, xpath); + + return ret; +} + +DEFPY_YANG(no_flex_algo, no_flex_algo_cmd, "no flex-algo (128-255)$algorithm", + NO_STR + "Flexible Algorithm\n" + "Flexible Algorithm Number\n") +{ + char xpath[XPATH_MAXLEN + 37]; + + snprintf(xpath, sizeof(xpath), + "%s/flex-algos/flex-algo[flex-algo='%ld']", VTY_CURR_XPATH, + algorithm); + + if (!yang_dnode_exists(vty->candidate_config->dnode, xpath)) { + vty_out(vty, "ISIS flex-algo %ld isn't exist.\n", algorithm); + return CMD_ERR_NO_MATCH; + } + + nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL); + return nb_cli_apply_changes_clear_pending( + vty, "./flex-algos/flex-algo[flex-algo='%ld']", algorithm); +} + +DEFPY_YANG(advertise_definition, advertise_definition_cmd, + "[no] advertise-definition", + NO_STR "Advertise Local Flexible Algorithm\n") +{ + nb_cli_enqueue_change(vty, "./advertise-definition", + no ? NB_OP_DESTROY : NB_OP_CREATE, + no ? NULL : "true"); + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG(affinity_include_any, affinity_include_any_cmd, + "[no] affinity include-any NAME...", + NO_STR + "Affinity configuration\n" + "Any Include with\n" + "Include NAME list\n") +{ + const char *xpath = "./affinity-include-anies/affinity-include-any"; + + return ag_change(vty, argc, argv, xpath, no, no ? 3 : 2); +} + +DEFPY_YANG(affinity_include_all, affinity_include_all_cmd, + "[no] affinity include-all NAME...", + NO_STR + "Affinity configuration\n" + "All Include with\n" + "Include NAME list\n") +{ + const char *xpath = "./affinity-include-alls/affinity-include-all"; + + return ag_change(vty, argc, argv, xpath, no, no ? 3 : 2); +} + +DEFPY_YANG(affinity_exclude_any, affinity_exclude_any_cmd, + "[no] affinity exclude-any NAME...", + NO_STR + "Affinity configuration\n" + "Any Exclude with\n" + "Exclude NAME list\n") +{ + const char *xpath = "./affinity-exclude-anies/affinity-exclude-any"; + + return ag_change(vty, argc, argv, xpath, no, no ? 3 : 2); +} + +DEFPY_YANG(prefix_metric, prefix_metric_cmd, "[no] prefix-metric", + NO_STR "Use Flex-Algo Prefix Metric\n") +{ + nb_cli_enqueue_change(vty, "./prefix-metric", + no ? NB_OP_DESTROY : NB_OP_CREATE, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG(dplane_sr_mpls, dplane_sr_mpls_cmd, "[no] dataplane sr-mpls", + NO_STR + "Advertise and participate in the specified Data-Planes\n" + "Advertise and participate in SR-MPLS data-plane\n") +{ + nb_cli_enqueue_change(vty, "./dplane-sr-mpls", + no ? NB_OP_DESTROY : NB_OP_CREATE, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_HIDDEN(dplane_srv6, dplane_srv6_cmd, "[no] dataplane srv6", + NO_STR + "Advertise and participate in the specified Data-Planes\n" + "Advertise and participate in SRv6 data-plane\n") +{ + + nb_cli_enqueue_change(vty, "./dplane-srv6", + no ? NB_OP_DESTROY : NB_OP_CREATE, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_HIDDEN(dplane_ip, dplane_ip_cmd, "[no] dataplane ip", + NO_STR + "Advertise and participate in the specified Data-Planes\n" + "Advertise and participate in IP data-plane\n") +{ + nb_cli_enqueue_change(vty, "./dplane-ip", + no ? NB_OP_DESTROY : NB_OP_CREATE, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG(metric_type, metric_type_cmd, + "[no] metric-type [igp$igp|te$te|delay$delay]", + NO_STR + "Metric-type used by flex-algo calculation\n" + "Use IGP metric (default)\n" + "Use Delay as metric\n" + "Use Traffic Engineering metric\n") +{ + const char *type = NULL; + + if (igp) { + type = "igp"; + } else if (te) { + type = "te-default"; + } else if (delay) { + type = "min-uni-link-delay"; + } else { + vty_out(vty, "Error: unknown metric type\n"); + return CMD_SUCCESS; + } + + if (!igp) + vty_out(vty, + "Warning: this version can advertise a Flex-Algorithm Definition (FAD) with the %s metric.\n" + "However, participation in a Flex-Algorithm with such a metric is not yet supported.\n", + type); + + nb_cli_enqueue_change(vty, "./metric-type", + no ? NB_OP_DESTROY : NB_OP_MODIFY, + no ? NULL : type); + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG(priority, priority_cmd, "[no] priority (0-255)$priority", + NO_STR + "Flex-Algo definition priority\n" + "Priority value\n") +{ + nb_cli_enqueue_change(vty, "./priority", + no ? NB_OP_DESTROY : NB_OP_MODIFY, + no ? NULL : priority_str); + return nb_cli_apply_changes(vty, NULL); +} + +void cli_show_isis_flex_algo(struct vty *vty, const struct lyd_node *dnode, + bool show_defaults) +{ + uint32_t algorithm; + enum flex_algo_metric_type metric_type; + uint32_t priority; + char type_str[10]; + + algorithm = yang_dnode_get_uint32(dnode, "./flex-algo"); + vty_out(vty, " flex-algo %u\n", algorithm); + + if (yang_dnode_exists(dnode, "./advertise-definition")) + vty_out(vty, " advertise-definition\n"); + + if (yang_dnode_exists(dnode, "./dplane-sr-mpls")) + vty_out(vty, " dataplane sr-mpls\n"); + if (yang_dnode_exists(dnode, "./dplane-srv6")) + vty_out(vty, " dataplane srv6\n"); + if (yang_dnode_exists(dnode, "./dplane-ip")) + vty_out(vty, " dataplane ip\n"); + + if (yang_dnode_exists(dnode, "./prefix-metric")) + vty_out(vty, " prefix-metric\n"); + + if (yang_dnode_exists(dnode, "./metric-type")) { + metric_type = yang_dnode_get_enum(dnode, "./metric-type"); + if (metric_type != MT_IGP) { + flex_algo_metric_type_print(type_str, sizeof(type_str), + metric_type); + vty_out(vty, " metric-type %s\n", type_str); + } + } + + if (yang_dnode_exists(dnode, "./priority")) { + priority = yang_dnode_get_uint32(dnode, "./priority"); + if (priority != FLEX_ALGO_PRIO_DEFAULT) + vty_out(vty, " priority %u\n", priority); + } + + if (yang_dnode_exists(dnode, + "./affinity-include-alls/affinity-include-all")) { + vty_out(vty, " affinity include-all"); + yang_dnode_iterate( + ag_iter_cb, vty, dnode, + "./affinity-include-alls/affinity-include-all"); + vty_out(vty, "\n"); + } + + if (yang_dnode_exists( + dnode, "./affinity-include-anies/affinity-include-any")) { + vty_out(vty, " affinity include-any"); + yang_dnode_iterate( + ag_iter_cb, vty, dnode, + "./affinity-include-anies/affinity-include-any"); + vty_out(vty, "\n"); + } + + if (yang_dnode_exists( + dnode, "./affinity-exclude-anies/affinity-exclude-any")) { + vty_out(vty, " affinity exclude-any"); + yang_dnode_iterate( + ag_iter_cb, vty, dnode, + "./affinity-exclude-anies/affinity-exclude-any"); + vty_out(vty, "\n"); + } +} + +void cli_show_isis_flex_algo_end(struct vty *vty, const struct lyd_node *dnode) +{ + vty_out(vty, " !\n"); +} + + void isis_cli_init(void) { install_element(CONFIG_NODE, &router_isis_cmd); @@ -3220,6 +3666,9 @@ void isis_cli_init(void) install_element(ISIS_NODE, &area_purge_originator_cmd); + install_element(ISIS_NODE, &isis_admin_group_send_zero_cmd); + install_element(ISIS_NODE, &isis_asla_legacy_flag_cmd); + install_element(ISIS_NODE, &isis_mpls_te_on_cmd); install_element(ISIS_NODE, &no_isis_mpls_te_on_cmd); install_element(ISIS_NODE, &isis_mpls_te_router_addr_cmd); @@ -3243,6 +3692,10 @@ void isis_cli_init(void) install_element(ISIS_NODE, &no_isis_sr_node_msd_cmd); install_element(ISIS_NODE, &isis_sr_prefix_sid_cmd); install_element(ISIS_NODE, &no_isis_sr_prefix_sid_cmd); +#ifndef FABRICD + install_element(ISIS_NODE, &isis_sr_prefix_sid_algorithm_cmd); + install_element(ISIS_NODE, &no_isis_sr_prefix_sid_algorithm_cmd); +#endif /* ifndef FABRICD */ install_element(ISIS_NODE, &isis_frr_lfa_priority_limit_cmd); install_element(ISIS_NODE, &isis_frr_lfa_tiebreaker_cmd); install_element(ISIS_NODE, &isis_frr_lfa_load_sharing_cmd); @@ -3290,6 +3743,7 @@ void isis_cli_init(void) install_element(INTERFACE_NODE, &isis_ti_lfa_cmd); install_element(ISIS_NODE, &log_adj_changes_cmd); + install_element(ISIS_NODE, &log_pdu_drops_cmd); install_element(ISIS_NODE, &isis_mpls_ldp_sync_cmd); install_element(ISIS_NODE, &no_isis_mpls_ldp_sync_cmd); @@ -3298,6 +3752,19 @@ void isis_cli_init(void) install_element(INTERFACE_NODE, &isis_mpls_if_ldp_sync_cmd); install_element(INTERFACE_NODE, &isis_mpls_if_ldp_sync_holddown_cmd); install_element(INTERFACE_NODE, &no_isis_mpls_if_ldp_sync_holddown_cmd); + + install_element(ISIS_NODE, &flex_algo_cmd); + install_element(ISIS_NODE, &no_flex_algo_cmd); + install_element(ISIS_FLEX_ALGO_NODE, &advertise_definition_cmd); + install_element(ISIS_FLEX_ALGO_NODE, &affinity_include_any_cmd); + install_element(ISIS_FLEX_ALGO_NODE, &affinity_include_all_cmd); + install_element(ISIS_FLEX_ALGO_NODE, &affinity_exclude_any_cmd); + install_element(ISIS_FLEX_ALGO_NODE, &dplane_sr_mpls_cmd); + install_element(ISIS_FLEX_ALGO_NODE, &dplane_srv6_cmd); + install_element(ISIS_FLEX_ALGO_NODE, &dplane_ip_cmd); + install_element(ISIS_FLEX_ALGO_NODE, &prefix_metric_cmd); + install_element(ISIS_FLEX_ALGO_NODE, &metric_type_cmd); + install_element(ISIS_FLEX_ALGO_NODE, &priority_cmd); } #endif /* ifndef FABRICD */ diff --git a/isisd/isis_common.h b/isisd/isis_common.h index c908dc2e24..2ded68f84d 100644 --- a/isisd/isis_common.h +++ b/isisd/isis_common.h @@ -11,14 +11,6 @@ #ifndef ISIS_COMMON_H #define ISIS_COMMON_H -/* - * Area Address - */ -struct area_addr { - uint8_t addr_len; - uint8_t area_addr[20]; -}; - struct isis_passwd { uint8_t len; #define ISIS_PASSWD_TYPE_UNUSED 0 diff --git a/isisd/isis_dynhn.c b/isisd/isis_dynhn.c index 446e522019..61c49d08a7 100644 --- a/isisd/isis_dynhn.c +++ b/isisd/isis_dynhn.c @@ -145,12 +145,10 @@ void dynhn_print_all(struct vty *vty, struct isis *isis) vty_out(vty, "Level System ID Dynamic Hostname\n"); 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); + vty_out(vty, "%pSY %-15s\n", dyn->id, dyn->hostname); } - vty_out(vty, " * %s %s\n", sysid_print(isis->sysid), - cmd_hostname_get()); + vty_out(vty, " * %pSY %s\n", isis->sysid, cmd_hostname_get()); return; } diff --git a/isisd/isis_events.c b/isisd/isis_events.c index 8a2a2ab971..32231a079f 100644 --- a/isisd/isis_events.c +++ b/isisd/isis_events.c @@ -217,8 +217,8 @@ void isis_event_auth_failure(char *area_tag, const char *error_string, uint8_t *sysid) { if (IS_DEBUG_EVENTS) - zlog_debug("ISIS-Evt (%s) Authentication failure %s from %s", - area_tag, error_string, sysid_print(sysid)); + zlog_debug("ISIS-Evt (%s) Authentication failure %s from %pSY", + area_tag, error_string, sysid); return; } diff --git a/isisd/isis_flex_algo.c b/isisd/isis_flex_algo.c new file mode 100644 index 0000000000..742a862fcd --- /dev/null +++ b/isisd/isis_flex_algo.c @@ -0,0 +1,336 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/********************************************************************* + * Copyright 2022 Hiroki Shirokura, LINE Corporation + * Copyright 2022 Masakazu Asama + * Copyright 2022 6WIND S.A. + * + * isis_flex_algo.c: IS-IS Flexible Algorithm + * + * Authors + * ------- + * Hiroki Shirokura + * Masakazu Asama + * Louis Scalbert + */ + +#include <zebra.h> + +#include "memory.h" +#include "stream.h" +#include "sbuf.h" +#include "network.h" +#include "command.h" +#include "bitfield.h" + +#include "isisd/isisd.h" +#include "isisd/isis_tlvs.h" +#include "isisd/isis_common.h" +#include "isisd/isis_mt.h" +#include "isisd/isis_misc.h" +#include "isisd/isis_adjacency.h" +#include "isisd/isis_circuit.h" +#include "isisd/isis_pdu.h" +#include "isisd/isis_lsp.h" +#include "isisd/isis_spf.h" +#include "isisd/isis_te.h" +#include "isisd/isis_sr.h" +#include "isisd/isis_spf_private.h" +#include "isisd/isis_flex_algo.h" + +#ifndef FABRICD +DEFINE_MTYPE_STATIC(ISISD, FLEX_ALGO, "ISIS Flex Algo"); + +void *isis_flex_algo_data_alloc(void *voidarg) +{ + struct isis_flex_algo_alloc_arg *arg = voidarg; + struct isis_flex_algo_data *data; + + data = XCALLOC(MTYPE_FLEX_ALGO, sizeof(*data)); + + for (int tree = SPFTREE_IPV4; tree < SPFTREE_COUNT; tree++) { + for (int level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) { + if (!(arg->area->is_type & level)) + continue; + data->spftree[tree][level - 1] = isis_spftree_new( + arg->area, &arg->area->lspdb[level - 1], + arg->area->isis->sysid, level, tree, + SPF_TYPE_FORWARD, 0, arg->algorithm); + } + } + + return data; +} + +void isis_flex_algo_data_free(void *voiddata) +{ + struct isis_flex_algo_data *data = voiddata; + + for (int tree = SPFTREE_IPV4; tree < SPFTREE_COUNT; tree++) + for (int level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) + if (data->spftree[tree][level - 1]) + isis_spftree_del( + data->spftree[tree][level - 1]); +} + +static struct isis_router_cap_fad * +isis_flex_algo_definition_cmp(struct isis_router_cap_fad *elected, + struct isis_router_cap_fad *fa) +{ + if (!elected || fa->fad.priority > elected->fad.priority || + (fa->fad.priority == elected->fad.priority && + lsp_id_cmp(fa->sysid, elected->sysid) > 0)) + return fa; + + return elected; +} + +/** + * @brief Look up the flex-algo definition with the highest priority in the LSP + * Database (LSDB). If the value of priority is the same, the flex-algo + * definition with the highest sysid will be selected. + * @param algorithm flex-algo algorithm number + * @param area pointer + * @param local router capability Flex-Algo Definition (FAD) double pointer. + * - fad is NULL: use the local router capability FAD from LSDB for the + * election. + * - fad is not NULL and *fad is NULL: use no local router capability FAD for + * the election. + * - fad and *fad are not NULL: uses the *fad local definition instead of the + * local definition from LSDB for the election. + * @return elected flex-algo-definition object if exist, else NULL + */ +static struct isis_router_cap_fad * +_isis_flex_algo_elected(int algorithm, const struct isis_area *area, + struct isis_router_cap_fad **fad) +{ + struct flex_algo *flex_ago; + const struct isis_lsp *lsp; + struct isis_router_cap_fad *fa, *elected = NULL; + + if (!flex_algo_id_valid(algorithm)) + return NULL; + + /* No elected FAD if the algorithm is not locally configured */ + flex_ago = flex_algo_lookup(area->flex_algos, algorithm); + if (!flex_ago) + return NULL; + + /* No elected FAD if no data-plane is enabled + * Currently, only Segment-Routing MPLS is supported. + * Segment-Routing SRv6 and IP will be configured in the future. + */ + if (!CHECK_FLAG(flex_ago->dataplanes, FLEX_ALGO_SR_MPLS)) + return NULL; + + /* + * Perform FAD comparison. First, compare the priority, and if they are + * the same, compare the sys-id. + */ + /* clang-format off */ + frr_each_const(lspdb, &area->lspdb[ISIS_LEVEL1 - 1], lsp) { + /* clang-format on */ + + if (!lsp->tlvs || !lsp->tlvs->router_cap) + continue; + + if (lsp->own_lsp && fad) + continue; + + fa = lsp->tlvs->router_cap->fads[algorithm]; + + if (!fa) + continue; + + assert(algorithm == fa->fad.algorithm); + + memcpy(fa->sysid, lsp->hdr.lsp_id, ISIS_SYS_ID_LEN + 2); + + elected = isis_flex_algo_definition_cmp(elected, fa); + } + + if (fad && *fad) + elected = isis_flex_algo_definition_cmp(elected, *fad); + + return elected; +} + +struct isis_router_cap_fad *isis_flex_algo_elected(int algorithm, + const struct isis_area *area) +{ + return _isis_flex_algo_elected(algorithm, area, NULL); +} + +/** + * @brief Check the Flex-Algo Definition is supported by the current FRR version + * @param flex-algo + * @return true if supported else false + */ +bool isis_flex_algo_supported(struct flex_algo *fad) +{ + if (fad->calc_type != CALC_TYPE_SPF) + return false; + if (fad->metric_type != MT_IGP) + return false; + if (fad->flags != 0) + return false; + if (fad->exclude_srlg) + return false; + if (fad->unsupported_subtlv) + return false; + + return true; +} + +/** + * @brief Look for the elected Flex-Algo Definition and check that it is + * supported by the current FRR version + * @param algorithm flex-algo algorithm number + * @param area pointer + * @param local router capability Flex-Algo Definition (FAD) double pointer. + * @return elected flex-algo-definition object if exist and supported, else NULL + */ +static struct isis_router_cap_fad * +_isis_flex_algo_elected_supported(int algorithm, const struct isis_area *area, + struct isis_router_cap_fad **fad) +{ + struct isis_router_cap_fad *elected_fad; + + elected_fad = _isis_flex_algo_elected(algorithm, area, fad); + if (!elected_fad) + return NULL; + + if (isis_flex_algo_supported(&elected_fad->fad)) + return elected_fad; + + return NULL; +} + +struct isis_router_cap_fad * +isis_flex_algo_elected_supported(int algorithm, const struct isis_area *area) +{ + return _isis_flex_algo_elected_supported(algorithm, area, NULL); +} + +struct isis_router_cap_fad * +isis_flex_algo_elected_supported_local_fad(int algorithm, + const struct isis_area *area, + struct isis_router_cap_fad **fad) +{ + return _isis_flex_algo_elected_supported(algorithm, area, fad); +} + +/** + * Check LSP is participating specified SR Algorithm + * + * @param lsp IS-IS lsp + * @param algorithm SR Algorithm + * @return Return true if sr-algorithm tlv includes specified + * algorithm in router capability tlv + */ +bool sr_algorithm_participated(const struct isis_lsp *lsp, uint8_t algorithm) +{ + if (!lsp || !lsp->tlvs || !lsp->tlvs->router_cap) + return false; + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) + if (lsp->tlvs->router_cap->algo[i] == algorithm) + return true; + return false; +} + +bool isis_flex_algo_constraint_drop(struct isis_spftree *spftree, + struct isis_lsp *lsp, + struct isis_extended_reach *reach) +{ + bool ret; + struct isis_ext_subtlvs *subtlvs = reach->subtlvs; + uint8_t lspid_orig[ISIS_SYS_ID_LEN + 2]; + uint8_t lspid_neigh[ISIS_SYS_ID_LEN + 2]; + struct isis_router_cap_fad *fad; + struct isis_asla_subtlvs *asla; + struct listnode *node; + uint32_t *link_admin_group = NULL; + uint32_t link_ext_admin_group_bitmap0; + struct admin_group *link_ext_admin_group = NULL; + + fad = isis_flex_algo_elected_supported(spftree->algorithm, + spftree->area); + if (!fad) + return true; + + for (ALL_LIST_ELEMENTS_RO(subtlvs->aslas, node, asla)) { + if (!CHECK_FLAG(asla->standard_apps, ISIS_SABM_FLAG_X)) + continue; + if (asla->legacy) { + if (IS_SUBTLV(subtlvs, EXT_ADM_GRP)) + link_admin_group = &subtlvs->adm_group; + + if (IS_SUBTLV(subtlvs, EXT_EXTEND_ADM_GRP) && + admin_group_nb_words(&subtlvs->ext_admin_group) != + 0) + link_ext_admin_group = + &subtlvs->ext_admin_group; + } else { + if (IS_SUBTLV(asla, EXT_ADM_GRP)) + link_admin_group = &asla->admin_group; + if (IS_SUBTLV(asla, EXT_EXTEND_ADM_GRP) && + admin_group_nb_words(&asla->ext_admin_group) != 0) + link_ext_admin_group = &asla->ext_admin_group; + } + break; + } + + /* RFC7308 section 2.3.1 + * A receiving node that notices that the AG differs from the first 32 + * bits of the EAG SHOULD report this mismatch to the operator. + */ + if (link_admin_group && link_ext_admin_group) { + link_ext_admin_group_bitmap0 = + admin_group_get_offset(link_ext_admin_group, 0); + if (*link_admin_group != link_ext_admin_group_bitmap0) { + memcpy(lspid_orig, lsp->hdr.lsp_id, + ISIS_SYS_ID_LEN + 2); + memcpy(lspid_neigh, reach->id, ISIS_SYS_ID_LEN + 2); + zlog_warn( + "ISIS-SPF: LSP from %pLS neighbor %pLS. Admin-group 0x%08x differs from ext admin-group 0x%08x.", + lspid_orig, lspid_neigh, *link_admin_group, + link_ext_admin_group_bitmap0); + } + } + + /* + * Exclude Any + */ + if (!admin_group_zero(&fad->fad.admin_group_exclude_any)) { + ret = admin_group_match_any(&fad->fad.admin_group_exclude_any, + link_admin_group, + link_ext_admin_group); + if (ret) + return true; + } + + /* + * Include Any + */ + if (!admin_group_zero(&fad->fad.admin_group_include_any)) { + ret = admin_group_match_any(&fad->fad.admin_group_include_any, + link_admin_group, + link_ext_admin_group); + if (!ret) + return true; + } + + /* + * Include All + */ + if (!admin_group_zero(&fad->fad.admin_group_include_all)) { + ret = admin_group_match_all(&fad->fad.admin_group_include_all, + link_admin_group, + link_ext_admin_group); + if (!ret) + return true; + } + + return false; +} + +#endif /* ifndef FABRICD */ diff --git a/isisd/isis_flex_algo.h b/isisd/isis_flex_algo.h new file mode 100644 index 0000000000..c475838243 --- /dev/null +++ b/isisd/isis_flex_algo.h @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/********************************************************************* + * Copyright 2022 Hiroki Shirokura, LINE Corporation + * Copyright 2022 Masakazu Asama + * Copyright 2022 6WIND S.A. + * + * isis_flex_algo.h: IS-IS Flexible Algorithm + * + * Authors + * ------- + * Hiroki Shirokura + * Masakazu Asama + * Louis Scalbert + */ + +#ifndef ISIS_FLEX_ALGO_H +#define ISIS_FLEX_ALGO_H + +#include "flex_algo.h" +#include "isisd/isis_constants.h" + +#ifndef FABRICD + +struct isis_flex_algo_data { + struct isis_spftree *spftree[SPFTREE_COUNT][ISIS_LEVELS]; + struct isis_area *area; +}; + +struct isis_flex_algo_alloc_arg { + uint8_t algorithm; + struct isis_area *area; +}; + +void *isis_flex_algo_data_alloc(void *arg); +void isis_flex_algo_data_free(void *data); + +struct isis_router_cap_fad * +isis_flex_algo_elected(int algorithm, const struct isis_area *area); +bool isis_flex_algo_supported(struct flex_algo *fad); +struct isis_router_cap_fad * +isis_flex_algo_elected_supported(int algorithm, const struct isis_area *area); +struct isis_router_cap_fad * +isis_flex_algo_elected_supported_local_fad(int algorithm, + const struct isis_area *area, + struct isis_router_cap_fad **fad); +struct isis_lsp; +bool sr_algorithm_participated(const struct isis_lsp *lsp, uint8_t algorithm); + +bool isis_flex_algo_constraint_drop(struct isis_spftree *spftree, + struct isis_lsp *lsp, + struct isis_extended_reach *reach); + +#endif /* ifndef FABRICD */ + +#endif /* ISIS_FLEX_ALGO_H */ diff --git a/isisd/isis_lfa.c b/isisd/isis_lfa.c index 7a25a92535..6f21f4cea2 100644 --- a/isisd/isis_lfa.c +++ b/isisd/isis_lfa.c @@ -355,6 +355,7 @@ bool isis_lfa_excise_node_check(const struct isis_spftree *spftree, struct tilfa_find_pnode_prefix_sid_args { uint32_t sid_index; + int algorithm; }; static int tilfa_find_pnode_prefix_sid_cb(const struct prefix *prefix, @@ -368,15 +369,17 @@ static int tilfa_find_pnode_prefix_sid_cb(const struct prefix *prefix, if (!subtlvs || subtlvs->prefix_sids.count == 0) return LSP_ITER_CONTINUE; - psid = (struct isis_prefix_sid *)subtlvs->prefix_sids.head; - - /* Require the node flag to be set. */ - if (!CHECK_FLAG(psid->flags, ISIS_PREFIX_SID_NODE)) - return LSP_ITER_CONTINUE; - - args->sid_index = psid->value; - - return LSP_ITER_STOP; + for (psid = (struct isis_prefix_sid *)subtlvs->prefix_sids.head; psid; + psid = psid->next) { + /* Require the node flag to be set. */ + if (!CHECK_FLAG(psid->flags, ISIS_PREFIX_SID_NODE)) + continue; + if (psid->algorithm != args->algorithm) + continue; + args->sid_index = psid->value; + return LSP_ITER_STOP; + } + return LSP_ITER_CONTINUE; } /* Find Prefix-SID associated to a System ID. */ @@ -390,6 +393,8 @@ static uint32_t tilfa_find_pnode_prefix_sid(struct isis_spftree *spftree, if (!lsp) return UINT32_MAX; + args.algorithm = spftree->algorithm; + args.sid_index = UINT32_MAX; isis_lsp_iterate_ip_reach(lsp, spftree->family, spftree->mtid, tilfa_find_pnode_prefix_sid_cb, &args); @@ -1098,7 +1103,8 @@ struct isis_spftree *isis_spf_reverse_run(const struct isis_spftree *spftree) spftree_reverse = isis_spftree_new( spftree->area, spftree->lspdb, spftree->sysid, spftree->level, spftree->tree_id, SPF_TYPE_REVERSE, - F_SPFTREE_NO_ADJACENCIES | F_SPFTREE_NO_ROUTES); + F_SPFTREE_NO_ADJACENCIES | F_SPFTREE_NO_ROUTES, + spftree->algorithm); isis_run_spf(spftree_reverse); return spftree_reverse; @@ -1194,7 +1200,8 @@ struct isis_spftree *isis_tilfa_compute(struct isis_area *area, /* Create post-convergence SPF tree. */ spftree_pc = isis_spftree_new(area, spftree->lspdb, spftree->sysid, spftree->level, spftree->tree_id, - SPF_TYPE_TI_LFA, spftree->flags); + SPF_TYPE_TI_LFA, spftree->flags, + spftree->algorithm); spftree_pc->lfa.old.spftree = spftree; spftree_pc->lfa.old.spftree_reverse = spftree_reverse; spftree_pc->lfa.protected_resource = *resource; @@ -1242,7 +1249,8 @@ int isis_spf_run_neighbors(struct isis_spftree *spftree) adj_node->lfa.spftree = isis_spftree_new( spftree->area, spftree->lspdb, adj_node->sysid, spftree->level, spftree->tree_id, SPF_TYPE_FORWARD, - F_SPFTREE_NO_ADJACENCIES | F_SPFTREE_NO_ROUTES); + F_SPFTREE_NO_ADJACENCIES | F_SPFTREE_NO_ROUTES, + spftree->algorithm); isis_run_spf(adj_node->lfa.spftree); } @@ -1466,8 +1474,8 @@ int isis_rlfa_activate(struct isis_spftree *spftree, struct rlfa *rlfa, if (ldp_label == MPLS_INVALID_LABEL) { if (IS_DEBUG_LFA) zlog_debug( - "ISIS-LFA: failed to activate RLFA: missing LDP label to reach PQ node through %s", - sysid_print(vadj->sadj->id)); + "ISIS-LFA: failed to activate RLFA: missing LDP label to reach PQ node through %pSY", + vadj->sadj->id); return -1; } @@ -1722,7 +1730,8 @@ struct isis_spftree *isis_rlfa_compute(struct isis_area *area, /* Create post-convergence SPF tree. */ spftree_pc = isis_spftree_new(area, spftree->lspdb, spftree->sysid, spftree->level, spftree->tree_id, - SPF_TYPE_RLFA, spftree->flags); + SPF_TYPE_RLFA, spftree->flags, + spftree->algorithm); spftree_pc->lfa.old.spftree = spftree; spftree_pc->lfa.old.spftree_reverse = spftree_reverse; spftree_pc->lfa.remote.max_metric = max_metric; diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c index d569f6bd5b..c07083a06d 100644 --- a/isisd/isis_lsp.c +++ b/isisd/isis_lsp.c @@ -46,6 +46,7 @@ #include "isisd/fabricd.h" #include "isisd/isis_tx_queue.h" #include "isisd/isis_nb.h" +#include "isisd/isis_flex_algo.h" DEFINE_MTYPE_STATIC(ISISD, ISIS_LSP, "ISIS LSP"); @@ -193,10 +194,9 @@ int lsp_compare(char *areatag, struct isis_lsp *lsp, uint32_t seqno, || (lsp->hdr.rem_lifetime != 0 && rem_lifetime != 0))) { if (IS_DEBUG_SNP_PACKETS) { zlog_debug( - "ISIS-Snp (%s): Compare LSP %s seq 0x%08x, cksum 0x%04hx, lifetime %hus", - areatag, rawlspid_print(lsp->hdr.lsp_id), - lsp->hdr.seqno, lsp->hdr.checksum, - lsp->hdr.rem_lifetime); + "ISIS-Snp (%s): Compare LSP %pLS seq 0x%08x, cksum 0x%04hx, lifetime %hus", + areatag, lsp->hdr.lsp_id, lsp->hdr.seqno, + lsp->hdr.checksum, lsp->hdr.rem_lifetime); zlog_debug( "ISIS-Snp (%s): is equal to ours seq 0x%08x, cksum 0x%04hx, lifetime %hus", areatag, seqno, checksum, rem_lifetime); @@ -223,9 +223,9 @@ int lsp_compare(char *areatag, struct isis_lsp *lsp, uint32_t seqno, && lsp->hdr.rem_lifetime)))) { if (IS_DEBUG_SNP_PACKETS) { zlog_debug( - "ISIS-Snp (%s): Compare LSP %s seq 0x%08x, cksum 0x%04hx, lifetime %hus", - areatag, rawlspid_print(lsp->hdr.lsp_id), seqno, - checksum, rem_lifetime); + "ISIS-Snp (%s): Compare LSP %pLS seq 0x%08x, cksum 0x%04hx, lifetime %hus", + areatag, lsp->hdr.lsp_id, seqno, checksum, + rem_lifetime); zlog_debug( "ISIS-Snp (%s): is newer than ours seq 0x%08x, cksum 0x%04hx, lifetime %hus", areatag, lsp->hdr.seqno, lsp->hdr.checksum, @@ -234,9 +234,10 @@ int lsp_compare(char *areatag, struct isis_lsp *lsp, uint32_t seqno, return LSP_NEWER; } if (IS_DEBUG_SNP_PACKETS) { - zlog_debug("ISIS-Snp (%s): Compare LSP %s seq 0x%08x, cksum 0x%04hx, lifetime %hus", - areatag, rawlspid_print(lsp->hdr.lsp_id), seqno, - checksum, rem_lifetime); + zlog_debug( + "ISIS-Snp (%s): Compare LSP %pLS seq 0x%08x, cksum 0x%04hx, lifetime %hus", + areatag, lsp->hdr.lsp_id, seqno, checksum, + rem_lifetime); zlog_debug( "ISIS-Snp (%s): is older than ours seq 0x%08x, cksum 0x%04hx, lifetime %hus", areatag, lsp->hdr.seqno, lsp->hdr.checksum, @@ -554,8 +555,8 @@ void lsp_update(struct isis_lsp *lsp, struct isis_lsp_hdr *hdr, if (lsp->own_lsp) { flog_err( EC_LIB_DEVELOPMENT, - "ISIS-Upd (%s): BUG updating LSP %s still marked as own LSP", - area->area_tag, rawlspid_print(lsp->hdr.lsp_id)); + "ISIS-Upd (%s): BUG updating LSP %pLS still marked as own LSP", + area->area_tag, lsp->hdr.lsp_id); lsp_clear_data(lsp); lsp->own_lsp = 0; } @@ -634,10 +635,8 @@ struct isis_lsp *lsp_new(struct isis_area *area, uint8_t *lsp_id, put_lsp_hdr(lsp, NULL, false); if (IS_DEBUG_EVENTS) - zlog_debug("New LSP with ID %s-%02x-%02x len %d seqnum %08x", - sysid_print(lsp_id), LSP_PSEUDO_ID(lsp->hdr.lsp_id), - LSP_FRAGMENT(lsp->hdr.lsp_id), lsp->hdr.pdu_len, - lsp->hdr.seqno); + zlog_debug("New LSP with ID %pLS len %d seqnum %08x", lsp_id, + lsp->hdr.pdu_len, lsp->hdr.seqno); return lsp; } @@ -704,7 +703,7 @@ void lspid_print(uint8_t *lsp_id, char *dest, size_t dest_len, char dynhost, else if (!memcmp(isis->sysid, lsp_id, ISIS_SYS_ID_LEN) && dynhost) snprintf(id, sizeof(id), "%.14s", cmd_hostname_get()); else - memcpy(id, sysid_print(lsp_id), 15); + snprintf(id, sizeof(id), "%pSY", lsp_id); if (frag) snprintf(dest, dest_len, "%s.%02x-%02x", id, @@ -881,6 +880,65 @@ static uint16_t lsp_refresh_time(struct isis_lsp *lsp, uint16_t rem_lifetime) return refresh_time; } +static void lsp_build_internal_reach_ipv4(struct isis_lsp *lsp, + struct isis_area *area, + struct prefix_ipv4 *ipv4, + uint32_t metric) +{ + struct sr_prefix_cfg *pcfgs[SR_ALGORITHM_COUNT] = {NULL}; + + if (area->oldmetric) { + lsp_debug( + "ISIS (%s): Adding old-style IP reachability for %pFX", + area->area_tag, ipv4); + isis_tlvs_add_oldstyle_ip_reach(lsp->tlvs, ipv4, metric); + } + + if (area->newmetric) { + lsp_debug("ISIS (%s): Adding te-style IP reachability for %pFX", + area->area_tag, ipv4); + + if (area->srdb.enabled) + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) { +#ifndef FABRICD + if (flex_algo_id_valid(i) && + !isis_flex_algo_elected_supported(i, area)) + continue; +#endif /* ifndef FABRICD */ + pcfgs[i] = + isis_sr_cfg_prefix_find(area, ipv4, i); + } + + isis_tlvs_add_extended_ip_reach(lsp->tlvs, ipv4, metric, false, + pcfgs); + } +} + +static void lsp_build_internal_reach_ipv6(struct isis_lsp *lsp, + struct isis_area *area, + struct prefix_ipv6 *ipv6, + uint32_t metric) +{ + struct sr_prefix_cfg *pcfgs[SR_ALGORITHM_COUNT] = {NULL}; + + lsp_debug("ISIS (%s): Adding IPv6 reachability for %pFX", + area->area_tag, ipv6); + + if (area->srdb.enabled) + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) { +#ifndef FABRICD + if (flex_algo_id_valid(i) && + !isis_flex_algo_elected_supported(i, area)) + continue; +#endif /* ifndef FABRICD */ + pcfgs[i] = isis_sr_cfg_prefix_find(area, ipv6, i); + } + + isis_tlvs_add_ipv6_reach(lsp->tlvs, isis_area_ipv6_topology(area), ipv6, + metric, false, pcfgs); +} + + static void lsp_build_ext_reach_ipv4(struct isis_lsp *lsp, struct isis_area *area) { @@ -906,13 +964,23 @@ static void lsp_build_ext_reach_ipv4(struct isis_lsp *lsp, isis_tlvs_add_oldstyle_ip_reach(lsp->tlvs, ipv4, metric); if (area->newmetric) { - struct sr_prefix_cfg *pcfg = NULL; + struct sr_prefix_cfg *pcfgs[SR_ALGORITHM_COUNT] = { + NULL}; if (area->srdb.enabled) - pcfg = isis_sr_cfg_prefix_find(area, ipv4); + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) { +#ifndef FABRICD + if (flex_algo_id_valid(i) && + !isis_flex_algo_elected_supported( + i, area)) + continue; +#endif /* ifndef FABRICD */ + pcfgs[i] = isis_sr_cfg_prefix_find( + area, ipv4, i); + } isis_tlvs_add_extended_ip_reach(lsp->tlvs, ipv4, metric, - true, pcfg); + true, pcfgs); } } } @@ -940,14 +1008,24 @@ static void lsp_build_ext_reach_ipv6(struct isis_lsp *lsp, metric = MAX_WIDE_PATH_METRIC; if (!src_p || !src_p->prefixlen) { - struct sr_prefix_cfg *pcfg = NULL; + struct sr_prefix_cfg *pcfgs[SR_ALGORITHM_COUNT] = { + NULL}; if (area->srdb.enabled) - pcfg = isis_sr_cfg_prefix_find(area, p); + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) { +#ifndef FABRICD + if (flex_algo_id_valid(i) && + !isis_flex_algo_elected_supported( + i, area)) + continue; +#endif /* ifndef FABRICD */ + pcfgs[i] = isis_sr_cfg_prefix_find( + area, p, i); + } isis_tlvs_add_ipv6_reach(lsp->tlvs, isis_area_ipv6_topology(area), - p, metric, true, pcfg); + p, metric, true, pcfgs); } else if (isis_area_ipv6_dstsrc_enabled(area)) { isis_tlvs_add_ipv6_dstsrc_reach(lsp->tlvs, ISIS_MT_IPV6_DSTSRC, @@ -1064,9 +1142,30 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area) /* Add Router Capability TLV. */ if (area->isis->router_id != 0) { - struct isis_router_cap cap = {}; + struct isis_router_cap *rcap; +#ifndef FABRICD + struct isis_router_cap_fad *rcap_fad; + struct listnode *node; + struct flex_algo *fa; +#endif /* ifndef FABRICD */ - cap.router_id.s_addr = area->isis->router_id; + rcap = isis_tlvs_init_router_capability(lsp->tlvs); + + rcap->router_id.s_addr = area->isis->router_id; + +#ifndef FABRICD + /* Set flex-algo definitions */ + for (ALL_LIST_ELEMENTS_RO(area->flex_algos->flex_algos, node, + fa)) { + if (!fa->advertise_definition) + continue; + lsp_debug("ISIS (%s): Flex-Algo Definition %u", + area->area_tag, fa->algorithm); + isis_tlvs_set_router_capability_fad(lsp->tlvs, fa, + fa->algorithm, + area->isis->sysid); + } +#endif /* ifndef FABRICD */ /* Add SR Sub-TLVs if SR is enabled. */ if (area->srdb.enabled) { @@ -1076,30 +1175,38 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area) /* SRGB first */ range_size = srdb->config.srgb_upper_bound - srdb->config.srgb_lower_bound + 1; - cap.srgb.flags = ISIS_SUBTLV_SRGB_FLAG_I - | ISIS_SUBTLV_SRGB_FLAG_V; - cap.srgb.range_size = range_size; - cap.srgb.lower_bound = srdb->config.srgb_lower_bound; + rcap->srgb.flags = ISIS_SUBTLV_SRGB_FLAG_I | + ISIS_SUBTLV_SRGB_FLAG_V; + rcap->srgb.range_size = range_size; + rcap->srgb.lower_bound = srdb->config.srgb_lower_bound; /* Then Algorithm */ - cap.algo[0] = SR_ALGORITHM_SPF; - cap.algo[1] = SR_ALGORITHM_UNSET; + rcap->algo[0] = SR_ALGORITHM_SPF; + rcap->algo[1] = SR_ALGORITHM_UNSET; +#ifndef FABRICD + for (ALL_LIST_ELEMENTS_RO(area->flex_algos->flex_algos, + node, fa)) { + if (fa->advertise_definition) + rcap_fad = rcap->fads[fa->algorithm]; + else + rcap_fad = NULL; + + if (!isis_flex_algo_elected_supported_local_fad( + fa->algorithm, area, &rcap_fad)) + continue; + lsp_debug("ISIS (%s): SR Algorithm %u", + area->area_tag, fa->algorithm); + rcap->algo[fa->algorithm] = fa->algorithm; + } +#endif /* ifndef FABRICD */ /* SRLB */ - cap.srlb.flags = 0; + rcap->srlb.flags = 0; range_size = srdb->config.srlb_upper_bound - srdb->config.srlb_lower_bound + 1; - cap.srlb.range_size = range_size; - cap.srlb.lower_bound = srdb->config.srlb_lower_bound; + rcap->srlb.range_size = range_size; + rcap->srlb.lower_bound = srdb->config.srlb_lower_bound; /* And finally MSD */ - cap.msd = srdb->config.msd; - } else { - /* Disable SR Algorithm */ - cap.algo[0] = SR_ALGORITHM_UNSET; - cap.algo[1] = SR_ALGORITHM_UNSET; + rcap->msd = srdb->config.msd; } - - isis_tlvs_set_router_capability(lsp->tlvs, &cap); - lsp_debug("ISIS (%s): Adding Router Capabilities information", - area->area_tag); } /* IPv4 address and TE router ID TLVs. @@ -1189,31 +1296,9 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area) struct listnode *ipnode; struct prefix_ipv4 *ipv4; for (ALL_LIST_ELEMENTS_RO(circuit->ip_addrs, ipnode, - ipv4)) { - if (area->oldmetric) { - lsp_debug( - "ISIS (%s): Adding old-style IP reachability for %pFX", - area->area_tag, ipv4); - isis_tlvs_add_oldstyle_ip_reach( - lsp->tlvs, ipv4, metric); - } - - if (area->newmetric) { - struct sr_prefix_cfg *pcfg = NULL; - - lsp_debug( - "ISIS (%s): Adding te-style IP reachability for %pFX", - area->area_tag, ipv4); - - if (area->srdb.enabled) - pcfg = isis_sr_cfg_prefix_find( - area, ipv4); - - isis_tlvs_add_extended_ip_reach( - lsp->tlvs, ipv4, metric, false, - pcfg); - } - } + ipv4)) + lsp_build_internal_reach_ipv4(lsp, area, ipv4, + metric); } if (circuit->ipv6_router && circuit->ipv6_non_link->count > 0) { @@ -1221,22 +1306,9 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area) struct prefix_ipv6 *ipv6; for (ALL_LIST_ELEMENTS_RO(circuit->ipv6_non_link, - ipnode, ipv6)) { - struct sr_prefix_cfg *pcfg = NULL; - - lsp_debug( - "ISIS (%s): Adding IPv6 reachability for %pFX", - area->area_tag, ipv6); - - if (area->srdb.enabled) - pcfg = isis_sr_cfg_prefix_find(area, - ipv6); - - isis_tlvs_add_ipv6_reach( - lsp->tlvs, - isis_area_ipv6_topology(area), ipv6, - metric, false, pcfg); - } + ipnode, ipv6)) + lsp_build_internal_reach_ipv6(lsp, area, ipv6, + metric); } switch (circuit->circ_type) { @@ -1250,10 +1322,8 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area) if (LSP_PSEUDO_ID(ne_id)) { if (area->oldmetric) { lsp_debug( - "ISIS (%s): Adding DIS %s.%02x as old-style neighbor", - area->area_tag, - sysid_print(ne_id), - LSP_PSEUDO_ID(ne_id)); + "ISIS (%s): Adding DIS %pPN as old-style neighbor", + area->area_tag, ne_id); isis_tlvs_add_oldstyle_reach( lsp->tlvs, ne_id, metric); @@ -1279,9 +1349,8 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area) if (area->oldmetric) { lsp_debug( - "ISIS (%s): Adding old-style is reach for %s", - area->area_tag, - sysid_print(ne_id)); + "ISIS (%s): Adding old-style is reach for %pSY", + area->area_tag, ne_id); isis_tlvs_add_oldstyle_reach( lsp->tlvs, ne_id, metric); } @@ -1424,12 +1493,12 @@ int lsp_generate(struct isis_area *area, int level) refresh_time, &area->t_lsp_refresh[level - 1]); if (IS_DEBUG_UPDATE_PACKETS) { - zlog_debug("ISIS-Upd (%s): Building L%d LSP %s, len %hu, seq 0x%08x, cksum 0x%04hx, lifetime %hus refresh %hus", - area->area_tag, level, - rawlspid_print(newlsp->hdr.lsp_id), - newlsp->hdr.pdu_len, newlsp->hdr.seqno, - newlsp->hdr.checksum, newlsp->hdr.rem_lifetime, - refresh_time); + zlog_debug( + "ISIS-Upd (%s): Building L%d LSP %pLS, len %hu, seq 0x%08x, cksum 0x%04hx, lifetime %hus refresh %hus", + area->area_tag, level, newlsp->hdr.lsp_id, + newlsp->hdr.pdu_len, newlsp->hdr.seqno, + newlsp->hdr.checksum, newlsp->hdr.rem_lifetime, + refresh_time); } sched_debug( "ISIS (%s): Built L%d LSP. Set triggered regenerate to non-pending.", @@ -1506,8 +1575,8 @@ static int lsp_regenerate(struct isis_area *area, int level) if (IS_DEBUG_UPDATE_PACKETS) { zlog_debug( - "ISIS-Upd (%s): Refreshed our L%d LSP %s, len %hu, seq 0x%08x, cksum 0x%04hx, lifetime %hus refresh %hus", - area->area_tag, level, rawlspid_print(lsp->hdr.lsp_id), + "ISIS-Upd (%s): Refreshed our L%d LSP %pLS, len %hu, seq 0x%08x, cksum 0x%04hx, lifetime %hus refresh %hus", + area->area_tag, level, lsp->hdr.lsp_id, lsp->hdr.pdu_len, lsp->hdr.seqno, lsp->hdr.checksum, lsp->hdr.rem_lifetime, refresh_time); } @@ -1698,9 +1767,9 @@ static void lsp_build_pseudo(struct isis_lsp *lsp, struct isis_circuit *circuit, lsp_clear_data(lsp); lsp->tlvs = isis_alloc_tlvs(); lsp_debug( - "ISIS (%s): Constructing pseudo LSP %s for interface %s level %d", - area->area_tag, rawlspid_print(lsp->hdr.lsp_id), - circuit->interface->name, level); + "ISIS (%s): Constructing pseudo LSP %pLS for interface %s level %d", + area->area_tag, lsp->hdr.lsp_id, circuit->interface->name, + level); lsp->level = level; /* RFC3787 section 4 SHOULD not set overload bit in pseudo LSPs */ @@ -1717,10 +1786,8 @@ static void lsp_build_pseudo(struct isis_lsp *lsp, struct isis_circuit *circuit, if (circuit->area->oldmetric) { isis_tlvs_add_oldstyle_reach(lsp->tlvs, ne_id, 0); - lsp_debug( - "ISIS (%s): Adding %s.%02x as old-style neighbor (self)", - area->area_tag, sysid_print(ne_id), - LSP_PSEUDO_ID(ne_id)); + lsp_debug("ISIS (%s): Adding %pPN as old-style neighbor (self)", + area->area_tag, ne_id); } if (circuit->area->newmetric) { if (area_is_mt(circuit->area)) @@ -1728,10 +1795,8 @@ static void lsp_build_pseudo(struct isis_lsp *lsp, struct isis_circuit *circuit, else mtid = ISIS_MT_DISABLE; isis_tlvs_add_extended_reach(lsp->tlvs, mtid, ne_id, 0, NULL); - lsp_debug( - "ISIS (%s): Adding %s.%02x as te-style neighbor (self)", - area->area_tag, sysid_print(ne_id), - LSP_PSEUDO_ID(ne_id)); + lsp_debug("ISIS (%s): Adding %pPN as te-style neighbor (self)", + area->area_tag, ne_id); } adj_list = list_new(); @@ -1740,8 +1805,8 @@ static void lsp_build_pseudo(struct isis_lsp *lsp, struct isis_circuit *circuit, for (ALL_LIST_ELEMENTS_RO(adj_list, node, adj)) { if (!(adj->level & level)) { lsp_debug( - "ISIS (%s): Ignoring neighbor %s, level does not intersect", - area->area_tag, sysid_print(adj->sysid)); + "ISIS (%s): Ignoring neighbor %pSY, level does not intersect", + area->area_tag, adj->sysid); continue; } @@ -1753,8 +1818,8 @@ static void lsp_build_pseudo(struct isis_lsp *lsp, struct isis_circuit *circuit, && !(level == IS_LEVEL_2 && adj->sys_type == ISIS_SYSTYPE_L2_IS)) { lsp_debug( - "ISIS (%s): Ignoring neighbor %s, level does not match", - area->area_tag, sysid_print(adj->sysid)); + "ISIS (%s): Ignoring neighbor %pSY, level does not match", + area->area_tag, adj->sysid); continue; } @@ -1762,18 +1827,16 @@ static void lsp_build_pseudo(struct isis_lsp *lsp, struct isis_circuit *circuit, if (circuit->area->oldmetric) { isis_tlvs_add_oldstyle_reach(lsp->tlvs, ne_id, 0); lsp_debug( - "ISIS (%s): Adding %s.%02x as old-style neighbor (peer)", - area->area_tag, sysid_print(ne_id), - LSP_PSEUDO_ID(ne_id)); + "ISIS (%s): Adding %pPN as old-style neighbor (peer)", + area->area_tag, ne_id); } if (circuit->area->newmetric) { isis_tlvs_add_extended_reach(lsp->tlvs, ISIS_MT_IPV4_UNICAST, ne_id, 0, NULL); lsp_debug( - "ISIS (%s): Adding %s.%02x as te-style neighbor (peer)", - area->area_tag, sysid_print(ne_id), - LSP_PSEUDO_ID(ne_id)); + "ISIS (%s): Adding %pPN as te-style neighbor (peer)", + area->area_tag, ne_id); } } list_delete(&adj_list); @@ -1832,10 +1895,9 @@ int lsp_generate_pseudo(struct isis_circuit *circuit, int level) if (IS_DEBUG_UPDATE_PACKETS) { zlog_debug( - "ISIS-Upd (%s): Built L%d Pseudo LSP %s, len %hu, seq 0x%08x, cksum 0x%04hx, lifetime %hus, refresh %hus", - circuit->area->area_tag, level, - rawlspid_print(lsp->hdr.lsp_id), lsp->hdr.pdu_len, - lsp->hdr.seqno, lsp->hdr.checksum, + "ISIS-Upd (%s): Built L%d Pseudo LSP %pLS, len %hu, seq 0x%08x, cksum 0x%04hx, lifetime %hus, refresh %hus", + circuit->area->area_tag, level, lsp->hdr.lsp_id, + lsp->hdr.pdu_len, lsp->hdr.seqno, lsp->hdr.checksum, lsp->hdr.rem_lifetime, refresh_time); } @@ -1863,8 +1925,8 @@ static int lsp_regenerate_pseudo(struct isis_circuit *circuit, int level) if (!lsp) { flog_err(EC_LIB_DEVELOPMENT, - "lsp_regenerate_pseudo: no l%d LSP %s found!", level, - rawlspid_print(lsp_id)); + "lsp_regenerate_pseudo: no l%d LSP %pLS found!", level, + lsp_id); return ISIS_ERROR; } @@ -1887,10 +1949,9 @@ static int lsp_regenerate_pseudo(struct isis_circuit *circuit, int level) if (IS_DEBUG_UPDATE_PACKETS) { zlog_debug( - "ISIS-Upd (%s): Refreshed L%d Pseudo LSP %s, len %hu, seq 0x%08x, cksum 0x%04hx, lifetime %hus, refresh %hus", - circuit->area->area_tag, level, - rawlspid_print(lsp->hdr.lsp_id), lsp->hdr.pdu_len, - lsp->hdr.seqno, lsp->hdr.checksum, + "ISIS-Upd (%s): Refreshed L%d Pseudo LSP %pLS, len %hu, seq 0x%08x, cksum 0x%04hx, lifetime %hus, refresh %hus", + circuit->area->area_tag, level, lsp->hdr.lsp_id, + lsp->hdr.pdu_len, lsp->hdr.seqno, lsp->hdr.checksum, lsp->hdr.rem_lifetime, refresh_time); } @@ -2101,10 +2162,9 @@ void lsp_tick(struct event *thread) if (lsp->age_out == 0) { zlog_debug( - "ISIS-Upd (%s): L%u LSP %s seq 0x%08x aged out", + "ISIS-Upd (%s): L%u LSP %pLS seq 0x%08x aged out", area->area_tag, lsp->level, - rawlspid_print(lsp->hdr.lsp_id), - lsp->hdr.seqno); + lsp->hdr.lsp_id, lsp->hdr.seqno); /* if we're aging out fragment 0, lsp_destroy() * below will delete all other fragments too, @@ -2207,11 +2267,10 @@ void _lsp_flood(struct isis_lsp *lsp, struct isis_circuit *circuit, const char *func, const char *file, int line) { if (IS_DEBUG_FLOODING) { - zlog_debug("Flooding LSP %s%s%s (From %s %s:%d)", - rawlspid_print(lsp->hdr.lsp_id), - circuit ? " except on " : "", - circuit ? circuit->interface->name : "", - func, file, line); + zlog_debug("Flooding LSP %pLS%s%s (From %s %s:%d)", + lsp->hdr.lsp_id, circuit ? " except on " : "", + circuit ? circuit->interface->name : "", func, file, + line); } if (!fabricd) diff --git a/isisd/isis_main.c b/isisd/isis_main.c index 2f4e4e0bd6..385cdcc350 100644 --- a/isisd/isis_main.c +++ b/isisd/isis_main.c @@ -29,6 +29,7 @@ #include "routemap.h" #include "affinitymap.h" +#include "isisd/isis_affinitymap.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" #include "isisd/isis_flags.h" @@ -281,7 +282,7 @@ int main(int argc, char **argv, char **envp) #endif /* FABRICD */ #ifndef FABRICD isis_cli_init(); -#endif /* ifdef FABRICD */ +#endif /* ifndef FABRICD */ isis_spf_init(); isis_redist_init(); isis_route_map_init(); @@ -290,7 +291,9 @@ int main(int argc, char **argv, char **envp) lsp_init(); mt_init(); - affinity_map_init(); +#ifndef FABRICD + isis_affinity_map_init(); +#endif /* ifndef FABRICD */ isis_zebra_init(master, instance); isis_bfd_init(master); diff --git a/isisd/isis_misc.c b/isisd/isis_misc.c index 95d24036ec..09ffa3479a 100644 --- a/isisd/isis_misc.c +++ b/isisd/isis_misc.c @@ -32,48 +32,13 @@ #include "isisd/isis_dynhn.h" /* staticly assigned vars for printing purposes */ +static char sys_hostname[ISO_SYSID_STRLEN]; struct in_addr new_prefix; -/* len of xx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xx */ -/* + place for #0 termination */ -char isonet[51]; /* len of xxYxxMxWxdxxhxxmxxs + place for #0 termination */ char datestring[20]; char nlpidstring[30]; /* - * This converts the isonet to its printable format - */ -const char *isonet_print(const uint8_t *from, int len) -{ - int i = 0; - char tbuf[4]; - isonet[0] = '\0'; - - if (!from) - return "unknown"; - - while (i < len) { - if (i & 1) { - snprintf(tbuf, sizeof(tbuf), "%02x", *(from + i)); - strlcat(isonet, tbuf, sizeof(isonet)); - } else { - if (i == (len - 1)) { /* No dot at the end of address */ - snprintf(tbuf, sizeof(tbuf), "%02x", - *(from + i)); - strlcat(isonet, tbuf, sizeof(isonet)); - } else { - snprintf(tbuf, sizeof(tbuf), "%02x.", - *(from + i)); - strlcat(isonet, tbuf, sizeof(isonet)); - } - } - i++; - } - - return isonet; -} - -/* * Returns 0 on error, length of buff on ok * extract dot from the dotted str, and insert all the number in a buff */ @@ -307,60 +272,6 @@ const char *isis_hello_padding2string(int hello_padding_type) return NULL; /* not reached */ } -/* - * Print functions - we print to static vars - */ -const char *snpa_print(const uint8_t *from) -{ - return isis_format_id(from, ISIS_SYS_ID_LEN); -} - -const char *sysid_print(const uint8_t *from) -{ - return isis_format_id(from, ISIS_SYS_ID_LEN); -} - -const char *rawlspid_print(const uint8_t *from) -{ - return isis_format_id(from, 8); -} - -#define FORMAT_ID_SIZE sizeof("0000.0000.0000.00-00") -const char *isis_format_id(const uint8_t *id, size_t len) -{ -#define FORMAT_BUF_COUNT 4 - static char buf_ring[FORMAT_BUF_COUNT][FORMAT_ID_SIZE]; - static size_t cur_buf = 0; - - char *rv; - - cur_buf++; - if (cur_buf >= FORMAT_BUF_COUNT) - cur_buf = 0; - - rv = buf_ring[cur_buf]; - - if (!id) { - snprintf(rv, FORMAT_ID_SIZE, "unknown"); - return rv; - } - - if (len < 6) { - snprintf(rv, FORMAT_ID_SIZE, "Short ID"); - return rv; - } - - snprintf(rv, FORMAT_ID_SIZE, "%02x%02x.%02x%02x.%02x%02x", id[0], id[1], - id[2], id[3], id[4], id[5]); - - if (len > 6) - snprintf(rv + 14, FORMAT_ID_SIZE - 14, ".%02x", id[6]); - if (len > 7) - snprintf(rv + 17, FORMAT_ID_SIZE - 17, "-%02x", id[7]); - - return rv; -} - const char *time2string(uint32_t time) { uint32_t rest; @@ -474,7 +385,8 @@ const char *print_sys_hostname(const uint8_t *sysid) return dyn->hostname; } - return sysid_print(sysid); + snprintfrr(sys_hostname, ISO_SYSID_STRLEN, "%pSY", sysid); + return sys_hostname; } /* @@ -508,11 +420,11 @@ void zlog_dump_data(void *data, int len) /* store hex str (for left side) */ snprintf(bytestr, sizeof(bytestr), "%02X ", *p); - strncat(hexstr, bytestr, sizeof(hexstr) - strlen(hexstr) - 1); + strlcat(hexstr, bytestr, sizeof(hexstr) - strlen(hexstr) - 1); /* store char str (for right side) */ snprintf(bytestr, sizeof(bytestr), "%c", c); - strncat(charstr, bytestr, + strlcat(charstr, bytestr, sizeof(charstr) - strlen(charstr) - 1); if ((i % 16) == 0) { @@ -523,9 +435,9 @@ void zlog_dump_data(void *data, int len) charstr[0] = 0; } else if ((i % 8) == 0) { /* half line: add whitespaces */ - strncat(hexstr, " ", + strlcat(hexstr, " ", sizeof(hexstr) - strlen(hexstr) - 1); - strncat(charstr, " ", + strlcat(charstr, " ", sizeof(charstr) - strlen(charstr) - 1); } p++; /* next byte */ diff --git a/isisd/isis_misc.h b/isisd/isis_misc.h index 01d9abe869..3a1d136b1d 100644 --- a/isisd/isis_misc.h +++ b/isisd/isis_misc.h @@ -28,11 +28,6 @@ int sysid2buff(uint8_t *, const char *); /* * Printing functions */ -const char *isonet_print(const uint8_t *, int len); -const char *sysid_print(const uint8_t *); -const char *snpa_print(const uint8_t *); -const char *rawlspid_print(const uint8_t *); -const char *isis_format_id(const uint8_t *id, size_t len); const char *time2string(uint32_t); const char *nlpid2str(uint8_t nlpid); /* typedef struct nlpids nlpids; */ diff --git a/isisd/isis_mt.c b/isisd/isis_mt.c index fcc0f53815..d04a24dc46 100644 --- a/isisd/isis_mt.c +++ b/isisd/isis_mt.c @@ -507,8 +507,8 @@ static void tlvs_add_mt_set(struct isis_area *area, struct isis_tlvs *tlvs, /* Check if MT is enable for this area */ if (!area_is_mt(area)) { lsp_debug( - "ISIS (%s): Adding %s.%02x as te-style neighbor (MT disable)", - area->area_tag, sysid_print(id), LSP_PSEUDO_ID(id)); + "ISIS (%s): Adding %pPN as te-style neighbor (MT disable)", + area->area_tag, id); isis_tlvs_add_extended_reach(tlvs, ISIS_MT_DISABLE, id, metric, ext); return; @@ -518,15 +518,12 @@ static void tlvs_add_mt_set(struct isis_area *area, struct isis_tlvs *tlvs, for (unsigned int i = 0; i < mt_count; i++) { uint16_t mtid = mt_set[i]; if (mt_set[i] == ISIS_MT_IPV4_UNICAST) { - lsp_debug( - "ISIS (%s): Adding %s.%02x as te-style neighbor", - area->area_tag, sysid_print(id), - LSP_PSEUDO_ID(id)); + lsp_debug("ISIS (%s): Adding %pPN as te-style neighbor", + area->area_tag, id); } else { lsp_debug( - "ISIS (%s): Adding %s.%02x as mt-style neighbor for %s", - area->area_tag, sysid_print(id), - LSP_PSEUDO_ID(id), isis_mtid2str(mtid)); + "ISIS (%s): Adding %pPN as mt-style neighbor for %s", + area->area_tag, id, isis_mtid2str(mtid)); } isis_tlvs_add_extended_reach(tlvs, mtid, id, metric, ext); } diff --git a/isisd/isis_nb.c b/isisd/isis_nb.c index 7dc3a0eb3d..6da8fa2d28 100644 --- a/isisd/isis_nb.c +++ b/isisd/isis_nb.c @@ -103,6 +103,20 @@ const struct frr_yang_module_info frr_isisd_info = { }, }, { + .xpath = "/frr-isisd:isis/instance/admin-group-send-zero", + .cbs = { + .cli_show = cli_show_isis_admin_group_send_zero, + .modify = isis_instance_admin_group_send_zero_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/asla-legacy-flag", + .cbs = { + .cli_show = cli_show_isis_asla_legacy_flag, + .modify = isis_instance_asla_legacy_flag_modify, + }, + }, + { .xpath = "/frr-isisd:isis/instance/lsp/mtu", .cbs = { .cli_show = cli_show_isis_lsp_mtu, @@ -559,6 +573,13 @@ const struct frr_yang_module_info frr_isisd_info = { }, }, { + .xpath = "/frr-isisd:isis/instance/log-pdu-drops", + .cbs = { + .cli_show = cli_show_isis_log_pdu_drops, + .modify = isis_instance_log_pdu_drops_modify, + }, + }, + { .xpath = "/frr-isisd:isis/instance/mpls-te", .cbs = { .cli_show = cli_show_isis_mpls_te, @@ -682,6 +703,118 @@ const struct frr_yang_module_info frr_isisd_info = { } }, { + .xpath = "/frr-isisd:isis/instance/segment-routing/algorithm-prefix-sids/algorithm-prefix-sid", + .cbs = { + .create = isis_instance_segment_routing_algorithm_prefix_sid_create, + .destroy = isis_instance_segment_routing_algorithm_prefix_sid_destroy, + .pre_validate = isis_instance_segment_routing_algorithm_prefix_sid_pre_validate, + .apply_finish = isis_instance_segment_routing_algorithm_prefix_sid_apply_finish, + .cli_show = cli_show_isis_prefix_sid_algorithm, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/segment-routing/algorithm-prefix-sids/algorithm-prefix-sid/sid-value-type", + .cbs = { + .modify = isis_instance_segment_routing_algorithm_prefix_sid_sid_value_type_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/segment-routing/algorithm-prefix-sids/algorithm-prefix-sid/sid-value", + .cbs = { + .modify = isis_instance_segment_routing_algorithm_prefix_sid_sid_value_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/segment-routing/algorithm-prefix-sids/algorithm-prefix-sid/last-hop-behavior", + .cbs = { + .modify = isis_instance_segment_routing_algorithm_prefix_sid_last_hop_behavior_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/segment-routing/algorithm-prefix-sids/algorithm-prefix-sid/n-flag-clear", + .cbs = { + .modify = isis_instance_segment_routing_algorithm_prefix_sid_n_flag_clear_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/flex-algos/flex-algo", + .cbs = { + .cli_show = cli_show_isis_flex_algo, + .cli_show_end = cli_show_isis_flex_algo_end, + .create = isis_instance_flex_algo_create, + .destroy = isis_instance_flex_algo_destroy, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/flex-algos/flex-algo/advertise-definition", + .cbs = { + .modify = isis_instance_flex_algo_advertise_definition_modify, + .destroy = isis_instance_flex_algo_advertise_definition_destroy, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/flex-algos/flex-algo/affinity-include-alls/affinity-include-all", + .cbs = { + .create = isis_instance_flex_algo_affinity_include_all_create, + .destroy = isis_instance_flex_algo_affinity_include_all_destroy, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/flex-algos/flex-algo/affinity-include-anies/affinity-include-any", + .cbs = { + .create = isis_instance_flex_algo_affinity_include_any_create, + .destroy = isis_instance_flex_algo_affinity_include_any_destroy, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/flex-algos/flex-algo/affinity-exclude-anies/affinity-exclude-any", + .cbs = { + .create = isis_instance_flex_algo_affinity_exclude_any_create, + .destroy = isis_instance_flex_algo_affinity_exclude_any_destroy, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/flex-algos/flex-algo/prefix-metric", + .cbs = { + .create = isis_instance_flex_algo_prefix_metric_create, + .destroy = isis_instance_flex_algo_prefix_metric_destroy, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/flex-algos/flex-algo/metric-type", + .cbs = { + .modify = isis_instance_flex_algo_metric_type_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/flex-algos/flex-algo/dplane-sr-mpls", + .cbs = { + .create = isis_instance_flex_algo_dplane_sr_mpls_create, + .destroy = isis_instance_flex_algo_dplane_sr_mpls_destroy, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/flex-algos/flex-algo/dplane-srv6", + .cbs = { + .create = isis_instance_flex_algo_dplane_srv6_create, + .destroy = isis_instance_flex_algo_dplane_srv6_destroy, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/flex-algos/flex-algo/dplane-ip", + .cbs = { + .create = isis_instance_flex_algo_dplane_ip_create, + .destroy = isis_instance_flex_algo_dplane_ip_destroy, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/flex-algos/flex-algo/priority", + .cbs = { + .modify = isis_instance_flex_algo_priority_modify, + .destroy = isis_instance_flex_algo_priority_destroy, + }, + }, + { .xpath = "/frr-isisd:isis/instance/mpls/ldp-sync", .cbs = { .cli_show = cli_show_isis_mpls_ldp_sync, diff --git a/isisd/isis_nb.h b/isisd/isis_nb.h index 480b2ce041..13efa36d78 100644 --- a/isisd/isis_nb.h +++ b/isisd/isis_nb.h @@ -29,6 +29,8 @@ int isis_instance_overload_on_startup_modify(struct nb_cb_modify_args *args); int isis_instance_advertise_high_metrics_modify(struct nb_cb_modify_args *args); int isis_instance_metric_style_modify(struct nb_cb_modify_args *args); int isis_instance_purge_originator_modify(struct nb_cb_modify_args *args); +int isis_instance_admin_group_send_zero_modify(struct nb_cb_modify_args *args); +int isis_instance_asla_legacy_flag_modify(struct nb_cb_modify_args *args); int isis_instance_lsp_mtu_modify(struct nb_cb_modify_args *args); int isis_instance_advertise_passive_only_modify(struct nb_cb_modify_args *args); int isis_instance_lsp_refresh_interval_level_1_modify( @@ -196,6 +198,7 @@ int isis_instance_fast_reroute_level_2_remote_lfa_prefix_list_modify( int isis_instance_fast_reroute_level_2_remote_lfa_prefix_list_destroy( struct nb_cb_destroy_args *args); int isis_instance_log_adjacency_changes_modify(struct nb_cb_modify_args *args); +int isis_instance_log_pdu_drops_modify(struct nb_cb_modify_args *args); int isis_instance_mpls_te_create(struct nb_cb_create_args *args); int isis_instance_mpls_te_destroy(struct nb_cb_destroy_args *args); int isis_instance_mpls_te_router_address_modify(struct nb_cb_modify_args *args); @@ -248,6 +251,67 @@ int isis_instance_segment_routing_prefix_sid_map_prefix_sid_last_hop_behavior_mo struct nb_cb_modify_args *args); int isis_instance_segment_routing_prefix_sid_map_prefix_sid_n_flag_clear_modify( struct nb_cb_modify_args *args); +int isis_instance_segment_routing_algorithm_prefix_sid_create( + struct nb_cb_create_args *args); +int isis_instance_segment_routing_algorithm_prefix_sid_destroy( + struct nb_cb_destroy_args *args); +int isis_instance_segment_routing_algorithm_prefix_sid_pre_validate( + struct nb_cb_pre_validate_args *args); +void isis_instance_segment_routing_algorithm_prefix_sid_apply_finish( + struct nb_cb_apply_finish_args *args); +int isis_instance_segment_routing_algorithm_prefix_sid_sid_value_type_modify( + struct nb_cb_modify_args *args); +int isis_instance_segment_routing_algorithm_prefix_sid_sid_value_modify( + struct nb_cb_modify_args *args); +int isis_instance_segment_routing_algorithm_prefix_sid_last_hop_behavior_modify( + struct nb_cb_modify_args *args); +int isis_instance_segment_routing_algorithm_prefix_sid_n_flag_clear_modify( + struct nb_cb_modify_args *args); +int isis_instance_flex_algo_create(struct nb_cb_create_args *args); +int isis_instance_flex_algo_destroy(struct nb_cb_destroy_args *args); +int isis_instance_flex_algo_advertise_definition_modify( + struct nb_cb_modify_args *args); +int isis_instance_flex_algo_advertise_definition_destroy( + struct nb_cb_destroy_args *args); +int isis_instance_flex_algo_affinity_include_any_create( + struct nb_cb_create_args *args); +int isis_instance_flex_algo_affinity_include_any_destroy( + struct nb_cb_destroy_args *args); +int isis_instance_flex_algo_affinity_include_all_create( + struct nb_cb_create_args *args); +int isis_instance_flex_algo_affinity_include_all_destroy( + struct nb_cb_destroy_args *args); +int isis_instance_flex_algo_affinity_exclude_any_create( + struct nb_cb_create_args *args); +int isis_instance_flex_algo_affinity_exclude_any_destroy( + struct nb_cb_destroy_args *args); +int isis_instance_flex_algo_prefix_metric_create( + struct nb_cb_create_args *args); +int isis_instance_flex_algo_prefix_metric_destroy( + struct nb_cb_destroy_args *args); +int isis_instance_flex_algo_dplane_sr_mpls_create( + struct nb_cb_create_args *args); +int isis_instance_flex_algo_dplane_sr_mpls_destroy( + struct nb_cb_destroy_args *args); +int isis_instance_flex_algo_dplane_srv6_create(struct nb_cb_create_args *args); +int isis_instance_flex_algo_dplane_srv6_destroy( + struct nb_cb_destroy_args *args); +int isis_instance_flex_algo_dplane_ip_create(struct nb_cb_create_args *args); +int isis_instance_flex_algo_dplane_ip_destroy(struct nb_cb_destroy_args *args); +int isis_instance_flex_algo_metric_type_modify(struct nb_cb_modify_args *args); +int isis_instance_flex_algo_priority_modify(struct nb_cb_modify_args *args); +int isis_instance_flex_algo_priority_destroy(struct nb_cb_destroy_args *args); +int isis_instance_flex_algo_frr_disable_modify(struct nb_cb_modify_args *args); +int isis_instance_flex_algo_frr_disable_destroy( + struct nb_cb_destroy_args *args); +int isis_instance_flex_algo_affinity_mapping_create( + struct nb_cb_create_args *args); +int isis_instance_flex_algo_affinity_mapping_destroy( + struct nb_cb_destroy_args *args); +int isis_instance_flex_algo_affinity_mapping_value_modify( + struct nb_cb_modify_args *args); +int isis_instance_flex_algo_affinity_mapping_value_destroy( + struct nb_cb_destroy_args *args); int isis_instance_mpls_ldp_sync_destroy(struct nb_cb_destroy_args *args); int isis_instance_mpls_ldp_sync_create(struct nb_cb_create_args *args); int isis_instance_mpls_ldp_sync_holddown_modify(struct nb_cb_modify_args *args); @@ -494,6 +558,12 @@ void cli_show_isis_purge_origin(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); void cli_show_isis_mpls_te(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); +void cli_show_isis_admin_group_send_zero(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults); +void cli_show_isis_asla_legacy_flag(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults); void cli_show_isis_mpls_te_router_addr(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); @@ -537,6 +607,9 @@ void cli_show_isis_node_msd(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); void cli_show_isis_prefix_sid(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); +void cli_show_isis_prefix_sid_algorithm(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults); void cli_show_isis_frr_lfa_priority_limit(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); @@ -609,6 +682,8 @@ void cli_show_ip_isis_priority(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); void cli_show_isis_log_adjacency(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); +void cli_show_isis_log_pdu_drops(struct vty *vty, const struct lyd_node *dnode, + bool show_defaults); void cli_show_isis_mpls_ldp_sync(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); void cli_show_isis_mpls_ldp_sync_holddown(struct vty *vty, @@ -620,6 +695,9 @@ void cli_show_isis_mpls_if_ldp_sync(struct vty *vty, void cli_show_isis_mpls_if_ldp_sync_holddown(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); +void cli_show_isis_flex_algo(struct vty *vty, const struct lyd_node *dnode, + bool show_defaults); +void cli_show_isis_flex_algo_end(struct vty *vty, const struct lyd_node *dnode); /* Notifications. */ void isis_notif_db_overload(const struct isis_area *area, bool overload); diff --git a/isisd/isis_nb_config.c b/isisd/isis_nb_config.c index 3817465a64..8a111b301d 100644 --- a/isisd/isis_nb_config.c +++ b/isisd/isis_nb_config.c @@ -21,6 +21,7 @@ #include "vrf.h" #include "ldp_sync.h" #include "link_state.h" +#include "affinitymap.h" #include "isisd/isisd.h" #include "isisd/isis_nb.h" @@ -39,8 +40,14 @@ #include "isisd/isis_redist.h" #include "isisd/isis_ldp_sync.h" #include "isisd/isis_dr.h" +#include "isisd/isis_sr.h" +#include "isisd/isis_flex_algo.h" #include "isisd/isis_zebra.h" +#define AFFINITY_INCLUDE_ANY 0 +#define AFFINITY_INCLUDE_ALL 1 +#define AFFINITY_EXCLUDE_ANY 2 + /* * XPath: /frr-isisd:isis/instance */ @@ -103,14 +110,14 @@ int isis_instance_is_type_modify(struct nb_cb_modify_args *args) } struct sysid_iter { - struct area_addr *addr; + struct iso_address *addr; bool same; }; static int sysid_iter_cb(const struct lyd_node *dnode, void *arg) { struct sysid_iter *iter = arg; - struct area_addr addr; + struct iso_address addr; const char *net; net = yang_dnode_get_string(dnode, NULL); @@ -130,7 +137,7 @@ static int sysid_iter_cb(const struct lyd_node *dnode, void *arg) int isis_instance_area_address_create(struct nb_cb_create_args *args) { struct isis_area *area; - struct area_addr addr, *addrr = NULL, *addrp = NULL; + struct iso_address addr, *addrr = NULL, *addrp = NULL; struct listnode *node; struct sysid_iter iter; uint8_t buff[255]; @@ -161,7 +168,8 @@ int isis_instance_area_address_create(struct nb_cb_create_args *args) } break; case NB_EV_PREPARE: - addrr = XMALLOC(MTYPE_ISIS_AREA_ADDR, sizeof(struct area_addr)); + addrr = XMALLOC(MTYPE_ISIS_AREA_ADDR, + sizeof(struct iso_address)); addrr->addr_len = dotformat2buff(buff, net_title); memcpy(addrr->area_addr, buff, addrr->addr_len); args->resource->ptr = addrr; @@ -217,7 +225,7 @@ int isis_instance_area_address_create(struct nb_cb_create_args *args) int isis_instance_area_address_destroy(struct nb_cb_destroy_args *args) { - struct area_addr addr, *addrp = NULL; + struct iso_address addr, *addrp = NULL; struct listnode *node; uint8_t buff[255]; struct isis_area *area; @@ -415,6 +423,71 @@ int isis_instance_purge_originator_modify(struct nb_cb_modify_args *args) return NB_OK; } + +/* + * XPath: /frr-isisd:isis/instance/admin-group-send-zero + */ +int isis_instance_admin_group_send_zero_modify(struct nb_cb_modify_args *args) +{ + struct isis_circuit *circuit; + struct isis_area *area; + struct listnode *node; + struct flex_algo *fa; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + area = nb_running_get_entry(args->dnode, NULL, true); + area->admin_group_send_zero = yang_dnode_get_bool(args->dnode, NULL); + + if (area->admin_group_send_zero) { + for (ALL_LIST_ELEMENTS_RO(area->flex_algos->flex_algos, node, + fa)) { + admin_group_allow_explicit_zero( + &fa->admin_group_exclude_any); + admin_group_allow_explicit_zero( + &fa->admin_group_include_any); + admin_group_allow_explicit_zero( + &fa->admin_group_include_all); + } + } else { + for (ALL_LIST_ELEMENTS_RO(area->flex_algos->flex_algos, node, + fa)) { + admin_group_disallow_explicit_zero( + &fa->admin_group_exclude_any); + admin_group_disallow_explicit_zero( + &fa->admin_group_include_any); + admin_group_disallow_explicit_zero( + &fa->admin_group_include_all); + } + } + + for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) + isis_link_params_update(circuit, circuit->interface); + + lsp_regenerate_schedule(area, IS_LEVEL_1 | IS_LEVEL_2, 0); + + return NB_OK; +} + + +/* + * XPath: /frr-isisd:isis/instance/asla-legacy-flag + */ +int isis_instance_asla_legacy_flag_modify(struct nb_cb_modify_args *args) +{ + struct isis_area *area; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + area = nb_running_get_entry(args->dnode, NULL, true); + area->asla_legacy_flag = yang_dnode_get_bool(args->dnode, NULL); + lsp_regenerate_schedule(area, IS_LEVEL_1 | IS_LEVEL_2, 0); + + return NB_OK; +} + /* * XPath: /frr-isisd:isis/instance/lsp/mtu */ @@ -1830,6 +1903,23 @@ int isis_instance_log_adjacency_changes_modify(struct nb_cb_modify_args *args) } /* + * XPath: /frr-isisd:isis/instance/log-pdu-drops + */ +int isis_instance_log_pdu_drops_modify(struct nb_cb_modify_args *args) +{ + struct isis_area *area; + bool log = yang_dnode_get_bool(args->dnode, NULL); + + if (args->event != NB_EV_APPLY) + return NB_OK; + + area = nb_running_get_entry(args->dnode, NULL, true); + area->log_pdu_drops = log ? 1 : 0; + + return NB_OK; +} + +/* * XPath: /frr-isisd:isis/instance/mpls-te */ int isis_instance_mpls_te_create(struct nb_cb_create_args *args) @@ -2258,7 +2348,7 @@ int isis_instance_segment_routing_prefix_sid_map_prefix_sid_create( area = nb_running_get_entry(args->dnode, NULL, true); yang_dnode_get_prefix(&prefix, args->dnode, "./prefix"); - pcfg = isis_sr_cfg_prefix_add(area, &prefix); + pcfg = isis_sr_cfg_prefix_add(area, &prefix, SR_ALGORITHM_SPF); nb_running_set_entry(args->dnode, pcfg); return NB_OK; @@ -2449,6 +2539,833 @@ int isis_instance_segment_routing_prefix_sid_map_prefix_sid_n_flag_clear_modify( } /* + * XPath: + * /frr-isisd:isis/instance/segment-routing/algorithm-prefix-sids/algorithm-prefix-sid + */ +int isis_instance_segment_routing_algorithm_prefix_sid_create( + struct nb_cb_create_args *args) +{ + struct isis_area *area; + struct prefix prefix; + struct sr_prefix_cfg *pcfg; + uint32_t algorithm; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + area = nb_running_get_entry(args->dnode, NULL, true); + yang_dnode_get_prefix(&prefix, args->dnode, "./prefix"); + algorithm = yang_dnode_get_uint32(args->dnode, "./algo"); + + pcfg = isis_sr_cfg_prefix_add(area, &prefix, algorithm); + pcfg->algorithm = algorithm; + nb_running_set_entry(args->dnode, pcfg); + + return NB_OK; +} + +int isis_instance_segment_routing_algorithm_prefix_sid_destroy( + struct nb_cb_destroy_args *args) +{ + struct sr_prefix_cfg *pcfg; + struct isis_area *area; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + pcfg = nb_running_unset_entry(args->dnode); + area = pcfg->area; + isis_sr_cfg_prefix_del(pcfg); + lsp_regenerate_schedule(area, area->is_type, 0); + + return NB_OK; +} + +int isis_instance_segment_routing_algorithm_prefix_sid_pre_validate( + struct nb_cb_pre_validate_args *args) +{ + const struct lyd_node *area_dnode; + struct isis_area *area; + struct prefix prefix; + uint32_t srgb_lbound; + uint32_t srgb_ubound; + uint32_t srgb_range; + uint32_t sid; + enum sr_sid_value_type sid_type; + struct isis_prefix_sid psid = {}; + + yang_dnode_get_prefix(&prefix, args->dnode, "./prefix"); + srgb_lbound = yang_dnode_get_uint32( + args->dnode, "../../label-blocks/srgb/lower-bound"); + srgb_ubound = yang_dnode_get_uint32( + args->dnode, "../../label-blocks/srgb/upper-bound"); + sid = yang_dnode_get_uint32(args->dnode, "./sid-value"); + sid_type = yang_dnode_get_enum(args->dnode, "./sid-value-type"); + + /* Check for invalid indexes/labels. */ + srgb_range = srgb_ubound - srgb_lbound + 1; + psid.value = sid; + switch (sid_type) { + case SR_SID_VALUE_TYPE_INDEX: + if (sid >= srgb_range) { + snprintf(args->errmsg, args->errmsg_len, + "SID index %u falls outside local SRGB range", + sid); + return NB_ERR_VALIDATION; + } + break; + case SR_SID_VALUE_TYPE_ABSOLUTE: + if (!IS_MPLS_UNRESERVED_LABEL(sid)) { + snprintf(args->errmsg, args->errmsg_len, + "Invalid absolute SID %u", sid); + return NB_ERR_VALIDATION; + } + SET_FLAG(psid.flags, ISIS_PREFIX_SID_VALUE); + SET_FLAG(psid.flags, ISIS_PREFIX_SID_LOCAL); + break; + } + + /* Check for Prefix-SID collisions. */ + area_dnode = yang_dnode_get_parent(args->dnode, "instance"); + area = nb_running_get_entry(area_dnode, NULL, false); + if (!area) + return NB_OK; + + for (int tree = SPFTREE_IPV4; tree < SPFTREE_COUNT; tree++) { + for (int level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) { + struct isis_spftree *spftree; + struct isis_vertex *vertex_psid; + + if (!(area->is_type & level)) + continue; + spftree = area->spftree[tree][level - 1]; + if (!spftree) + continue; + + vertex_psid = + isis_spf_prefix_sid_lookup(spftree, &psid); + if (vertex_psid && + !prefix_same(&vertex_psid->N.ip.p.dest, &prefix)) { + snprintfrr( + args->errmsg, args->errmsg_len, + "Prefix-SID collision detected, SID %s %u is already in use by prefix %pFX (L%u)", + CHECK_FLAG(psid.flags, + ISIS_PREFIX_SID_VALUE) + ? "label" + : "index", + psid.value, &vertex_psid->N.ip.p.dest, + level); + return NB_ERR_VALIDATION; + } + } + } + + return NB_OK; +} + +void isis_instance_segment_routing_algorithm_prefix_sid_apply_finish( + struct nb_cb_apply_finish_args *args) +{ + struct sr_prefix_cfg *pcfg; + struct isis_area *area; + + pcfg = nb_running_get_entry(args->dnode, NULL, true); + area = pcfg->area; + lsp_regenerate_schedule(area, area->is_type, 0); +} + +/* + * XPath: + * /frr-isisd:isis/instance/segment-routing/algorithm-prefix-sids/algorithm-prefix-sid/sid-value-type + */ +int isis_instance_segment_routing_algorithm_prefix_sid_sid_value_type_modify( + struct nb_cb_modify_args *args) +{ + struct sr_prefix_cfg *pcfg; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + pcfg = nb_running_get_entry(args->dnode, NULL, true); + pcfg->sid_type = yang_dnode_get_enum(args->dnode, NULL); + + return NB_OK; +} + +/* + * XPath: + * /frr-isisd:isis/instance/segment-routing/algorithm-prefix-sids/algorithm-prefix-sid/sid-value + */ +int isis_instance_segment_routing_algorithm_prefix_sid_sid_value_modify( + struct nb_cb_modify_args *args) +{ + struct sr_prefix_cfg *pcfg; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + pcfg = nb_running_get_entry(args->dnode, NULL, true); + pcfg->sid = yang_dnode_get_uint32(args->dnode, NULL); + + return NB_OK; +} + +/* + * XPath: + * /frr-isisd:isis/instance/segment-routing/algorithm-prefix-sid-map/algorithm-prefix-sid/last-hop-behavior + */ +int isis_instance_segment_routing_algorithm_prefix_sid_last_hop_behavior_modify( + struct nb_cb_modify_args *args) +{ + struct sr_prefix_cfg *pcfg; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + pcfg = nb_running_get_entry(args->dnode, NULL, true); + pcfg->last_hop_behavior = yang_dnode_get_enum(args->dnode, NULL); + + return NB_OK; +} + +/* + * XPath: + * /frr-isisd:isis/instance/segment-routing/algorithm-prefix-sids/algorithm-prefix-sid/n-flag-clear + */ +int isis_instance_segment_routing_algorithm_prefix_sid_n_flag_clear_modify( + struct nb_cb_modify_args *args) +{ + struct sr_prefix_cfg *pcfg; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + pcfg = nb_running_get_entry(args->dnode, NULL, true); + pcfg->n_flag_clear = yang_dnode_get_bool(args->dnode, NULL); + + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/flex-algos/flex-algo + */ +int isis_instance_flex_algo_create(struct nb_cb_create_args *args) +{ + struct isis_area *area; + struct flex_algo *fa; + bool advertise; + uint32_t algorithm; + uint32_t priority = FLEX_ALGO_PRIO_DEFAULT; + struct isis_flex_algo_alloc_arg arg; + + algorithm = yang_dnode_get_uint32(args->dnode, "./flex-algo"); + advertise = yang_dnode_exists(args->dnode, "./advertise-definition"); + + switch (args->event) { + case NB_EV_APPLY: + area = nb_running_get_entry(args->dnode, NULL, true); + arg.algorithm = algorithm; + arg.area = area; + fa = flex_algo_alloc(area->flex_algos, algorithm, &arg); + fa->priority = priority; + fa->advertise_definition = advertise; + if (area->admin_group_send_zero) { + admin_group_allow_explicit_zero( + &fa->admin_group_exclude_any); + admin_group_allow_explicit_zero( + &fa->admin_group_include_any); + admin_group_allow_explicit_zero( + &fa->admin_group_include_all); + } + lsp_regenerate_schedule(area, area->is_type, 0); + break; + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + } + + return NB_OK; +} + +int isis_instance_flex_algo_destroy(struct nb_cb_destroy_args *args) +{ + struct isis_area *area; + uint32_t algorithm; + + algorithm = yang_dnode_get_uint32(args->dnode, "./flex-algo"); + area = nb_running_get_entry(args->dnode, NULL, true); + + switch (args->event) { + case NB_EV_APPLY: + flex_algo_delete(area->flex_algos, algorithm); + lsp_regenerate_schedule(area, area->is_type, 0); + break; + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/flex-algos/flex-algo/advertise-definition + */ +int isis_instance_flex_algo_advertise_definition_modify( + struct nb_cb_modify_args *args) +{ + struct isis_area *area; + struct flex_algo *fa; + bool advertise; + uint32_t algorithm; + + + algorithm = yang_dnode_get_uint32(args->dnode, "./../flex-algo"); + advertise = yang_dnode_exists(args->dnode, "./../advertise-definition"); + + switch (args->event) { + case NB_EV_APPLY: + area = nb_running_get_entry(args->dnode, NULL, true); + fa = flex_algo_lookup(area->flex_algos, algorithm); + if (!fa) { + snprintf(args->errmsg, args->errmsg_len, + "flex-algo object not found"); + return NB_ERR_RESOURCE; + } + fa->advertise_definition = advertise; + lsp_regenerate_schedule(area, area->is_type, 0); + break; + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + } + + return NB_OK; +} + +int isis_instance_flex_algo_advertise_definition_destroy( + struct nb_cb_destroy_args *args) +{ + struct isis_area *area; + struct flex_algo *fa; + uint32_t algorithm; + + area = nb_running_get_entry(args->dnode, NULL, true); + + algorithm = yang_dnode_get_uint32(args->dnode, "./../flex-algo"); + + switch (args->event) { + case NB_EV_APPLY: + fa = flex_algo_lookup(area->flex_algos, algorithm); + if (!fa) { + snprintf(args->errmsg, args->errmsg_len, + "flex-algo object not found"); + return NB_ERR_RESOURCE; + } + fa->advertise_definition = false; + lsp_regenerate_schedule(area, area->is_type, 0); + break; + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + } + + return NB_OK; +} + +static int isis_instance_flex_algo_affinity_set(struct nb_cb_create_args *args, + int type) +{ + struct affinity_map *map; + struct isis_area *area; + struct admin_group *ag; + struct flex_algo *fa; + uint32_t algorithm; + const char *val; + + algorithm = yang_dnode_get_uint32(args->dnode, "../../flex-algo"); + area = nb_running_get_entry(args->dnode, NULL, true); + val = yang_dnode_get_string(args->dnode, "."); + + switch (args->event) { + case NB_EV_VALIDATE: + fa = flex_algo_lookup(area->flex_algos, algorithm); + if (!fa) { + snprintf(args->errmsg, args->errmsg_len, + "flex-algo object not found"); + return NB_ERR_RESOURCE; + } + map = affinity_map_get(val); + if (!map) { + snprintf(args->errmsg, args->errmsg_len, + "affinity map %s isn't found", val); + return NB_ERR_VALIDATION; + } + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + fa = flex_algo_lookup(area->flex_algos, algorithm); + if (!fa) { + snprintf(args->errmsg, args->errmsg_len, + "flex-algo object not found"); + return NB_ERR_RESOURCE; + } + map = affinity_map_get(val); + if (!map) { + snprintf(args->errmsg, args->errmsg_len, + "affinity map %s isn't found", val); + return NB_ERR_RESOURCE; + } + if (type == AFFINITY_INCLUDE_ANY) + ag = &fa->admin_group_include_any; + else if (type == AFFINITY_INCLUDE_ALL) + ag = &fa->admin_group_include_all; + else if (type == AFFINITY_EXCLUDE_ANY) + ag = &fa->admin_group_exclude_any; + else + break; + + admin_group_set(ag, map->bit_position); + lsp_regenerate_schedule(area, area->is_type, 0); + break; + } + + return NB_OK; +} + +static int +isis_instance_flex_algo_affinity_unset(struct nb_cb_destroy_args *args, + int type) +{ + struct affinity_map *map; + struct isis_area *area; + struct admin_group *ag; + struct flex_algo *fa; + uint32_t algorithm; + const char *val; + + algorithm = yang_dnode_get_uint32(args->dnode, "../../flex-algo"); + area = nb_running_get_entry(args->dnode, NULL, true); + val = yang_dnode_get_string(args->dnode, "."); + + switch (args->event) { + case NB_EV_VALIDATE: + fa = flex_algo_lookup(area->flex_algos, algorithm); + if (!fa) { + snprintf(args->errmsg, args->errmsg_len, + "flex-algo object not found"); + return NB_ERR_RESOURCE; + } + map = affinity_map_get(val); + if (!map) { + snprintf(args->errmsg, args->errmsg_len, + "affinity map %s isn't found", val); + return NB_ERR_VALIDATION; + } + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + fa = flex_algo_lookup(area->flex_algos, algorithm); + if (!fa) { + snprintf(args->errmsg, args->errmsg_len, + "flex-algo object not found"); + return NB_ERR_RESOURCE; + } + map = affinity_map_get(val); + if (!map) { + snprintf(args->errmsg, args->errmsg_len, + "affinity map %s isn't found", val); + return NB_ERR_RESOURCE; + } + if (type == AFFINITY_INCLUDE_ANY) + ag = &fa->admin_group_include_any; + else if (type == AFFINITY_INCLUDE_ALL) + ag = &fa->admin_group_include_all; + else if (type == AFFINITY_EXCLUDE_ANY) + ag = &fa->admin_group_exclude_any; + else + break; + + admin_group_unset(ag, map->bit_position); + if (area->admin_group_send_zero) + admin_group_allow_explicit_zero(ag); + lsp_regenerate_schedule(area, area->is_type, 0); + break; + } + + return NB_OK; +} + +/* + * XPath: + * /frr-isisd:isis/instance/flex-algos/flex-algo/affinity-include-anies/affinity-include-any + */ +int isis_instance_flex_algo_affinity_include_any_create( + struct nb_cb_create_args *args) +{ + return isis_instance_flex_algo_affinity_set(args, AFFINITY_INCLUDE_ANY); +} + +int isis_instance_flex_algo_affinity_include_any_destroy( + struct nb_cb_destroy_args *args) +{ + return isis_instance_flex_algo_affinity_unset(args, + AFFINITY_INCLUDE_ANY); +} + +/* + * XPath: + * /frr-isisd:isis/instance/flex-algos/flex-algo/affinity-include-alls/affinity-include-all + */ +int isis_instance_flex_algo_affinity_include_all_create( + struct nb_cb_create_args *args) +{ + return isis_instance_flex_algo_affinity_set(args, AFFINITY_INCLUDE_ALL); +} + +int isis_instance_flex_algo_affinity_include_all_destroy( + struct nb_cb_destroy_args *args) +{ + return isis_instance_flex_algo_affinity_unset(args, + AFFINITY_INCLUDE_ALL); +} + +/* + * XPath: + * /frr-isisd:isis/instance/flex-algos/flex-algo/affinity-exclude-anies/affinity-exclude-any + */ +int isis_instance_flex_algo_affinity_exclude_any_create( + struct nb_cb_create_args *args) +{ + return isis_instance_flex_algo_affinity_set(args, AFFINITY_EXCLUDE_ANY); +} + +int isis_instance_flex_algo_affinity_exclude_any_destroy( + struct nb_cb_destroy_args *args) +{ + return isis_instance_flex_algo_affinity_unset(args, + AFFINITY_EXCLUDE_ANY); +} + +/* + * XPath: /frr-isisd:isis/instance/flex-algos/flex-algo/prefix-metric + */ + +int isis_instance_flex_algo_prefix_metric_create(struct nb_cb_create_args *args) +{ + struct isis_area *area; + const char *area_tag; + struct flex_algo *fa; + uint32_t algorithm; + + area_tag = yang_dnode_get_string(args->dnode, "../../../area-tag"); + area = isis_area_lookup(area_tag, VRF_DEFAULT); + if (!area) + return NB_ERR_RESOURCE; + + algorithm = yang_dnode_get_uint32(args->dnode, "./../flex-algo"); + + switch (args->event) { + case NB_EV_APPLY: + fa = flex_algo_lookup(area->flex_algos, algorithm); + if (!fa) { + snprintf(args->errmsg, args->errmsg_len, + "flex-algo object not found"); + return NB_ERR_RESOURCE; + } + SET_FLAG(fa->flags, FAD_FLAG_M); + lsp_regenerate_schedule(area, area->is_type, 0); + break; + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + } + + return NB_OK; +} + +int isis_instance_flex_algo_prefix_metric_destroy( + struct nb_cb_destroy_args *args) +{ + struct isis_area *area; + const char *area_tag; + struct flex_algo *fa; + uint32_t algorithm; + + area_tag = yang_dnode_get_string(args->dnode, "../../../area-tag"); + area = isis_area_lookup(area_tag, VRF_DEFAULT); + if (!area) + return NB_ERR_RESOURCE; + + algorithm = yang_dnode_get_uint32(args->dnode, "./../flex-algo"); + + switch (args->event) { + case NB_EV_APPLY: + fa = flex_algo_lookup(area->flex_algos, algorithm); + if (!fa) { + snprintf(args->errmsg, args->errmsg_len, + "flex-algo object not found"); + return NB_ERR_RESOURCE; + } + UNSET_FLAG(fa->flags, FAD_FLAG_M); + lsp_regenerate_schedule(area, area->is_type, 0); + break; + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + } + + return NB_OK; +} + +static int isis_instance_flex_algo_dplane_set(struct nb_cb_create_args *args, + int type) +{ + struct isis_area *area; + const char *area_tag; + struct flex_algo *fa; + uint32_t algorithm; + + area_tag = yang_dnode_get_string(args->dnode, "../../../area-tag"); + area = isis_area_lookup(area_tag, VRF_DEFAULT); + if (!area) + return NB_ERR_RESOURCE; + + algorithm = yang_dnode_get_uint32(args->dnode, "./../flex-algo"); + + switch (args->event) { + case NB_EV_APPLY: + fa = flex_algo_lookup(area->flex_algos, algorithm); + if (!fa) { + snprintf(args->errmsg, args->errmsg_len, + "flex-algo object not found"); + return NB_ERR_RESOURCE; + } + SET_FLAG(fa->dataplanes, type); + lsp_regenerate_schedule(area, area->is_type, 0); + break; + case NB_EV_VALIDATE: + if (type == FLEX_ALGO_SRV6 || type == FLEX_ALGO_IP) { + snprintf(args->errmsg, args->errmsg_len, + "%s Flex-algo dataplane is not yet supported.", + type == FLEX_ALGO_SRV6 ? "SRv6" : "IP"); + return NB_ERR_VALIDATION; + } + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + } + + return NB_OK; +} + +static int isis_instance_flex_algo_dplane_unset(struct nb_cb_destroy_args *args, + int type) +{ + struct isis_area *area; + const char *area_tag; + struct flex_algo *fa; + uint32_t algorithm; + + area_tag = yang_dnode_get_string(args->dnode, "../../../area-tag"); + area = isis_area_lookup(area_tag, VRF_DEFAULT); + if (!area) + return NB_ERR_RESOURCE; + + algorithm = yang_dnode_get_uint32(args->dnode, "./../flex-algo"); + + switch (args->event) { + case NB_EV_APPLY: + fa = flex_algo_lookup(area->flex_algos, algorithm); + if (!fa) { + snprintf(args->errmsg, args->errmsg_len, + "flex-algo object not found"); + return NB_ERR_RESOURCE; + } + UNSET_FLAG(fa->dataplanes, type); + lsp_regenerate_schedule(area, area->is_type, 0); + break; + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/flex-algos/flex-algo/dplane-sr-mpls + */ + +int isis_instance_flex_algo_dplane_sr_mpls_create( + struct nb_cb_create_args *args) +{ + return isis_instance_flex_algo_dplane_set(args, FLEX_ALGO_SR_MPLS); +} + +int isis_instance_flex_algo_dplane_sr_mpls_destroy( + struct nb_cb_destroy_args *args) +{ + return isis_instance_flex_algo_dplane_unset(args, FLEX_ALGO_SR_MPLS); +} + +/* + * XPath: /frr-isisd:isis/instance/flex-algos/flex-algo/dplane-srv6 + */ + +int isis_instance_flex_algo_dplane_srv6_create(struct nb_cb_create_args *args) +{ + return isis_instance_flex_algo_dplane_set(args, FLEX_ALGO_SRV6); +} + +int isis_instance_flex_algo_dplane_srv6_destroy(struct nb_cb_destroy_args *args) +{ + return isis_instance_flex_algo_dplane_unset(args, FLEX_ALGO_SRV6); +} + +/* + * XPath: /frr-isisd:isis/instance/flex-algos/flex-algo/dplane-ip + */ + +int isis_instance_flex_algo_dplane_ip_create(struct nb_cb_create_args *args) +{ + return isis_instance_flex_algo_dplane_set(args, FLEX_ALGO_IP); +} + +int isis_instance_flex_algo_dplane_ip_destroy(struct nb_cb_destroy_args *args) +{ + return isis_instance_flex_algo_dplane_unset(args, FLEX_ALGO_IP); +} + +/* + * XPath: /frr-isisd:isis/instance/flex-algos/flex-algo/metric-type + */ + +int isis_instance_flex_algo_metric_type_modify(struct nb_cb_modify_args *args) +{ + struct isis_area *area; + const char *area_tag; + struct flex_algo *fa; + uint32_t algorithm; + enum flex_algo_metric_type metric_type; + + area_tag = yang_dnode_get_string(args->dnode, "../../../area-tag"); + area = isis_area_lookup(area_tag, VRF_DEFAULT); + if (!area) + return NB_ERR_RESOURCE; + + algorithm = yang_dnode_get_uint32(args->dnode, "./../flex-algo"); + metric_type = yang_dnode_get_enum(args->dnode, NULL); + + switch (args->event) { + case NB_EV_APPLY: + fa = flex_algo_lookup(area->flex_algos, algorithm); + if (!fa) { + snprintf(args->errmsg, args->errmsg_len, + "flex-algo object not found"); + return NB_ERR_RESOURCE; + } + fa->metric_type = metric_type; + lsp_regenerate_schedule(area, area->is_type, 0); + break; + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/flex-algos/flex-algo/priority + */ + +int isis_instance_flex_algo_priority_modify(struct nb_cb_modify_args *args) +{ + struct isis_area *area; + const char *area_tag; + struct flex_algo *fa; + uint32_t algorithm; + uint32_t priority; + + area_tag = yang_dnode_get_string(args->dnode, "../../../area-tag"); + area = isis_area_lookup(area_tag, VRF_DEFAULT); + if (!area) + return NB_ERR_RESOURCE; + + algorithm = yang_dnode_get_uint32(args->dnode, "./../flex-algo"); + priority = yang_dnode_get_uint32(args->dnode, NULL); + + switch (args->event) { + case NB_EV_APPLY: + fa = flex_algo_lookup(area->flex_algos, algorithm); + if (!fa) { + snprintf(args->errmsg, args->errmsg_len, + "flex-algo object not found"); + return NB_ERR_RESOURCE; + } + fa->priority = priority; + lsp_regenerate_schedule(area, area->is_type, 0); + break; + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + } + + return NB_OK; +} + +int isis_instance_flex_algo_priority_destroy(struct nb_cb_destroy_args *args) +{ + struct isis_area *area; + const char *area_tag; + struct flex_algo *fa; + uint32_t algorithm; + uint32_t priority = FLEX_ALGO_PRIO_DEFAULT; + + area_tag = yang_dnode_get_string(args->dnode, "../../../area-tag"); + area = isis_area_lookup(area_tag, VRF_DEFAULT); + if (!area) + return NB_ERR_RESOURCE; + + algorithm = yang_dnode_get_uint32(args->dnode, "./../flex-algo"); + priority = yang_dnode_get_uint32(args->dnode, NULL); + + switch (args->event) { + case NB_EV_APPLY: + fa = flex_algo_lookup(area->flex_algos, algorithm); + if (!fa) { + snprintf(args->errmsg, args->errmsg_len, + "flex-algo object not found"); + return NB_ERR_RESOURCE; + } + fa->priority = priority; + lsp_regenerate_schedule(area, area->is_type, 0); + break; + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + } + + return NB_OK; +} + +/* * XPath: /frr-isisd:isis/instance/mpls/ldp-sync */ int isis_instance_mpls_ldp_sync_create(struct nb_cb_create_args *args) @@ -2570,7 +3487,8 @@ int lib_interface_isis_area_tag_modify(struct nb_cb_modify_args *args) struct isis_circuit *circuit; if (args->event == NB_EV_VALIDATE) { - circuit = nb_running_get_entry_non_rec(lyd_parent(args->dnode), NULL, false); + circuit = nb_running_get_entry_non_rec(lyd_parent(args->dnode), + NULL, false); if (circuit) { snprintf(args->errmsg, args->errmsg_len, "Changing area tag is not allowed"); diff --git a/isisd/isis_nb_notifications.c b/isisd/isis_nb_notifications.c index 94b1c47d3e..5a1e312b4d 100644 --- a/isisd/isis_nb_notifications.c +++ b/isisd/isis_nb_notifications.c @@ -134,6 +134,7 @@ void isis_notif_lsp_too_large(const struct isis_circuit *circuit, const char *xpath = "/frr-isisd:lsp-too-large"; struct list *arguments = yang_data_list_new(); char xpath_arg[XPATH_MAXLEN]; + char xpath_value[ISO_SYSID_STRLEN]; struct yang_data *data; struct isis_area *area = circuit->area; @@ -143,7 +144,8 @@ void isis_notif_lsp_too_large(const struct isis_circuit *circuit, data = yang_data_new_uint32(xpath_arg, pdu_size); listnode_add(arguments, data); snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath); - data = yang_data_new_string(xpath_arg, rawlspid_print(lsp_id)); + snprintfrr(xpath_value, ISO_SYSID_STRLEN, "%pLS", lsp_id); + data = yang_data_new_string(xpath_arg, xpath_value); listnode_add(arguments, data); hook_call(isis_hook_lsp_too_large, circuit, pdu_size, lsp_id); @@ -180,11 +182,13 @@ void isis_notif_corrupted_lsp(const struct isis_area *area, const char *xpath = "/frr-isisd:corrupted-lsp-detected"; struct list *arguments = yang_data_list_new(); char xpath_arg[XPATH_MAXLEN]; + char xpath_value[ISO_SYSID_STRLEN]; struct yang_data *data; notif_prep_instance_hdr(xpath, area, "default", arguments); snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath); - data = yang_data_new_string(xpath_arg, rawlspid_print(lsp_id)); + snprintfrr(xpath_value, ISO_SYSID_STRLEN, "%pLS", lsp_id); + data = yang_data_new_string(xpath_arg, xpath_value); listnode_add(arguments, data); hook_call(isis_hook_corrupted_lsp, area); @@ -201,11 +205,13 @@ void isis_notif_lsp_exceed_max(const struct isis_area *area, const char *xpath = "/frr-isisd:attempt-to-exceed-max-sequence"; struct list *arguments = yang_data_list_new(); char xpath_arg[XPATH_MAXLEN]; + char xpath_value[ISO_SYSID_STRLEN]; struct yang_data *data; notif_prep_instance_hdr(xpath, area, "default", arguments); snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath); - data = yang_data_new_string(xpath_arg, rawlspid_print(lsp_id)); + snprintfrr(xpath_value, ISO_SYSID_STRLEN, "%pLS", lsp_id); + data = yang_data_new_string(xpath_arg, xpath_value); listnode_add(arguments, data); hook_call(isis_hook_lsp_exceed_max, area, lsp_id); @@ -299,6 +305,7 @@ void isis_notif_adj_state_change(const struct isis_adjacency *adj, const char *xpath = "/frr-isisd:adjacency-state-change"; struct list *arguments = yang_data_list_new(); char xpath_arg[XPATH_MAXLEN]; + char xpath_value[ISO_SYSID_STRLEN]; struct yang_data *data; struct isis_circuit *circuit = adj->circuit; struct isis_area *area = circuit->area; @@ -312,7 +319,8 @@ void isis_notif_adj_state_change(const struct isis_adjacency *adj, listnode_add(arguments, data); } snprintf(xpath_arg, sizeof(xpath_arg), "%s/neighbor-system-id", xpath); - data = yang_data_new_string(xpath_arg, sysid_print(adj->sysid)); + snprintfrr(xpath_value, ISO_SYSID_STRLEN, "%pSY", adj->sysid); + data = yang_data_new_string(xpath_arg, xpath_value); listnode_add(arguments, data); snprintf(xpath_arg, sizeof(xpath_arg), "%s/state", xpath); @@ -389,13 +397,15 @@ void isis_notif_lsp_received(const struct isis_circuit *circuit, const char *xpath = "/frr-isisd:lsp-received"; struct list *arguments = yang_data_list_new(); char xpath_arg[XPATH_MAXLEN]; + char xpath_value[ISO_SYSID_STRLEN]; struct yang_data *data; struct isis_area *area = circuit->area; notif_prep_instance_hdr(xpath, area, "default", arguments); notif_prepr_iface_hdr(xpath, circuit, arguments); snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath); - data = yang_data_new_string(xpath_arg, rawlspid_print(lsp_id)); + snprintfrr(xpath_value, ISO_SYSID_STRLEN, "%pLS", lsp_id); + data = yang_data_new_string(xpath_arg, xpath_value); listnode_add(arguments, data); snprintf(xpath_arg, sizeof(xpath_arg), "%s/sequence", xpath); data = yang_data_new_uint32(xpath_arg, seqno); @@ -419,11 +429,13 @@ void isis_notif_lsp_gen(const struct isis_area *area, const uint8_t *lsp_id, const char *xpath = "/frr-isisd:lsp-generation"; struct list *arguments = yang_data_list_new(); char xpath_arg[XPATH_MAXLEN]; + char xpath_value[ISO_SYSID_STRLEN]; struct yang_data *data; notif_prep_instance_hdr(xpath, area, "default", arguments); snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath); - data = yang_data_new_string(xpath_arg, rawlspid_print(lsp_id)); + snprintfrr(xpath_value, ISO_SYSID_STRLEN, "%pLS", lsp_id); + data = yang_data_new_string(xpath_arg, xpath_value); listnode_add(arguments, data); snprintf(xpath_arg, sizeof(xpath_arg), "%s/sequence", xpath); data = yang_data_new_uint32(xpath_arg, seqno); @@ -503,13 +515,15 @@ void isis_notif_lsp_error(const struct isis_circuit *circuit, const char *xpath = "/frr-isisd:lsp-error-detected"; struct list *arguments = yang_data_list_new(); char xpath_arg[XPATH_MAXLEN]; + char xpath_value[ISO_SYSID_STRLEN]; struct yang_data *data; struct isis_area *area = circuit->area; notif_prep_instance_hdr(xpath, area, "default", arguments); notif_prepr_iface_hdr(xpath, circuit, arguments); snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath); - data = yang_data_new_string(xpath_arg, rawlspid_print(lsp_id)); + snprintfrr(xpath_value, ISO_SYSID_STRLEN, "%pLS", lsp_id); + data = yang_data_new_string(xpath_arg, xpath_value); listnode_add(arguments, data); snprintf(xpath_arg, sizeof(xpath_arg), "%s/raw-pdu", xpath); data = yang_data_new_binary(xpath_arg, raw_pdu, raw_pdu_len); @@ -530,13 +544,15 @@ void isis_notif_seqno_skipped(const struct isis_circuit *circuit, const char *xpath = "/frr-isisd:sequence-number-skipped"; struct list *arguments = yang_data_list_new(); char xpath_arg[XPATH_MAXLEN]; + char xpath_value[ISO_SYSID_STRLEN]; struct yang_data *data; struct isis_area *area = circuit->area; notif_prep_instance_hdr(xpath, area, "default", arguments); notif_prepr_iface_hdr(xpath, circuit, arguments); snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath); - data = yang_data_new_string(xpath_arg, rawlspid_print(lsp_id)); + snprintfrr(xpath_value, ISO_SYSID_STRLEN, "%pLS", lsp_id); + data = yang_data_new_string(xpath_arg, xpath_value); listnode_add(arguments, data); hook_call(isis_hook_seqno_skipped, circuit, lsp_id); @@ -553,13 +569,15 @@ void isis_notif_own_lsp_purge(const struct isis_circuit *circuit, const char *xpath = "/frr-isisd:own-lsp-purge"; struct list *arguments = yang_data_list_new(); char xpath_arg[XPATH_MAXLEN]; + char xpath_value[ISO_SYSID_STRLEN]; struct yang_data *data; struct isis_area *area = circuit->area; notif_prep_instance_hdr(xpath, area, "default", arguments); notif_prepr_iface_hdr(xpath, circuit, arguments); snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath); - data = yang_data_new_string(xpath_arg, rawlspid_print(lsp_id)); + snprintfrr(xpath_value, ISO_SYSID_STRLEN, "%pLS", lsp_id); + data = yang_data_new_string(xpath_arg, xpath_value); listnode_add(arguments, data); hook_call(isis_hook_own_lsp_purge, circuit, lsp_id); diff --git a/isisd/isis_nb_state.c b/isisd/isis_nb_state.c index 13fdddf555..b7c33ed27b 100644 --- a/isisd/isis_nb_state.c +++ b/isisd/isis_nb_state.c @@ -132,8 +132,11 @@ lib_interface_state_isis_adjacencies_adjacency_neighbor_sysid_get_elem( struct nb_cb_get_elem_args *args) { const struct isis_adjacency *adj = args->list_entry; + char xpath_value[ISO_SYSID_STRLEN]; - return yang_data_new_string(args->xpath, sysid_print(adj->sysid)); + snprintfrr(xpath_value, ISO_SYSID_STRLEN, "%pSY", adj->sysid); + + return yang_data_new_string(args->xpath, xpath_value); } /* @@ -158,8 +161,11 @@ lib_interface_state_isis_adjacencies_adjacency_neighbor_snpa_get_elem( struct nb_cb_get_elem_args *args) { const struct isis_adjacency *adj = args->list_entry; + char xpath_value[ISO_SYSID_STRLEN]; + + snprintfrr(xpath_value, ISO_SYSID_STRLEN, "%pSY", adj->snpa); - return yang_data_new_string(args->xpath, snpa_print(adj->snpa)); + return yang_data_new_string(args->xpath, xpath_value); } /* diff --git a/isisd/isis_pdu.c b/isisd/isis_pdu.c index d53d43ad0e..0cd43a7abc 100644 --- a/isisd/isis_pdu.c +++ b/isisd/isis_pdu.c @@ -514,9 +514,9 @@ static int process_lan_hello(struct iih_info *iih) if (IS_DEBUG_ADJ_PACKETS) { zlog_debug( - "ISIS-Adj (%s): Rcvd L%d LAN IIH from %s on %s, cirType %s, cirID %u, length %zd", - iih->circuit->area->area_tag, iih->level, - snpa_print(iih->ssnpa), iih->circuit->interface->name, + "ISIS-Adj (%s): Rcvd L%d LAN IIH from %pSY on %s, cirType %s, cirID %u, length %zd", + iih->circuit->area->area_tag, iih->level, iih->ssnpa, + iih->circuit->interface->name, circuit_t2string(iih->circuit->is_type), iih->circuit->circuit_id, stream_get_endp(iih->circuit->rcv_stream)); @@ -862,31 +862,32 @@ static int process_lsp(uint8_t pdu_type, struct isis_circuit *circuit, #ifndef FABRICD /* send northbound notification */ + char buf[ISO_SYSID_STRLEN]; + + snprintfrr(buf, ISO_SYSID_STRLEN, "%pSY", hdr.lsp_id); isis_notif_lsp_received(circuit, hdr.lsp_id, hdr.seqno, time(NULL), - sysid_print(hdr.lsp_id)); + buf); #endif /* ifndef FABRICD */ if (pdu_len_validate(hdr.pdu_len, circuit)) { - zlog_debug("ISIS-Upd (%s): LSP %s invalid LSP length %hu", - circuit->area->area_tag, rawlspid_print(hdr.lsp_id), - hdr.pdu_len); + zlog_debug("ISIS-Upd (%s): LSP %pLS invalid LSP length %hu", + circuit->area->area_tag, hdr.lsp_id, hdr.pdu_len); return ISIS_WARNING; } if (IS_DEBUG_UPDATE_PACKETS) { - zlog_debug("ISIS-Upd (%s): Rcvd L%d LSP %s, seq 0x%08x, cksum 0x%04hx, lifetime %hus, len %hu, on %s", - circuit->area->area_tag, level, - rawlspid_print(hdr.lsp_id), hdr.seqno, hdr.checksum, - hdr.rem_lifetime, hdr.pdu_len, - circuit->interface->name); + zlog_debug( + "ISIS-Upd (%s): Rcvd L%d LSP %pLS, seq 0x%08x, cksum 0x%04hx, lifetime %hus, len %hu, on %s", + circuit->area->area_tag, level, hdr.lsp_id, hdr.seqno, + hdr.checksum, hdr.rem_lifetime, hdr.pdu_len, + circuit->interface->name); } /* lsp is_type check */ if ((hdr.lsp_bits & IS_LEVEL_1) != IS_LEVEL_1) { - zlog_debug( - "ISIS-Upd (%s): LSP %s invalid LSP is type 0x%x", - circuit->area->area_tag, rawlspid_print(hdr.lsp_id), - hdr.lsp_bits & IS_LEVEL_1_AND_2); + zlog_debug("ISIS-Upd (%s): LSP %pLS invalid LSP is type 0x%x", + circuit->area->area_tag, hdr.lsp_id, + hdr.lsp_bits & IS_LEVEL_1_AND_2); /* continue as per RFC1122 Be liberal in what you accept, and * conservative in what you send */ } @@ -896,27 +897,25 @@ static int process_lsp(uint8_t pdu_type, struct isis_circuit *circuit, if (iso_csum_verify(STREAM_DATA(circuit->rcv_stream) + 12, hdr.pdu_len - 12, hdr.checksum, 12)) { zlog_debug( - "ISIS-Upd (%s): LSP %s invalid LSP checksum 0x%04hx", - circuit->area->area_tag, rawlspid_print(hdr.lsp_id), - hdr.checksum); + "ISIS-Upd (%s): LSP %pLS invalid LSP checksum 0x%04hx", + circuit->area->area_tag, hdr.lsp_id, hdr.checksum); return ISIS_WARNING; } /* 7.3.15.1 a) 1 - external domain circuit will discard lsps */ if (circuit->ext_domain) { zlog_debug( - "ISIS-Upd (%s): LSP %s received at level %d over circuit with externalDomain = true", - circuit->area->area_tag, rawlspid_print(hdr.lsp_id), - level); + "ISIS-Upd (%s): LSP %pLS received at level %d over circuit with externalDomain = true", + circuit->area->area_tag, hdr.lsp_id, level); return ISIS_WARNING; } /* 7.3.15.1 a) 2,3 - manualL2OnlyMode not implemented */ if (!(circuit->is_type & level)) { zlog_debug( - "ISIS-Upd (%s): LSP %s received at level %d over circuit of type %s", - circuit->area->area_tag, rawlspid_print(hdr.lsp_id), - level, circuit_t2string(circuit->is_type)); + "ISIS-Upd (%s): LSP %pLS received at level %d over circuit of type %s", + circuit->area->area_tag, hdr.lsp_id, level, + circuit_t2string(circuit->is_type)); return ISIS_WARNING; } @@ -1016,11 +1015,11 @@ static int process_lsp(uint8_t pdu_type, struct isis_circuit *circuit, if (circuit->circ_type == CIRCUIT_T_BROADCAST) { if (!isis_adj_lookup_snpa(ssnpa, circuit->u.bc.adjdb[level - 1])) { - zlog_debug("(%s): DS ======= LSP %s, seq 0x%08x, cksum 0x%04hx, lifetime %hus on %s", - circuit->area->area_tag, - rawlspid_print(hdr.lsp_id), hdr.seqno, - hdr.checksum, hdr.rem_lifetime, - circuit->interface->name); + zlog_debug( + "(%s): DS ======= LSP %pLS, seq 0x%08x, cksum 0x%04hx, lifetime %hus on %s", + circuit->area->area_tag, hdr.lsp_id, hdr.seqno, + hdr.checksum, hdr.rem_lifetime, + circuit->interface->name); goto out; /* Silently discard */ } } @@ -1057,9 +1056,9 @@ dontcheckadj: if (lsp && (lsp->hdr.seqno == hdr.seqno) && (lsp->hdr.checksum != hdr.checksum) && hdr.rem_lifetime) { - zlog_warn("ISIS-Upd (%s): LSP %s seq 0x%08x with confused checksum received.", - circuit->area->area_tag, rawlspid_print(hdr.lsp_id), - hdr.seqno); + zlog_warn( + "ISIS-Upd (%s): LSP %pLS seq 0x%08x with confused checksum received.", + circuit->area->area_tag, hdr.lsp_id, hdr.seqno); hdr.rem_lifetime = 0; lsp_confusion = true; } else @@ -1153,10 +1152,9 @@ dontcheckadj: } if (IS_DEBUG_UPDATE_PACKETS) zlog_debug( - "ISIS-Upd (%s): (1) re-originating LSP %s new seq 0x%08x", + "ISIS-Upd (%s): (1) re-originating LSP %pLS new seq 0x%08x", circuit->area->area_tag, - rawlspid_print(hdr.lsp_id), - lsp->hdr.seqno); + hdr.lsp_id, lsp->hdr.seqno); } else { /* our own LSP with 0 remaining life time */ #ifndef FABRICD @@ -1194,9 +1192,8 @@ dontcheckadj: #endif /* ifndef FABRICD */ if (IS_DEBUG_UPDATE_PACKETS) { zlog_debug( - "ISIS-Upd (%s): (2) re-originating LSP %s new seq 0x%08x", - circuit->area->area_tag, - rawlspid_print(hdr.lsp_id), + "ISIS-Upd (%s): (2) re-originating LSP %pLS new seq 0x%08x", + circuit->area->area_tag, hdr.lsp_id, lsp->hdr.seqno); } lsp_flood(lsp, NULL); @@ -1361,9 +1358,9 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit, if (!is_csnp && (circuit->circ_type == CIRCUIT_T_BROADCAST) && !circuit->u.bc.is_dr[level - 1]) { zlog_debug( - "ISIS-Snp (%s): Rcvd L%d %cSNP from %s on %s, skipping: we are not the DIS", - circuit->area->area_tag, level, typechar, - snpa_print(ssnpa), circuit->interface->name); + "ISIS-Snp (%s): Rcvd L%d %cSNP from %pSY on %s, skipping: we are not the DIS", + circuit->area->area_tag, level, typechar, ssnpa, + circuit->interface->name); return ISIS_OK; } @@ -1452,16 +1449,16 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit, /* debug isis snp-packets */ if (IS_DEBUG_SNP_PACKETS) { - zlog_debug("ISIS-Snp (%s): Rcvd L%d %cSNP from %s on %s", - circuit->area->area_tag, level, typechar, - snpa_print(ssnpa), circuit->interface->name); + zlog_debug("ISIS-Snp (%s): Rcvd L%d %cSNP from %pSY on %s", + circuit->area->area_tag, level, typechar, ssnpa, + circuit->interface->name); for (struct isis_lsp_entry *entry = entry_head; entry; entry = entry->next) { zlog_debug( - "ISIS-Snp (%s): %cSNP entry %s, seq 0x%08x, cksum 0x%04hx, lifetime %hus", - circuit->area->area_tag, typechar, - rawlspid_print(entry->id), entry->seqno, - entry->checksum, entry->rem_lifetime); + "ISIS-Snp (%s): %cSNP entry %pLS, seq 0x%08x, cksum 0x%04hx, lifetime %hus", + circuit->area->area_tag, typechar, entry->id, + entry->seqno, entry->checksum, + entry->rem_lifetime); } } @@ -1654,14 +1651,14 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa) if (idrp == ISO9542_ESIS) { flog_err(EC_LIB_DEVELOPMENT, "No support for ES-IS packet IDRP=%hhx", idrp); - pdu_counter_count(circuit->area->pdu_drop_counters, pdu_type); + pdu_counter_count_drop(circuit->area, pdu_type); return ISIS_ERROR; } if (idrp != ISO10589_ISIS) { flog_err(EC_ISIS_PACKET, "Not an IS-IS packet IDRP=%hhx", idrp); - pdu_counter_count(circuit->area->pdu_drop_counters, pdu_type); + pdu_counter_count_drop(circuit->area, pdu_type); return ISIS_ERROR; } @@ -1672,7 +1669,7 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa) isis_notif_version_skew(circuit, version1, raw_pdu, sizeof(raw_pdu)); #endif /* ifndef FABRICD */ - pdu_counter_count(circuit->area->pdu_drop_counters, pdu_type); + pdu_counter_count_drop(circuit->area, pdu_type); return ISIS_WARNING; } @@ -1696,14 +1693,14 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa) isis_notif_id_len_mismatch(circuit, id_len, raw_pdu, sizeof(raw_pdu)); #endif /* ifndef FABRICD */ - pdu_counter_count(circuit->area->pdu_drop_counters, pdu_type); + pdu_counter_count_drop(circuit->area, pdu_type); return ISIS_ERROR; } uint8_t expected_length; if (pdu_size(pdu_type, &expected_length)) { zlog_warn("Unsupported ISIS PDU %hhu", pdu_type); - pdu_counter_count(circuit->area->pdu_drop_counters, pdu_type); + pdu_counter_count_drop(circuit->area, pdu_type); return ISIS_WARNING; } @@ -1711,7 +1708,7 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa) flog_err(EC_ISIS_PACKET, "Expected fixed header length = %hhu but got %hhu", expected_length, length); - pdu_counter_count(circuit->area->pdu_drop_counters, pdu_type); + pdu_counter_count_drop(circuit->area, pdu_type); return ISIS_ERROR; } @@ -1719,7 +1716,7 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa) flog_err( EC_ISIS_PACKET, "PDU is too short to contain fixed header of given PDU type."); - pdu_counter_count(circuit->area->pdu_drop_counters, pdu_type); + pdu_counter_count_drop(circuit->area, pdu_type); return ISIS_ERROR; } @@ -1730,14 +1727,14 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa) isis_notif_version_skew(circuit, version2, raw_pdu, sizeof(raw_pdu)); #endif /* ifndef FABRICD */ - pdu_counter_count(circuit->area->pdu_drop_counters, pdu_type); + pdu_counter_count_drop(circuit->area, pdu_type); return ISIS_WARNING; } if (circuit->is_passive) { zlog_warn("Received ISIS PDU on passive circuit %s", circuit->interface->name); - pdu_counter_count(circuit->area->pdu_drop_counters, pdu_type); + pdu_counter_count_drop(circuit->area, pdu_type); return ISIS_WARNING; } @@ -1756,7 +1753,7 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa) isis_notif_max_area_addr_mismatch(circuit, max_area_addrs, raw_pdu, sizeof(raw_pdu)); #endif /* ifndef FABRICD */ - pdu_counter_count(circuit->area->pdu_drop_counters, pdu_type); + pdu_counter_count_drop(circuit->area, pdu_type); return ISIS_ERROR; } @@ -1765,8 +1762,7 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa) case L2_LAN_HELLO: case P2P_HELLO: if (fabricd && pdu_type != P2P_HELLO) { - pdu_counter_count(circuit->area->pdu_drop_counters, - pdu_type); + pdu_counter_count_drop(circuit->area, pdu_type); return ISIS_ERROR; } @@ -1777,8 +1773,7 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa) case FS_LINK_STATE: if (fabricd && pdu_type != L2_LINK_STATE && pdu_type != FS_LINK_STATE) { - pdu_counter_count(circuit->area->pdu_drop_counters, - pdu_type); + pdu_counter_count_drop(circuit->area, pdu_type); return ISIS_ERROR; } @@ -1791,12 +1786,12 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa) retval = process_snp(pdu_type, circuit, ssnpa); break; default: - pdu_counter_count(circuit->area->pdu_drop_counters, pdu_type); + pdu_counter_count_drop(circuit->area, pdu_type); return ISIS_ERROR; } if (retval != ISIS_OK) - pdu_counter_count(circuit->area->pdu_drop_counters, pdu_type); + pdu_counter_count_drop(circuit->area, pdu_type); return retval; } @@ -2481,11 +2476,11 @@ void send_lsp(struct isis_circuit *circuit, struct isis_lsp *lsp, if (stream_get_endp(lsp->pdu) > stream_get_size(circuit->snd_stream)) { flog_err( EC_ISIS_PACKET, - "ISIS-Upd (%s): Can't send L%d LSP %s, seq 0x%08x, cksum 0x%04hx, lifetime %hus on %s. LSP Size is %zu while interface stream size is %zu.", - circuit->area->area_tag, lsp->level, - rawlspid_print(lsp->hdr.lsp_id), lsp->hdr.seqno, - lsp->hdr.checksum, lsp->hdr.rem_lifetime, - circuit->interface->name, stream_get_endp(lsp->pdu), + "ISIS-Upd (%s): Can't send L%d LSP %pLS, seq 0x%08x, cksum 0x%04hx, lifetime %hus on %s. LSP Size is %zu while interface stream size is %zu.", + circuit->area->area_tag, lsp->level, lsp->hdr.lsp_id, + lsp->hdr.seqno, lsp->hdr.checksum, + lsp->hdr.rem_lifetime, circuit->interface->name, + stream_get_endp(lsp->pdu), stream_get_size(circuit->snd_stream)); #ifndef FABRICD /* send a northbound notification */ @@ -2509,14 +2504,14 @@ void send_lsp(struct isis_circuit *circuit, struct isis_lsp *lsp, } if (IS_DEBUG_UPDATE_PACKETS) { - zlog_debug("ISIS-Upd (%s): Sending %sL%d LSP %s, seq 0x%08x, cksum 0x%04hx, lifetime %hus on %s", - circuit->area->area_tag, - (tx_type == TX_LSP_CIRCUIT_SCOPED) - ? "Circuit scoped " : "", - lsp->level, - rawlspid_print(lsp->hdr.lsp_id), lsp->hdr.seqno, - lsp->hdr.checksum, lsp->hdr.rem_lifetime, - circuit->interface->name); + zlog_debug( + "ISIS-Upd (%s): Sending %sL%d LSP %pLS, seq 0x%08x, cksum 0x%04hx, lifetime %hus on %s", + circuit->area->area_tag, + (tx_type == TX_LSP_CIRCUIT_SCOPED) ? "Circuit scoped " + : "", + lsp->level, lsp->hdr.lsp_id, lsp->hdr.seqno, + lsp->hdr.checksum, lsp->hdr.rem_lifetime, + circuit->interface->name); if (IS_DEBUG_PACKET_DUMP) zlog_dump_data(STREAM_DATA(circuit->snd_stream), stream_get_endp(circuit->snd_stream)); @@ -2554,3 +2549,37 @@ out: isis_tx_queue_del(circuit->tx_queue, lsp); } } + +void isis_log_pdu_drops(struct isis_area *area, const char *pdu_type) +{ + uint64_t total_drops = 0; + + for (int i = 0; i < PDU_COUNTER_SIZE; i++) { + if (!area->pdu_drop_counters[i]) + continue; + total_drops += area->pdu_drop_counters[i]; + } + + zlog_info("PDU drop detected of type: %s. %" PRIu64 + " Total Drops; %" PRIu64 " L1 IIH drops; %" PRIu64 + " L2 IIH drops; %" PRIu64 " P2P IIH drops; %" PRIu64 + " L1 LSP drops; %" PRIu64 " L2 LSP drops; %" PRIu64 + " FS LSP drops; %" PRIu64 " L1 CSNP drops; %" PRIu64 + " L2 CSNP drops; %" PRIu64 " L1 PSNP drops; %" PRIu64 + " L2 PSNP drops.", + pdu_type, total_drops, + pdu_counter_get_count(area->pdu_drop_counters, L1_LAN_HELLO), + pdu_counter_get_count(area->pdu_drop_counters, L2_LAN_HELLO), + pdu_counter_get_count(area->pdu_drop_counters, P2P_HELLO), + pdu_counter_get_count(area->pdu_drop_counters, L1_LINK_STATE), + pdu_counter_get_count(area->pdu_drop_counters, L2_LINK_STATE), + pdu_counter_get_count(area->pdu_drop_counters, FS_LINK_STATE), + pdu_counter_get_count(area->pdu_drop_counters, + L1_COMPLETE_SEQ_NUM), + pdu_counter_get_count(area->pdu_drop_counters, + L2_COMPLETE_SEQ_NUM), + pdu_counter_get_count(area->pdu_drop_counters, + L1_PARTIAL_SEQ_NUM), + pdu_counter_get_count(area->pdu_drop_counters, + L2_PARTIAL_SEQ_NUM)); +} diff --git a/isisd/isis_pdu.h b/isisd/isis_pdu.h index ccd89a70f1..5303c61d38 100644 --- a/isisd/isis_pdu.h +++ b/isisd/isis_pdu.h @@ -206,4 +206,6 @@ void send_lsp(struct isis_circuit *circuit, void fill_fixed_hdr(uint8_t pdu_type, struct stream *stream); int send_hello(struct isis_circuit *circuit, int level); int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa); +void isis_log_pdu_drops(struct isis_area *area, const char *pdu_type); + #endif /* _ZEBRA_ISIS_PDU_H */ diff --git a/isisd/isis_pdu_counter.c b/isisd/isis_pdu_counter.c index 9d07b5e598..a3605a32a1 100644 --- a/isisd/isis_pdu_counter.c +++ b/isisd/isis_pdu_counter.c @@ -8,10 +8,10 @@ #include "vty.h" -#include "isisd/isis_pdu_counter.h" #include "isisd/isisd.h" #include "isisd/isis_circuit.h" #include "isisd/isis_pdu.h" +#include "isisd/isis_pdu_counter.h" static int pdu_type_to_counter_index(uint8_t pdu_type) { @@ -91,3 +91,23 @@ void pdu_counter_print(struct vty *vty, const char *prefix, pdu_counter_index_to_name(i), counter[i]); } } + +void pdu_counter_count_drop(struct isis_area *area, uint8_t pdu_type) +{ + pdu_counter_count(area->pdu_drop_counters, pdu_type); + + if (area->log_pdu_drops) { + isis_log_pdu_drops( + area, pdu_counter_index_to_name( + pdu_type_to_counter_index(pdu_type))); + } +} + +uint64_t pdu_counter_get_count(pdu_counter_t counter, uint8_t pdu_type) +{ + int index = pdu_type_to_counter_index(pdu_type); + + if (index < 0) + return -1; + return counter[index]; +} diff --git a/isisd/isis_pdu_counter.h b/isisd/isis_pdu_counter.h index c53c47368f..5c35b4fb51 100644 --- a/isisd/isis_pdu_counter.h +++ b/isisd/isis_pdu_counter.h @@ -24,5 +24,7 @@ typedef uint64_t pdu_counter_t[PDU_COUNTER_SIZE]; void pdu_counter_print(struct vty *vty, const char *prefix, pdu_counter_t counter); void pdu_counter_count(pdu_counter_t counter, uint8_t pdu_type); +void pdu_counter_count_drop(struct isis_area *area, uint8_t pdu_type); +uint64_t pdu_counter_get_count(pdu_counter_t counter, uint8_t pdu_type); #endif diff --git a/isisd/isis_route.c b/isisd/isis_route.c index 3b653194bf..be92dcc22e 100644 --- a/isisd/isis_route.c +++ b/isisd/isis_route.c @@ -36,9 +36,12 @@ #include "isis_spf_private.h" #include "isis_route.h" #include "isis_zebra.h" +#include "isis_flex_algo.h" DEFINE_MTYPE_STATIC(ISISD, ISIS_NEXTHOP, "ISIS nexthop"); DEFINE_MTYPE_STATIC(ISISD, ISIS_ROUTE_INFO, "ISIS route info"); +DEFINE_MTYPE_STATIC(ISISD, ISIS_ROUTE_TABLE_INFO, "ISIS route table info"); + DEFINE_HOOK(isis_route_update_hook, (struct isis_area * area, struct prefix *prefix, @@ -51,8 +54,25 @@ static void isis_route_update(struct isis_area *area, struct prefix *prefix, struct prefix_ipv6 *src_p, struct isis_route_info *route_info); -static struct isis_nexthop *isis_nexthop_create(int family, union g_addr *ip, - ifindex_t ifindex) +static struct mpls_label_stack * +label_stack_dup(const struct mpls_label_stack *const orig) +{ + struct mpls_label_stack *copy; + int array_size; + + if (orig == NULL) + return NULL; + + array_size = orig->num_labels * sizeof(mpls_label_t); + copy = XCALLOC(MTYPE_ISIS_NEXTHOP_LABELS, + sizeof(struct mpls_label_stack) + array_size); + copy->num_labels = orig->num_labels; + memcpy(copy->label, orig->label, array_size); + return copy; +} + +static struct isis_nexthop * +isis_nexthop_create(int family, const union g_addr *const ip, ifindex_t ifindex) { struct isis_nexthop *nexthop; @@ -65,12 +85,40 @@ static struct isis_nexthop *isis_nexthop_create(int family, union g_addr *ip, return nexthop; } +static struct isis_nexthop * +isis_nexthop_dup(const struct isis_nexthop *const orig) +{ + struct isis_nexthop *nexthop; + + nexthop = isis_nexthop_create(orig->family, &orig->ip, orig->ifindex); + memcpy(nexthop->sysid, orig->sysid, ISIS_SYS_ID_LEN); + nexthop->sr = orig->sr; + nexthop->label_stack = label_stack_dup(orig->label_stack); + + return nexthop; +} + void isis_nexthop_delete(struct isis_nexthop *nexthop) { XFREE(MTYPE_ISIS_NEXTHOP_LABELS, nexthop->label_stack); XFREE(MTYPE_ISIS_NEXTHOP, nexthop); } +static struct list *isis_nexthop_list_dup(const struct list *orig) +{ + struct list *copy; + struct listnode *node; + struct isis_nexthop *nh; + struct isis_nexthop *nhcopy; + + copy = list_new(); + for (ALL_LIST_ELEMENTS_RO(orig, node, nh)) { + nhcopy = isis_nexthop_dup(nh); + listnode_add(copy, nhcopy); + } + return copy; +} + static struct isis_nexthop *nexthoplookup(struct list *nexthops, int family, union g_addr *ip, ifindex_t ifindex) { @@ -238,13 +286,28 @@ isis_route_info_new(struct prefix *prefix, struct prefix_ipv6 *src_p, rinfo->cost = cost; rinfo->depth = depth; - rinfo->sr = *sr; + rinfo->sr_algo[sr->algorithm] = *sr; + rinfo->sr_algo[sr->algorithm].nexthops = rinfo->nexthops; + rinfo->sr_algo[sr->algorithm].nexthops_backup = + rinfo->backup ? rinfo->backup->nexthops : NULL; return rinfo; } static void isis_route_info_delete(struct isis_route_info *route_info) { + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) { + if (!route_info->sr_algo[i].present) + continue; + + if (route_info->sr_algo[i].nexthops == route_info->nexthops) + continue; + + route_info->sr_algo[i].nexthops->del = + (void (*)(void *))isis_nexthop_delete; + list_delete(&route_info->sr_algo[i].nexthops); + } + if (route_info->nexthops) { route_info->nexthops->del = (void (*)(void *))isis_nexthop_delete; @@ -260,6 +323,27 @@ void isis_route_node_cleanup(struct route_table *table, struct route_node *node) isis_route_info_delete(node->info); } +struct isis_route_table_info *isis_route_table_info_alloc(uint8_t algorithm) +{ + struct isis_route_table_info *info; + + info = XCALLOC(MTYPE_ISIS_ROUTE_TABLE_INFO, sizeof(*info)); + info->algorithm = algorithm; + return info; +} + +void isis_route_table_info_free(void *info) +{ + XFREE(MTYPE_ISIS_ROUTE_TABLE_INFO, info); +} + +uint8_t isis_route_table_algorithm(const struct route_table *table) +{ + const struct isis_route_table_info *info = table->info; + + return info ? info->algorithm : 0; +} + static bool isis_sr_psid_info_same(struct isis_sr_psid_info *new, struct isis_sr_psid_info *old) { @@ -273,6 +357,9 @@ static bool isis_sr_psid_info_same(struct isis_sr_psid_info *new, || new->sid.value != old->sid.value) return false; + if (new->sid.algorithm != old->sid.algorithm) + return false; + return true; } @@ -313,10 +400,22 @@ static int isis_route_info_same(struct isis_route_info *new, return 0; } - if (!isis_sr_psid_info_same(&new->sr, &old->sr)) { - if (buf) - snprintf(buf, buf_size, "SR input label"); - return 0; + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) { + struct isis_sr_psid_info new_sr_algo; + struct isis_sr_psid_info old_sr_algo; + + new_sr_algo = new->sr_algo[i]; + old_sr_algo = old->sr_algo[i]; + + if (!isis_sr_psid_info_same(&new_sr_algo, &old_sr_algo)) { + if (buf) + snprintf( + buf, buf_size, + "SR input label algo-%u (old: %s, new: %s)", + i, old_sr_algo.present ? "yes" : "no", + new_sr_algo.present ? "yes" : "no"); + return 0; + } } if (new->nexthops->count != old->nexthops->count) { @@ -405,7 +504,9 @@ isis_route_create(struct prefix *prefix, struct prefix_ipv6 *src_p, zlog_debug( "ISIS-Rte (%s): route changed: %pFX, change: %s", area->area_tag, prefix, change_buf); - rinfo_new->sr_previous = rinfo_old->sr; + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) + rinfo_new->sr_algo_previous[i] = + rinfo_old->sr_algo[i]; isis_route_info_delete(rinfo_old); route_info = rinfo_new; UNSET_FLAG(route_info->flag, @@ -461,11 +562,42 @@ static void isis_route_remove_previous_sid(struct isis_area *area, * Explicitly uninstall previous Prefix-SID label if it has * changed or was removed. */ - if (route_info->sr_previous.present && - (!route_info->sr.present || - route_info->sr_previous.label != route_info->sr.label)) - isis_zebra_prefix_sid_uninstall(area, prefix, route_info, - &route_info->sr_previous); + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) { + if (route_info->sr_algo_previous[i].present && + (!route_info->sr_algo[i].present || + route_info->sr_algo_previous[i].label != + route_info->sr_algo[i].label)) + isis_zebra_prefix_sid_uninstall( + area, prefix, route_info, + &route_info->sr_algo_previous[i]); + } +} + +static void set_merge_route_info_sr_algo(struct isis_route_info *mrinfo, + struct isis_route_info *rinfo) +{ + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) { + if (rinfo->sr_algo[i].present) { + assert(i == rinfo->sr_algo[i].algorithm); + assert(rinfo->nexthops); + assert(rinfo->backup ? rinfo->backup->nexthops != NULL + : true); + + if (mrinfo->sr_algo[i].nexthops != NULL && + mrinfo->sr_algo[i].nexthops != mrinfo->nexthops) { + mrinfo->sr_algo[i].nexthops->del = + (void (*)(void *))isis_nexthop_delete; + list_delete(&mrinfo->sr_algo[i].nexthops); + } + + mrinfo->sr_algo[i] = rinfo->sr_algo[i]; + mrinfo->sr_algo[i].nexthops = isis_nexthop_list_dup( + rinfo->sr_algo[i].nexthops); + } + } + + UNSET_FLAG(rinfo->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED); + UNSET_FLAG(mrinfo->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED); } static void isis_route_update(struct isis_area *area, struct prefix *prefix, @@ -484,19 +616,35 @@ static void isis_route_update(struct isis_area *area, struct prefix *prefix, /* Install route. */ isis_zebra_route_add_route(area->isis, prefix, src_p, route_info); - /* Install/reinstall Prefix-SID label. */ - if (route_info->sr.present) - isis_zebra_prefix_sid_install(area, prefix, route_info, - &route_info->sr); + + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) { + struct isis_sr_psid_info sr_algo; + + sr_algo = route_info->sr_algo[i]; + + /* + * Install/reinstall Prefix-SID label. + */ + if (sr_algo.present) + isis_zebra_prefix_sid_install(area, prefix, + &sr_algo); + + hook_call(isis_route_update_hook, area, prefix, + route_info); + } + hook_call(isis_route_update_hook, area, prefix, route_info); SET_FLAG(route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED); UNSET_FLAG(route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_RESYNC); } else { /* Uninstall Prefix-SID label. */ - if (route_info->sr.present) - isis_zebra_prefix_sid_uninstall( - area, prefix, route_info, &route_info->sr); + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) + if (route_info->sr_algo[i].present) + isis_zebra_prefix_sid_uninstall( + area, prefix, route_info, + &route_info->sr_algo[i]); + /* Uninstall route. */ isis_zebra_route_del_route(area->isis, prefix, src_p, route_info); @@ -516,6 +664,7 @@ static void _isis_route_verify_table(struct isis_area *area, #ifdef EXTREME_DEBUG char buff[SRCDEST2STR_BUFFER]; #endif /* EXTREME_DEBUG */ + uint8_t algorithm = isis_route_table_algorithm(table); for (rnode = route_top(table); rnode; rnode = srcdest_route_next(rnode)) { @@ -538,10 +687,14 @@ static void _isis_route_verify_table(struct isis_area *area, src_p); if (rnode_bck) { rinfo->backup = rnode_bck->info; + rinfo->sr_algo[algorithm].nexthops_backup = + rinfo->backup->nexthops; UNSET_FLAG(rinfo->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED); } else if (rinfo->backup) { rinfo->backup = NULL; + rinfo->sr_algo[algorithm].nexthops_backup = + NULL; UNSET_FLAG(rinfo->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED); } @@ -573,7 +726,7 @@ static void _isis_route_verify_table(struct isis_area *area, if (CHECK_FLAG(rinfo->flag, ISIS_ROUTE_FLAG_ACTIVE)) continue; - /* Area is either L1 or L2 => we use level route tables + /* In case the verify is not for a merge, we use a single table * directly for * validating => no problems with deleting routes. */ if (!tables) { @@ -581,13 +734,12 @@ static void _isis_route_verify_table(struct isis_area *area, continue; } - /* If area is L1L2, we work with merge table and - * therefore must - * delete node from level tables as well before deleting + /* If we work on a merged table, + * therefore we must + * delete node from each table as well before deleting * route info. */ - for (int level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) { - drnode = srcdest_rnode_lookup(tables[level - 1], - dst_p, src_p); + for (int i = 0; tables[i]; i++) { + drnode = srcdest_rnode_lookup(tables[i], dst_p, src_p); if (!drnode) continue; @@ -604,10 +756,36 @@ static void _isis_route_verify_table(struct isis_area *area, } } +static void _isis_route_verify_merge(struct isis_area *area, + struct route_table **tables, + struct route_table **tables_backup, + int tree); + void isis_route_verify_table(struct isis_area *area, struct route_table *table, - struct route_table *table_backup) + struct route_table *table_backup, int tree) { - _isis_route_verify_table(area, table, table_backup, NULL); + struct route_table *tables[SR_ALGORITHM_COUNT] = {table}; + struct route_table *tables_backup[SR_ALGORITHM_COUNT] = {table_backup}; +#ifndef FABRICD + int tables_next = 1; + int level = area->is_type == IS_LEVEL_1 ? ISIS_LEVEL1 : ISIS_LEVEL2; + struct listnode *node; + struct flex_algo *fa; + struct isis_flex_algo_data *data; + + for (ALL_LIST_ELEMENTS_RO(area->flex_algos->flex_algos, node, fa)) { + data = fa->data; + tables[tables_next] = + data->spftree[tree][level - 1]->route_table; + tables_backup[tables_next] = + data->spftree[tree][level - 1]->route_table_backup; + _isis_route_verify_table(area, tables[tables_next], + tables_backup[tables_next], NULL); + tables_next++; + } +#endif /* ifndef FABRICD */ + + _isis_route_verify_merge(area, tables, tables_backup, tree); } /* Function to validate route tables for L1L2 areas. In this case we can't use @@ -624,18 +802,27 @@ void isis_route_verify_merge(struct isis_area *area, struct route_table *level1_table, struct route_table *level1_table_backup, struct route_table *level2_table, - struct route_table *level2_table_backup) + struct route_table *level2_table_backup, int tree) { - struct route_table *tables[] = {level1_table, level2_table}; + struct route_table *tables[] = {level1_table, level2_table, NULL}; struct route_table *tables_backup[] = {level1_table_backup, - level2_table_backup}; + level2_table_backup, NULL}; + _isis_route_verify_merge(area, tables, tables_backup, tree); +} + +static void _isis_route_verify_merge(struct isis_area *area, + struct route_table **tables, + struct route_table **tables_backup, + int tree) +{ struct route_table *merge; struct route_node *rnode, *mrnode; merge = srcdest_table_init(); - for (int level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) { - for (rnode = route_top(tables[level - 1]); rnode; + for (int i = 0; tables[i]; i++) { + uint8_t algorithm = isis_route_table_algorithm(tables[i]); + for (rnode = route_top(tables[i]); rnode; rnode = srcdest_route_next(rnode)) { struct isis_route_info *rinfo = rnode->info; struct route_node *rnode_bck; @@ -651,14 +838,18 @@ void isis_route_verify_merge(struct isis_area *area, (const struct prefix **)&src_p); /* Link primary route to backup route. */ - rnode_bck = srcdest_rnode_lookup( - tables_backup[level - 1], prefix, src_p); + rnode_bck = srcdest_rnode_lookup(tables_backup[i], + prefix, src_p); if (rnode_bck) { rinfo->backup = rnode_bck->info; + rinfo->sr_algo[algorithm].nexthops_backup = + rinfo->backup->nexthops; UNSET_FLAG(rinfo->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED); } else if (rinfo->backup) { rinfo->backup = NULL; + rinfo->sr_algo[algorithm].nexthops_backup = + NULL; UNSET_FLAG(rinfo->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED); } @@ -667,6 +858,8 @@ void isis_route_verify_merge(struct isis_area *area, struct isis_route_info *mrinfo = mrnode->info; if (mrinfo) { route_unlock_node(mrnode); + set_merge_route_info_sr_algo(mrinfo, rinfo); + if (CHECK_FLAG(mrinfo->flag, ISIS_ROUTE_FLAG_ACTIVE)) { /* Clear the ZEBRA_SYNCED flag on the @@ -696,8 +889,9 @@ void isis_route_verify_merge(struct isis_area *area, ISIS_ROUTE_FLAG_ZEBRA_SYNCED)) { continue; } + } else { + mrnode->info = rnode->info; } - mrnode->info = rnode->info; } } @@ -710,6 +904,7 @@ void isis_route_invalidate_table(struct isis_area *area, { struct route_node *rode; struct isis_route_info *rinfo; + uint8_t algorithm = isis_route_table_algorithm(table); for (rode = route_top(table); rode; rode = srcdest_route_next(rode)) { if (rode->info == NULL) continue; @@ -717,6 +912,7 @@ void isis_route_invalidate_table(struct isis_area *area, if (rinfo->backup) { rinfo->backup = NULL; + rinfo->sr_algo[algorithm].nexthops_backup = NULL; /* * For now, always force routes that have backup * nexthops to be reinstalled. diff --git a/isisd/isis_route.h b/isisd/isis_route.h index 40e7462898..4d49a5ae9c 100644 --- a/isisd/isis_route.h +++ b/isisd/isis_route.h @@ -30,12 +30,16 @@ struct isis_route_info { uint8_t flag; uint32_t cost; uint32_t depth; - struct isis_sr_psid_info sr; - struct isis_sr_psid_info sr_previous; + struct isis_sr_psid_info sr_algo[SR_ALGORITHM_COUNT]; + struct isis_sr_psid_info sr_algo_previous[SR_ALGORITHM_COUNT]; struct list *nexthops; struct isis_route_info *backup; }; +struct isis_route_table_info { + uint8_t algorithm; +}; + DECLARE_HOOK(isis_route_update_hook, (struct isis_area * area, struct prefix *prefix, struct isis_route_info *route_info), @@ -56,14 +60,14 @@ void isis_route_delete(struct isis_area *area, struct route_node *rode, /* Walk the given table and install new routes to zebra and remove old ones. * route status is tracked using ISIS_ROUTE_FLAG_ACTIVE */ void isis_route_verify_table(struct isis_area *area, struct route_table *table, - struct route_table *table_backup); + struct route_table *table_backup, int tree); /* Same as isis_route_verify_table, but merge L1 and L2 routes before */ void isis_route_verify_merge(struct isis_area *area, struct route_table *level1_table, struct route_table *level1_table_backup, struct route_table *level2_table, - struct route_table *level2_table_backup); + struct route_table *level2_table_backup, int tree); /* Unset ISIS_ROUTE_FLAG_ACTIVE on all routes. Used before running spf. */ void isis_route_invalidate_table(struct isis_area *area, @@ -73,9 +77,14 @@ void isis_route_invalidate_table(struct isis_area *area, void isis_route_node_cleanup(struct route_table *table, struct route_node *node); + void isis_route_switchover_nexthop(struct isis_area *area, struct route_table *table, int family, union g_addr *nexthop_addr, ifindex_t ifindex); +struct isis_route_table_info *isis_route_table_info_alloc(uint8_t algorithm); +void isis_route_table_info_free(void *info); +uint8_t isis_route_table_algorithm(const struct route_table *table); + #endif /* _ZEBRA_ISIS_ROUTE_H */ diff --git a/isisd/isis_snmp.c b/isisd/isis_snmp.c index 6d9974fe9f..f9e3780e29 100644 --- a/isisd/isis_snmp.c +++ b/isisd/isis_snmp.c @@ -833,12 +833,12 @@ static int isis_snmp_conv_next(uint8_t *buf, size_t max_len, size_t *out_len, */ static int isis_snmp_area_addr_lookup_exact(oid *oid_idx, size_t oid_idx_len, struct isis_area **ret_area, - struct area_addr **ret_addr) + struct iso_address **ret_addr) { uint8_t cmp_buf[ISIS_SNMP_OSI_ADDR_LEN_MAX]; size_t addr_len; struct isis_area *area = NULL; - struct area_addr *addr = NULL; + struct iso_address *addr = NULL; struct listnode *addr_node; struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); @@ -880,15 +880,15 @@ static int isis_snmp_area_addr_lookup_exact(oid *oid_idx, size_t oid_idx_len, static int isis_snmp_area_addr_lookup_next(oid *oid_idx, size_t oid_idx_len, struct isis_area **ret_area, - struct area_addr **ret_addr) + struct iso_address **ret_addr) { uint8_t cmp_buf[ISIS_SNMP_OSI_ADDR_LEN_MAX]; size_t addr_len; int try_exact = 0; struct isis_area *found_area = NULL; struct isis_area *area = NULL; - struct area_addr *found_addr = NULL; - struct area_addr *addr = NULL; + struct iso_address *found_addr = NULL; + struct iso_address *addr = NULL; struct listnode *addr_node; struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); @@ -1501,7 +1501,7 @@ static uint8_t *isis_snmp_find_man_area(struct variable *v, oid *name, WriteMethod **write_method) { int res; - struct area_addr *area_addr = NULL; + struct iso_address *area_addr = NULL; oid *oid_idx; size_t oid_idx_len; size_t off = 0; @@ -2485,6 +2485,11 @@ static uint8_t *isis_snmp_find_isadj(struct variable *v, oid *name, uint32_t delta_ticks; time_t now_time; + /* Ring buffer to print SNPA */ +#define FORMAT_BUF_COUNT 4 + static char snpa[FORMAT_BUF_COUNT][ISO_SYSID_STRLEN]; + static size_t cur_buf = 0; + *write_method = NULL; if (*length <= v->namelen) { @@ -2531,9 +2536,10 @@ static uint8_t *isis_snmp_find_isadj(struct variable *v, oid *name, return SNMP_INTEGER(adj->threeway_state); case ISIS_ISADJ_NEIGHSNPAADDRESS: { - const char *snpa = (char *)snpa_print(adj->snpa); - *var_len = strlen(snpa); - return (uint8_t *)snpa; + cur_buf = (cur_buf + 1) % FORMAT_BUF_COUNT; + snprintfrr(snpa[cur_buf], ISO_SYSID_STRLEN, "%pSY", adj->snpa); + *var_len = strlen(snpa[cur_buf]); + return (uint8_t *)snpa[cur_buf]; } case ISIS_ISADJ_NEIGHSYSTYPE: diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c index bfe3758cd8..732853796a 100644 --- a/isisd/isis_spf.c +++ b/isisd/isis_spf.c @@ -26,6 +26,7 @@ #include "spf_backoff.h" #include "srcdest_table.h" #include "vrf.h" +#include "lib/json.h" #include "isis_errors.h" #include "isis_constants.h" @@ -43,6 +44,7 @@ #include "isis_csm.h" #include "isis_mt.h" #include "isis_tlvs.h" +#include "isis_flex_algo.h" #include "isis_zebra.h" #include "fabricd.h" #include "isis_spf_private.h" @@ -322,28 +324,41 @@ static void isis_spf_adj_free(void *arg) XFREE(MTYPE_ISIS_SPF_ADJ, sadj); } -struct isis_spftree *isis_spftree_new(struct isis_area *area, - struct lspdb_head *lspdb, - const uint8_t *sysid, int level, - enum spf_tree_id tree_id, - enum spf_type type, uint8_t flags) +static void _isis_spftree_init(struct isis_spftree *tree) { - struct isis_spftree *tree; - - tree = XCALLOC(MTYPE_ISIS_SPFTREE, sizeof(struct isis_spftree)); - isis_vertex_queue_init(&tree->tents, "IS-IS SPF tents", true); isis_vertex_queue_init(&tree->paths, "IS-IS SPF paths", false); tree->route_table = srcdest_table_init(); tree->route_table->cleanup = isis_route_node_cleanup; + tree->route_table->info = isis_route_table_info_alloc(tree->algorithm); tree->route_table_backup = srcdest_table_init(); + tree->route_table_backup->info = + isis_route_table_info_alloc(tree->algorithm); tree->route_table_backup->cleanup = isis_route_node_cleanup; - tree->area = area; - tree->lspdb = lspdb; tree->prefix_sids = hash_create(prefix_sid_key_make, prefix_sid_cmp, "SR Prefix-SID Entries"); tree->sadj_list = list_new(); tree->sadj_list->del = isis_spf_adj_free; + isis_rlfa_list_init(tree); + tree->lfa.remote.pc_spftrees = list_new(); + tree->lfa.remote.pc_spftrees->del = (void (*)(void *))isis_spftree_del; + if (tree->type == SPF_TYPE_RLFA || tree->type == SPF_TYPE_TI_LFA) { + isis_spf_node_list_init(&tree->lfa.p_space); + isis_spf_node_list_init(&tree->lfa.q_space); + } +} + +struct isis_spftree * +isis_spftree_new(struct isis_area *area, struct lspdb_head *lspdb, + const uint8_t *sysid, int level, enum spf_tree_id tree_id, + enum spf_type type, uint8_t flags, uint8_t algorithm) +{ + struct isis_spftree *tree; + + tree = XCALLOC(MTYPE_ISIS_SPFTREE, sizeof(struct isis_spftree)); + + tree->area = area; + tree->lspdb = lspdb; tree->last_run_timestamp = 0; tree->last_run_monotime = 0; tree->last_run_duration = 0; @@ -354,18 +369,14 @@ struct isis_spftree *isis_spftree_new(struct isis_area *area, tree->tree_id = tree_id; tree->family = (tree->tree_id == SPFTREE_IPV4) ? AF_INET : AF_INET6; tree->flags = flags; - isis_rlfa_list_init(tree); - tree->lfa.remote.pc_spftrees = list_new(); - tree->lfa.remote.pc_spftrees->del = (void (*)(void *))isis_spftree_del; - if (tree->type == SPF_TYPE_RLFA || tree->type == SPF_TYPE_TI_LFA) { - isis_spf_node_list_init(&tree->lfa.p_space); - isis_spf_node_list_init(&tree->lfa.q_space); - } + tree->algorithm = algorithm; + + _isis_spftree_init(tree); return tree; } -void isis_spftree_del(struct isis_spftree *spftree) +static void _isis_spftree_del(struct isis_spftree *spftree) { hash_clean_and_free(&spftree->prefix_sids, NULL); isis_zebra_rlfa_unregister_all(spftree); @@ -380,6 +391,14 @@ void isis_spftree_del(struct isis_spftree *spftree) list_delete(&spftree->sadj_list); isis_vertex_queue_free(&spftree->tents); isis_vertex_queue_free(&spftree->paths); + isis_route_table_info_free(spftree->route_table->info); + isis_route_table_info_free(spftree->route_table_backup->info); +} + +void isis_spftree_del(struct isis_spftree *spftree) +{ + _isis_spftree_del(spftree); + route_table_finish(spftree->route_table); route_table_finish(spftree->route_table_backup); spftree->route_table = NULL; @@ -388,6 +407,14 @@ void isis_spftree_del(struct isis_spftree *spftree) return; } +#ifndef FABRICD +static void isis_spftree_clear(struct isis_spftree *spftree) +{ + _isis_spftree_del(spftree); + _isis_spftree_init(spftree); +} +#endif /* ifndef FABRICD */ + static void isis_spftree_adj_del(struct isis_spftree *spftree, struct isis_adjacency *adj) { @@ -410,10 +437,10 @@ void spftree_area_init(struct isis_area *area) if (area->spftree[tree][level - 1]) continue; - area->spftree[tree][level - 1] = - isis_spftree_new(area, &area->lspdb[level - 1], - area->isis->sysid, level, tree, - SPF_TYPE_FORWARD, 0); + area->spftree[tree][level - 1] = isis_spftree_new( + area, &area->lspdb[level - 1], + area->isis->sysid, level, tree, + SPF_TYPE_FORWARD, 0, SR_ALGORITHM_SPF); } } } @@ -495,8 +522,8 @@ static struct isis_vertex *isis_spf_add_root(struct isis_spftree *spftree) #ifdef EXTREME_DEBUG if (IS_DEBUG_SPF_EVENTS) zlog_debug( - "ISIS-SPF: added this IS %s %s depth %d dist %d to PATHS", - vtype2string(vertex->type), + "ISIS-SPF: A:%hhu added this IS %s %s depth %d dist %d to PATHS", + spftree->algorithm, vtype2string(vertex->type), vid2string(vertex, buff, sizeof(buff)), vertex->depth, vertex->d_N); #endif /* EXTREME_DEBUG */ @@ -585,9 +612,20 @@ isis_spf_add2tent(struct isis_spftree *spftree, enum vertextype vtype, void *id, vertex->N.ip.sr.sid = *psid; vertex->N.ip.sr.label = sr_prefix_in_label(area, psid, local); + vertex->N.ip.sr.algorithm = psid->algorithm; + if (vertex->N.ip.sr.label != MPLS_INVALID_LABEL) vertex->N.ip.sr.present = true; +#ifndef FABRICD + if (flex_algo_id_valid(spftree->algorithm) && + !isis_flex_algo_elected_supported( + spftree->algorithm, spftree->area)) { + vertex->N.ip.sr.present = false; + vertex->N.ip.sr.label = MPLS_INVALID_LABEL; + } +#endif /* ifndef FABRICD */ + (void)hash_get(spftree->prefix_sids, vertex, hash_alloc_intern); } @@ -615,8 +653,8 @@ isis_spf_add2tent(struct isis_spftree *spftree, enum vertextype vtype, void *id, #ifdef EXTREME_DEBUG if (IS_DEBUG_SPF_EVENTS) zlog_debug( - "ISIS-SPF: add to TENT %s %s %s depth %d dist %d adjcount %d", - print_sys_hostname(vertex->N.id), + "ISIS-SPF: A:%hhu add to TENT %s %s %s depth %d dist %d adjcount %d", + spftree->algorithm, print_sys_hostname(vertex->N.id), vtype2string(vertex->type), vid2string(vertex, buff, sizeof(buff)), vertex->depth, vertex->d_N, listcount(vertex->Adj_N)); @@ -709,7 +747,8 @@ static void process_N(struct isis_spftree *spftree, enum vertextype vtype, #ifdef EXTREME_DEBUG if (IS_DEBUG_SPF_EVENTS) zlog_debug( - "ISIS-SPF: process_N %s %s %s dist %d already found from PATH", + "ISIS-SPF: A:%hhu process_N %s %s %s dist %d already found from PATH", + spftree->algorithm, print_sys_hostname(vertex->N.id), vtype2string(vtype), vid2string(vertex, buff, sizeof(buff)), dist); @@ -725,7 +764,8 @@ static void process_N(struct isis_spftree *spftree, enum vertextype vtype, #ifdef EXTREME_DEBUG if (IS_DEBUG_SPF_EVENTS) zlog_debug( - "ISIS-SPF: process_N %s %s %s dist %d parent %s adjcount %d", + "ISIS-SPF: A:%hhu process_N %s %s %s dist %d parent %s adjcount %d", + spftree->algorithm, print_sys_hostname(vertex->N.id), vtype2string(vtype), vid2string(vertex, buff, sizeof(buff)), dist, @@ -771,8 +811,9 @@ static void process_N(struct isis_spftree *spftree, enum vertextype vtype, #ifdef EXTREME_DEBUG if (IS_DEBUG_SPF_EVENTS) zlog_debug( - "ISIS-SPF: process_N add2tent %s %s dist %d parent %s", - print_sys_hostname(id), vtype2string(vtype), dist, + "ISIS-SPF: A:%hhu process_N add2tent %s %s dist %d parent %s", + spftree->algorithm, print_sys_hostname(id), + vtype2string(vtype), dist, (parent ? print_sys_hostname(parent->N.id) : "null")); #endif /* EXTREME_DEBUG */ @@ -832,7 +873,8 @@ lspfragloop: #ifdef EXTREME_DEBUG if (IS_DEBUG_SPF_EVENTS) - zlog_debug("ISIS-SPF: process_lsp %s", + zlog_debug("ISIS-SPF: A:%hhu process_lsp %s", + spftree->algorithm, print_sys_hostname(lsp->hdr.lsp_id)); #endif /* EXTREME_DEBUG */ @@ -889,6 +931,16 @@ lspfragloop: && !memcmp(er->id, null_sysid, ISIS_SYS_ID_LEN)) continue; +#ifndef FABRICD + + if (flex_algo_id_valid(spftree->algorithm) && + (!sr_algorithm_participated( + lsp, spftree->algorithm) || + isis_flex_algo_constraint_drop(spftree, + lsp, er))) + continue; +#endif /* ifndef FABRICD */ + dist = cost + (CHECK_FLAG(spftree->flags, F_SPFTREE_HOPCOUNT_METRIC) @@ -965,9 +1017,21 @@ lspfragloop: struct isis_prefix_sid *psid = (struct isis_prefix_sid *)i; - if (psid->algorithm != SR_ALGORITHM_SPF) + if (psid->algorithm != + spftree->algorithm) continue; +#ifndef FABRICD + if (flex_algo_id_valid( + spftree->algorithm) && + (!sr_algorithm_participated( + lsp, spftree->algorithm) || + !isis_flex_algo_elected_supported( + spftree->algorithm, + spftree->area))) + continue; +#endif /* ifndef FABRICD */ + has_valid_psid = true; process_N(spftree, VTYPE_IPREACH_TE, &ip_info, dist, depth + 1, @@ -1033,8 +1097,20 @@ lspfragloop: struct isis_prefix_sid *psid = (struct isis_prefix_sid *)i; - if (psid->algorithm != SR_ALGORITHM_SPF) + if (psid->algorithm != + spftree->algorithm) + continue; + +#ifndef FABRICD + if (flex_algo_id_valid( + spftree->algorithm) && + (!sr_algorithm_participated( + lsp, spftree->algorithm) || + !isis_flex_algo_elected_supported( + spftree->algorithm, + spftree->area))) continue; +#endif /* ifndef FABRICD */ has_valid_psid = true; process_N(spftree, vtype, &ip_info, @@ -1067,8 +1143,8 @@ end: && !isis_level2_adj_up(spftree->area)) { struct prefix_pair ip_info = { {0} }; if (IS_DEBUG_RTE_EVENTS) - zlog_debug("ISIS-Spf (%s): add default %s route", - rawlspid_print(lsp->hdr.lsp_id), + zlog_debug("ISIS-Spf (%pLS): add default %s route", + lsp->hdr.lsp_id, spftree->family == AF_INET ? "ipv4" : "ipv6"); @@ -1157,7 +1233,7 @@ static int isis_spf_preload_tent_ip_reach_cb(const struct prefix *prefix, struct isis_prefix_sid *psid = (struct isis_prefix_sid *)i; - if (psid->algorithm != SR_ALGORITHM_SPF) + if (psid->algorithm != spftree->algorithm) continue; has_valid_psid = true; @@ -1207,9 +1283,8 @@ static void isis_spf_preload_tent(struct isis_spftree *spftree, if (isis_lfa_excise_adj_check(spftree, adj_id)) { if (IS_DEBUG_LFA) - zlog_debug("ISIS-SPF: excising adjacency %s", - isis_format_id(sadj->id, - ISIS_SYS_ID_LEN + 1)); + zlog_debug("ISIS-SPF: excising adjacency %pPN", + sadj->id); continue; } @@ -1324,8 +1399,8 @@ static void spf_adj_list_parse_tlv(struct isis_spftree *spftree, LSP_FRAGMENT(lspid) = 0; lsp = lsp_search(spftree->lspdb, lspid); if (lsp == NULL || lsp->hdr.rem_lifetime == 0) { - zlog_warn("ISIS-SPF: No LSP found from root to L%d %s", - spftree->level, rawlspid_print(lspid)); + zlog_warn("ISIS-SPF: No LSP found from root to L%d %pLS", + spftree->level, lspid); return; } @@ -1421,6 +1496,19 @@ static void spf_adj_list_parse_lsp(struct isis_spftree *spftree, for (struct isis_extended_reach *reach = (struct isis_extended_reach *)head; reach; reach = reach->next) { +#ifndef FABRICD + /* + * cutting out adjacency by flex-algo link + * affinity attribute + */ + if (flex_algo_id_valid(spftree->algorithm) && + (!sr_algorithm_participated( + lsp, spftree->algorithm) || + isis_flex_algo_constraint_drop( + spftree, lsp, reach))) + continue; +#endif /* ifndef FABRICD */ + spf_adj_list_parse_tlv( spftree, adj_list, reach->id, pseudo_nodeid, pseudo_metric, @@ -1476,11 +1564,13 @@ static void add_to_paths(struct isis_spftree *spftree, #ifdef EXTREME_DEBUG if (IS_DEBUG_SPF_EVENTS) - zlog_debug("ISIS-SPF: added %s %s %s depth %d dist %d to PATHS", - print_sys_hostname(vertex->N.id), - vtype2string(vertex->type), - vid2string(vertex, buff, sizeof(buff)), - vertex->depth, vertex->d_N); + zlog_debug( + "ISIS-SPF: A:%hhu S:%p added %s %s %s depth %d dist %d to PATHS", + spftree->algorithm, spftree, + print_sys_hostname(vertex->N.id), + vtype2string(vertex->type), + vid2string(vertex, buff, sizeof(buff)), vertex->depth, + vertex->d_N); #endif /* EXTREME_DEBUG */ } @@ -1626,10 +1716,23 @@ static void spf_path_process(struct isis_spftree *spftree, break; } - isis_route_create( - &vertex->N.ip.p.dest, &vertex->N.ip.p.src, - vertex->d_N, vertex->depth, &vertex->N.ip.sr, - vertex->Adj_N, allow_ecmp, area, route_table); +#ifdef EXTREME_DEBUG + struct isis_route_info *ri = +#endif /* EXTREME_DEBUG */ + isis_route_create(&vertex->N.ip.p.dest, + &vertex->N.ip.p.src, + vertex->d_N, vertex->depth, + &vertex->N.ip.sr, + vertex->Adj_N, allow_ecmp, + area, route_table); + +#ifdef EXTREME_DEBUG + zlog_debug( + "ISIS-SPF: A:%hhu create route pfx %pFX dist %d, sr.algo %d, table %p, rv %p", + spftree->algorithm, &vertex->N.ip.p.dest, + vertex->d_N, vertex->N.ip.sr.algorithm, + route_table, ri); +#endif /* EXTREME_DEBUG */ } else if (IS_DEBUG_SPF_EVENTS) zlog_debug( "ISIS-SPF: no adjacencies, do not install route for %s depth %d dist %d", @@ -1649,12 +1752,10 @@ static void isis_spf_loop(struct isis_spftree *spftree, vertex = isis_vertex_queue_pop(&spftree->tents); #ifdef EXTREME_DEBUG - if (IS_DEBUG_SPF_EVENTS) - zlog_debug( - "ISIS-SPF: get TENT node %s %s depth %d dist %d to PATHS", - print_sys_hostname(vertex->N.id), - vtype2string(vertex->type), vertex->depth, - vertex->d_N); + zlog_debug( + "ISIS-SPF: A:%hhu get TENT node %s %s depth %d dist %d to PATHS", + spftree->algorithm, print_sys_hostname(vertex->N.id), + vtype2string(vertex->type), vertex->depth, vertex->d_N); #endif /* EXTREME_DEBUG */ add_to_paths(spftree, vertex); @@ -1663,9 +1764,8 @@ static void isis_spf_loop(struct isis_spftree *spftree, lsp = lsp_for_vertex(spftree, vertex); if (!lsp) { - zlog_warn("ISIS-SPF: No LSP found for %s", - isis_format_id(vertex->N.id, - sizeof(vertex->N.id))); + zlog_warn("ISIS-SPF: No LSP found for %pPN", + vertex->N.id); continue; } @@ -1703,10 +1803,10 @@ struct isis_spftree *isis_run_hopcount_spf(struct isis_area *area, struct isis_spftree *spftree) { if (!spftree) - spftree = isis_spftree_new(area, &area->lspdb[IS_LEVEL_2 - 1], - sysid, ISIS_LEVEL2, SPFTREE_IPV4, - SPF_TYPE_FORWARD, - F_SPFTREE_HOPCOUNT_METRIC); + spftree = isis_spftree_new( + area, &area->lspdb[IS_LEVEL_2 - 1], sysid, ISIS_LEVEL2, + SPFTREE_IPV4, SPF_TYPE_FORWARD, + F_SPFTREE_HOPCOUNT_METRIC, SR_ALGORITHM_SPF); init_spt(spftree, ISIS_MT_IPV4_UNICAST); if (!memcmp(sysid, area->isis->sysid, ISIS_SYS_ID_LEN)) { @@ -1777,6 +1877,27 @@ void isis_run_spf(struct isis_spftree *spftree) exit(1); } +#ifndef FABRICD + /* If a node is configured to participate in a particular Flexible- + * Algorithm, but there is no valid Flex-Algorithm definition available + * for it, or the selected Flex-Algorithm definition includes + * calculation-type, metric-type, constraint, flag, or Sub-TLV that is + * not supported by the node, it MUST stop participating in such + * Flexible-Algorithm. + */ + if (flex_algo_id_valid(spftree->algorithm) && + !isis_flex_algo_elected_supported(spftree->algorithm, + spftree->area)) { + if (!CHECK_FLAG(spftree->flags, F_SPFTREE_DISABLED)) { + isis_spftree_clear(spftree); + SET_FLAG(spftree->flags, F_SPFTREE_DISABLED); + lsp_regenerate_schedule(spftree->area, + spftree->area->is_type, 0); + } + goto out; + } +#endif /* ifndef FABRICD */ + /* * C.2.5 Step 0 */ @@ -1797,6 +1918,18 @@ void isis_run_spf(struct isis_spftree *spftree) } isis_spf_loop(spftree, spftree->sysid); + + +#ifndef FABRICD + /* flex-algo */ + if (CHECK_FLAG(spftree->flags, F_SPFTREE_DISABLED)) { + UNSET_FLAG(spftree->flags, F_SPFTREE_DISABLED); + lsp_regenerate_schedule(spftree->area, spftree->area->is_type, + 0); + } + +out: +#endif /* ifndef FABRICD */ spftree->runcount++; spftree->last_run_timestamp = time(NULL); spftree->last_run_monotime = monotime(&time_end); @@ -1818,29 +1951,37 @@ static void isis_run_spf_with_protection(struct isis_area *area, isis_spf_run_lfa(area, spftree); } -void isis_spf_verify_routes(struct isis_area *area, struct isis_spftree **trees) +void isis_spf_verify_routes(struct isis_area *area, struct isis_spftree **trees, + int tree) { if (area->is_type == IS_LEVEL_1) { isis_route_verify_table(area, trees[0]->route_table, - trees[0]->route_table_backup); + trees[0]->route_table_backup, tree); } else if (area->is_type == IS_LEVEL_2) { isis_route_verify_table(area, trees[1]->route_table, - trees[1]->route_table_backup); + trees[1]->route_table_backup, tree); } else { isis_route_verify_merge(area, trees[0]->route_table, trees[0]->route_table_backup, trees[1]->route_table, - trees[1]->route_table_backup); + trees[1]->route_table_backup, tree); } } void isis_spf_invalidate_routes(struct isis_spftree *tree) { + struct isis_route_table_info *backup_info; + isis_route_invalidate_table(tree->area, tree->route_table); /* Delete backup routes. */ + + backup_info = tree->route_table_backup->info; route_table_finish(tree->route_table_backup); + isis_route_table_info_free(backup_info); tree->route_table_backup = srcdest_table_init(); + tree->route_table_backup->info = + isis_route_table_info_alloc(tree->algorithm); tree->route_table_backup->cleanup = isis_route_node_cleanup; } @@ -1859,6 +2000,12 @@ static void isis_run_spf_cb(struct event *thread) struct isis_area *area = run->area; int level = run->level; int have_run = 0; + struct listnode *node; + struct isis_circuit *circuit; +#ifndef FABRICD + struct flex_algo *fa; + struct isis_flex_algo_data *data; +#endif /* ifndef FABRICD */ XFREE(MTYPE_ISIS_SPF_RUN, run); @@ -1879,11 +2026,27 @@ static void isis_run_spf_cb(struct event *thread) if (area->ip_circuits) { isis_run_spf_with_protection( area, area->spftree[SPFTREE_IPV4][level - 1]); +#ifndef FABRICD + for (ALL_LIST_ELEMENTS_RO(area->flex_algos->flex_algos, node, + fa)) { + data = fa->data; + isis_run_spf_with_protection( + area, data->spftree[SPFTREE_IPV4][level - 1]); + } +#endif /* ifndef FABRICD */ have_run = 1; } if (area->ipv6_circuits) { isis_run_spf_with_protection( area, area->spftree[SPFTREE_IPV6][level - 1]); +#ifndef FABRICD + for (ALL_LIST_ELEMENTS_RO(area->flex_algos->flex_algos, node, + fa)) { + data = fa->data; + isis_run_spf_with_protection( + area, data->spftree[SPFTREE_IPV6][level - 1]); + } +#endif /* ifndef FABRICD */ have_run = 1; } if (area->ipv6_circuits && isis_area_ipv6_dstsrc_enabled(area)) { @@ -1898,8 +2061,6 @@ static void isis_run_spf_cb(struct event *thread) isis_area_verify_routes(area); /* walk all circuits and reset any spf specific flags */ - struct listnode *node; - struct isis_circuit *circuit; for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) UNSET_FLAG(circuit->flags, ISIS_CIRCUIT_FLAPPED_AFTER_SPF); @@ -2105,8 +2266,13 @@ void isis_print_spftree(struct vty *vty, struct isis_spftree *spftree) } static void show_isis_topology_common(struct vty *vty, int levels, - struct isis *isis) + struct isis *isis, uint8_t algo) { +#ifndef FABRICD + struct isis_flex_algo_data *fa_data; + struct flex_algo *fa; +#endif /* ifndef FABRICD */ + struct isis_spftree *spftree; struct listnode *node; struct isis_area *area; @@ -2114,27 +2280,68 @@ static void show_isis_topology_common(struct vty *vty, int levels, return; for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) { - vty_out(vty, "Area %s:\n", - area->area_tag ? area->area_tag : "null"); + vty_out(vty, + "Area %s:", area->area_tag ? area->area_tag : "null"); + +#ifndef FABRICD + /* + * The shapes of the flex algo spftree 2-dimensional array + * and the area spftree 2-dimensional array are not guaranteed + * to be identical. + */ + fa = NULL; + if (flex_algo_id_valid(algo)) { + fa = flex_algo_lookup(area->flex_algos, algo); + if (!fa) + continue; + fa_data = (struct isis_flex_algo_data *)fa->data; + } else + fa_data = NULL; + + if (algo != SR_ALGORITHM_SPF) + vty_out(vty, " Algorithm %hhu\n", algo); + else +#endif /* ifndef FABRICD */ + vty_out(vty, "\n"); for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) { if ((level & levels) == 0) continue; if (area->ip_circuits > 0) { - isis_print_spftree( - vty, - area->spftree[SPFTREE_IPV4][level - 1]); +#ifndef FABRICD + if (fa_data) + spftree = fa_data->spftree[SPFTREE_IPV4] + [level - 1]; + else +#endif /* ifndef FABRICD */ + spftree = area->spftree[SPFTREE_IPV4] + [level - 1]; + + isis_print_spftree(vty, spftree); } if (area->ipv6_circuits > 0) { - isis_print_spftree( - vty, - area->spftree[SPFTREE_IPV6][level - 1]); +#ifndef FABRICD + if (fa_data) + spftree = fa_data->spftree[SPFTREE_IPV6] + [level - 1]; + else +#endif /* ifndef FABRICD */ + spftree = area->spftree[SPFTREE_IPV6] + [level - 1]; + isis_print_spftree(vty, spftree); } if (isis_area_ipv6_dstsrc_enabled(area)) { - isis_print_spftree(vty, - area->spftree[SPFTREE_DSTSRC] - [level - 1]); +#ifndef FABRICD + if (fa_data) + spftree = + fa_data->spftree[SPFTREE_DSTSRC] + [level - 1]; + else +#endif /* ifndef FABRICD */ + spftree = area->spftree[SPFTREE_DSTSRC] + [level - 1]; + isis_print_spftree(vty, spftree); } } @@ -2154,7 +2361,8 @@ DEFUN(show_isis_topology, show_isis_topology_cmd, " [vrf <NAME|all>] topology" #ifndef FABRICD " [<level-1|level-2>]" -#endif + " [algorithm (128-255)]" +#endif /* ifndef FABRICD */ , SHOW_STR PROTO_HELP VRF_CMD_HELP_STR "All VRFs\n" @@ -2162,49 +2370,238 @@ DEFUN(show_isis_topology, show_isis_topology_cmd, #ifndef FABRICD "Paths to all level-1 routers in the area\n" "Paths to all level-2 routers in the domain\n" -#endif + "Show Flex-algo routes\n" + "Algorithm number\n" +#endif /* ifndef FABRICD */ ) { int levels = ISIS_LEVELS; struct listnode *node; struct isis *isis = NULL; - int idx = 0; const char *vrf_name = VRF_DEFAULT_NAME; bool all_vrf = false; int idx_vrf = 0; + uint8_t algorithm = SR_ALGORITHM_SPF; +#ifndef FABRICD + int idx = 0; - if (argv_find(argv, argc, "topology", &idx)) { - if (argc < idx + 2) - levels = ISIS_LEVEL1 | ISIS_LEVEL2; - else if (strmatch(argv[idx + 1]->arg, "level-1")) - levels = ISIS_LEVEL1; - else - levels = ISIS_LEVEL2; + levels = ISIS_LEVEL1 | ISIS_LEVEL2; + if (argv_find(argv, argc, "level-1", &idx)) + levels = ISIS_LEVEL1; + if (argv_find(argv, argc, "level-2", &idx)) + levels = ISIS_LEVEL2; + if (argv_find(argv, argc, "algorithm", &idx)) + algorithm = (uint8_t)strtoul(argv[idx + 1]->arg, NULL, 10); +#endif /* ifndef FABRICD */ + + if (!im) { + vty_out(vty, "IS-IS Routing Process not enabled\n"); + return CMD_SUCCESS; } + ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + + if (vrf_name) { + if (all_vrf) { + for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) + show_isis_topology_common(vty, levels, isis, + algorithm); + return CMD_SUCCESS; + } + isis = isis_lookup_by_vrfname(vrf_name); + if (isis != NULL) + show_isis_topology_common(vty, levels, isis, algorithm); + } + + return CMD_SUCCESS; +} + +#ifndef FABRICD +static void show_isis_flex_algo_display_eag(struct vty *vty, char *buf, + int indent, + struct admin_group *admin_group) +{ + if (admin_group_zero(admin_group)) + vty_out(vty, "not-set\n"); + else { + vty_out(vty, "%s\n", + admin_group_string(buf, ADMIN_GROUP_PRINT_MAX_SIZE, + indent, admin_group)); + admin_group_print(buf, indent, admin_group); + if (buf[0] != '\0') + vty_out(vty, " Bit positions: %s\n", buf); + } +} + +static void show_isis_flex_algo_common(struct vty *vty, struct isis *isis, + uint8_t algorithm) +{ + struct isis_router_cap_fad *router_fad; + char buf[ADMIN_GROUP_PRINT_MAX_SIZE]; + struct admin_group *admin_group; + struct isis_area *area; + struct listnode *node; + struct flex_algo *fa; + int indent, algo; + bool fad_identical, fad_supported; + + if (!isis->area_list || isis->area_list->count == 0) + return; + + for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) { + /* + * The shapes of the flex algo spftree 2-dimensional array + * and the area spftree 2-dimensional array are not guaranteed + * to be identical. + */ + + for (algo = 0; algo < SR_ALGORITHM_COUNT; algo++) { + if (algorithm != SR_ALGORITHM_UNSET && + algorithm != algo) + continue; + + fa = flex_algo_lookup(area->flex_algos, algo); + if (!fa) + continue; + + vty_out(vty, "Area %s:", + area->area_tag ? area->area_tag : "null"); + + vty_out(vty, " Algorithm %d\n", algo); + vty_out(vty, "\n"); + + vty_out(vty, " Enabled Data-Planes:"); + if (fa->dataplanes == 0) { + vty_out(vty, " None\n\n"); + continue; + } + if (CHECK_FLAG(fa->dataplanes, FLEX_ALGO_SR_MPLS)) + vty_out(vty, " SR-MPLS"); + if (CHECK_FLAG(fa->dataplanes, FLEX_ALGO_SRV6)) + vty_out(vty, " SRv6"); + if (CHECK_FLAG(fa->dataplanes, FLEX_ALGO_IP)) + vty_out(vty, " IP"); + vty_out(vty, "\n\n"); + + + router_fad = isis_flex_algo_elected(algo, area); + vty_out(vty, + " Elected and running Flexible-Algorithm Definition:\n"); + if (router_fad) + vty_out(vty, " Source: %pSY\n", + router_fad->sysid); + else + vty_out(vty, " Source: Not found\n"); + + if (!router_fad) { + vty_out(vty, "\n"); + continue; + } + + fad_identical = + flex_algo_definition_cmp(fa, &router_fad->fad); + fad_supported = + isis_flex_algo_supported(&router_fad->fad); + vty_out(vty, " Priority: %d\n", + router_fad->fad.priority); + vty_out(vty, " Equal to local: %s\n", + fad_identical ? "yes" : "no"); + vty_out(vty, " Local state: %s\n", + fad_supported + ? "enabled" + : "disabled (unsupported definition)"); + vty_out(vty, " Calculation type: "); + if (router_fad->fad.calc_type == 0) + vty_out(vty, "spf\n"); + else + vty_out(vty, "%d\n", router_fad->fad.calc_type); + vty_out(vty, " Metric type: %s\n", + flex_algo_metric_type_print( + buf, sizeof(buf), + router_fad->fad.metric_type)); + vty_out(vty, " Prefix-metric: %s\n", + CHECK_FLAG(router_fad->fad.flags, FAD_FLAG_M) + ? "enabled" + : "disabled"); + if (router_fad->fad.flags != 0 && + router_fad->fad.flags != FAD_FLAG_M) + vty_out(vty, " Flags: 0x%x\n", + router_fad->fad.flags); + vty_out(vty, " Exclude SRLG: %s\n", + router_fad->fad.exclude_srlg ? "enabled" + : "disabled"); + + admin_group = &router_fad->fad.admin_group_exclude_any; + indent = vty_out(vty, " Exclude-any admin-group: "); + show_isis_flex_algo_display_eag(vty, buf, indent, + admin_group); + + admin_group = &router_fad->fad.admin_group_include_all; + indent = vty_out(vty, " Include-all admin-group: "); + show_isis_flex_algo_display_eag(vty, buf, indent, + admin_group); + + admin_group = &router_fad->fad.admin_group_include_any; + indent = vty_out(vty, " Include-any admin-group: "); + show_isis_flex_algo_display_eag(vty, buf, indent, + admin_group); + + if (router_fad->fad.unsupported_subtlv) + vty_out(vty, + " Unsupported sub-TLV: Present (see logs)"); + + vty_out(vty, "\n"); + } + } +} + +DEFUN(show_isis_flex_algo, show_isis_flex_algo_cmd, + "show " PROTO_NAME + " [vrf <NAME|all>] flex-algo" + " [(128-255)]", + SHOW_STR PROTO_HELP VRF_CMD_HELP_STR + "All VRFs\n" + "IS-IS Flex-algo information\n" + "Algorithm number\n") +{ + struct isis *isis; + struct listnode *node; + const char *vrf_name = VRF_DEFAULT_NAME; + bool all_vrf = false; + int idx = 0; + int idx_vrf = 0; + uint8_t flex_algo; if (!im) { vty_out(vty, "IS-IS Routing Process not enabled\n"); return CMD_SUCCESS; } + + if (argv_find(argv, argc, "flex-algo", &idx) && (idx + 1) < argc) + flex_algo = (uint8_t)strtoul(argv[idx + 1]->arg, NULL, 10); + else + flex_algo = SR_ALGORITHM_UNSET; + ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (vrf_name) { if (all_vrf) { for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) - show_isis_topology_common(vty, levels, isis); + show_isis_flex_algo_common(vty, isis, + flex_algo); return CMD_SUCCESS; } isis = isis_lookup_by_vrfname(vrf_name); if (isis != NULL) - show_isis_topology_common(vty, levels, isis); + show_isis_flex_algo_common(vty, isis, flex_algo); } return CMD_SUCCESS; } +#endif /* ifndef FABRICD */ static void isis_print_route(struct ttable *tt, const struct prefix *prefix, struct isis_route_info *rinfo, bool prefix_sid, - bool no_adjacencies) + bool no_adjacencies, bool json) { struct isis_nexthop *nexthop; struct listnode *node; @@ -2212,96 +2609,116 @@ static void isis_print_route(struct ttable *tt, const struct prefix *prefix, char buf_prefix[BUFSIZ]; (void)prefix2str(prefix, buf_prefix, sizeof(buf_prefix)); - for (ALL_LIST_ELEMENTS_RO(rinfo->nexthops, node, nexthop)) { - struct interface *ifp; - char buf_iface[BUFSIZ]; - char buf_nhop[BUFSIZ]; - - if (!no_adjacencies) { - inet_ntop(nexthop->family, &nexthop->ip, buf_nhop, - sizeof(buf_nhop)); - ifp = if_lookup_by_index(nexthop->ifindex, VRF_DEFAULT); - if (ifp) - strlcpy(buf_iface, ifp->name, - sizeof(buf_iface)); - else - snprintf(buf_iface, sizeof(buf_iface), - "ifindex %u", nexthop->ifindex); - } else { - strlcpy(buf_nhop, print_sys_hostname(nexthop->sysid), - sizeof(buf_nhop)); - strlcpy(buf_iface, "-", sizeof(buf_iface)); - } - - if (prefix_sid) { - char buf_sid[BUFSIZ] = {}; - char buf_lblop[BUFSIZ] = {}; - - if (nexthop->sr.present) { - snprintf(buf_sid, sizeof(buf_sid), "%u", - nexthop->sr.sid.value); - sr_op2str(buf_lblop, sizeof(buf_lblop), - rinfo->sr.label, nexthop->sr.label); + for (int alg = 0; alg < SR_ALGORITHM_COUNT; alg++) { + for (ALL_LIST_ELEMENTS_RO(rinfo->sr_algo[alg].nexthops, node, + nexthop)) { + struct interface *ifp; + char buf_iface[BUFSIZ]; + char buf_nhop[BUFSIZ]; + + if (!no_adjacencies) { + inet_ntop(nexthop->family, &nexthop->ip, + buf_nhop, sizeof(buf_nhop)); + ifp = if_lookup_by_index(nexthop->ifindex, + VRF_DEFAULT); + if (ifp) + strlcpy(buf_iface, ifp->name, + sizeof(buf_iface)); + else + snprintf(buf_iface, sizeof(buf_iface), + "ifindex %u", + nexthop->ifindex); } else { - strlcpy(buf_sid, "-", sizeof(buf_sid)); - strlcpy(buf_lblop, "-", sizeof(buf_lblop)); + strlcpy(buf_nhop, + print_sys_hostname(nexthop->sysid), + sizeof(buf_nhop)); + strlcpy(buf_iface, "-", sizeof(buf_iface)); } - if (first) { - ttable_add_row(tt, "%s|%u|%s|%s|%s|%s", - buf_prefix, rinfo->cost, - buf_iface, buf_nhop, buf_sid, - buf_lblop); - first = false; - } else - ttable_add_row(tt, "||%s|%s|%s|%s", buf_iface, - buf_nhop, buf_sid, buf_lblop); - } else { - char buf_labels[BUFSIZ] = {}; - - if (nexthop->label_stack) { - for (int i = 0; - i < nexthop->label_stack->num_labels; - i++) { - char buf_label[BUFSIZ]; - - label2str( - nexthop->label_stack->label[i], - 0, buf_label, - sizeof(buf_label)); - if (i != 0) - strlcat(buf_labels, "/", + if (prefix_sid) { + char buf_sid[BUFSIZ] = {}; + char buf_lblop[BUFSIZ] = {}; + + if (rinfo->sr_algo[alg].present) { + snprintf(buf_sid, sizeof(buf_sid), "%u", + rinfo->sr_algo[alg].sid.value); + sr_op2str(buf_lblop, sizeof(buf_lblop), + rinfo->sr_algo[alg].label, + nexthop->sr.label); + } else if (alg == SR_ALGORITHM_SPF) { + strlcpy(buf_sid, "-", sizeof(buf_sid)); + strlcpy(buf_lblop, "-", + sizeof(buf_lblop)); + } else { + continue; + } + + if (first || json) { + ttable_add_row(tt, + "%s|%u|%s|%s|%s|%s|%d", + buf_prefix, rinfo->cost, + buf_iface, buf_nhop, + buf_sid, buf_lblop, alg); + first = false; + } else + ttable_add_row(tt, "||%s|%s|%s|%s|%d", + buf_iface, buf_nhop, + buf_sid, buf_lblop, alg); + } else { + char buf_labels[BUFSIZ] = {}; + + if (nexthop->label_stack) { + for (int i = 0; + i < + nexthop->label_stack->num_labels; + i++) { + char buf_label[BUFSIZ]; + + label2str(nexthop->label_stack + ->label[i], + 0, buf_label, + sizeof(buf_label)); + if (i != 0) + strlcat(buf_labels, "/", + sizeof(buf_labels)); + strlcat(buf_labels, buf_label, sizeof(buf_labels)); - strlcat(buf_labels, buf_label, + } + } else if (nexthop->sr.present) + label2str(nexthop->sr.label, 0, + buf_labels, + sizeof(buf_labels)); + else + strlcpy(buf_labels, "-", sizeof(buf_labels)); - } - } else if (nexthop->sr.present) - label2str(nexthop->sr.label, 0, buf_labels, - sizeof(buf_labels)); - else - strlcpy(buf_labels, "-", sizeof(buf_labels)); - - if (first) { - ttable_add_row(tt, "%s|%u|%s|%s|%s", buf_prefix, - rinfo->cost, buf_iface, buf_nhop, - buf_labels); - first = false; - } else - ttable_add_row(tt, "||%s|%s|%s", buf_iface, - buf_nhop, buf_labels); + + if (first || json) { + ttable_add_row(tt, "%s|%u|%s|%s|%s", + buf_prefix, rinfo->cost, + buf_iface, buf_nhop, + buf_labels); + first = false; + } else + ttable_add_row(tt, "||%s|%s|%s", + buf_iface, buf_nhop, + buf_labels); + } } } + if (list_isempty(rinfo->nexthops)) { if (prefix_sid) { char buf_sid[BUFSIZ] = {}; char buf_lblop[BUFSIZ] = {}; - if (rinfo->sr.present) { + if (rinfo->sr_algo[SR_ALGORITHM_SPF].present) { snprintf(buf_sid, sizeof(buf_sid), "%u", - rinfo->sr.sid.value); - sr_op2str(buf_lblop, sizeof(buf_lblop), - rinfo->sr.label, - MPLS_LABEL_IMPLICIT_NULL); + rinfo->sr_algo[SR_ALGORITHM_SPF] + .sid.value); + sr_op2str( + buf_lblop, sizeof(buf_lblop), + rinfo->sr_algo[SR_ALGORITHM_SPF].label, + MPLS_LABEL_IMPLICIT_NULL); } else { strlcpy(buf_sid, "-", sizeof(buf_sid)); strlcpy(buf_lblop, "-", sizeof(buf_lblop)); @@ -2317,7 +2734,7 @@ static void isis_print_route(struct ttable *tt, const struct prefix *prefix, } void isis_print_routes(struct vty *vty, struct isis_spftree *spftree, - bool prefix_sid, bool backup) + struct json_object **json, bool prefix_sid, bool backup) { struct route_table *route_table; struct ttable *tt; @@ -2343,13 +2760,16 @@ void isis_print_routes(struct vty *vty, struct isis_spftree *spftree, return; } - vty_out(vty, "IS-IS %s %s routing table:\n\n", - circuit_t2string(spftree->level), tree_id_text); + if (json == NULL) + vty_out(vty, "IS-IS %s %s routing table:\n\n", + circuit_t2string(spftree->level), tree_id_text); /* Prepare table. */ tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]); if (prefix_sid) - ttable_add_row(tt, "Prefix|Metric|Interface|Nexthop|SID|Label Op."); + ttable_add_row( + tt, + "Prefix|Metric|Interface|Nexthop|SID|Label Op.|Algo"); else ttable_add_row(tt, "Prefix|Metric|Interface|Nexthop|Label(s)"); tt->style.cell.rpad = 2; @@ -2369,55 +2789,157 @@ void isis_print_routes(struct vty *vty, struct isis_spftree *spftree, if (!rinfo) continue; - isis_print_route(tt, &rn->p, rinfo, prefix_sid, no_adjacencies); + isis_print_route(tt, &rn->p, rinfo, prefix_sid, no_adjacencies, + json != NULL); } /* Dump the generated table. */ - if (tt->nrows > 1) { + if (json == NULL && tt->nrows > 1) { char *table; table = ttable_dump(tt, "\n"); vty_out(vty, "%s\n", table); XFREE(MTYPE_TMP, table); + } else if (json) { + *json = ttable_json(tt, prefix_sid ? "sdssdsdd" : "sdsss"); } ttable_del(tt); } static void show_isis_route_common(struct vty *vty, int levels, struct isis *isis, bool prefix_sid, - bool backup) + bool backup, uint8_t algo, + json_object **json) { + json_object *json_level = NULL, *jstr = NULL, *json_val; +#ifndef FABRICD + struct isis_flex_algo_data *fa_data; + struct flex_algo *fa; +#endif /* ifndef FABRICD */ + struct isis_spftree *spftree; struct listnode *node; struct isis_area *area; + char key[8]; if (!isis->area_list || isis->area_list->count == 0) return; + if (json) + *json = json_object_new_object(); + for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) { - vty_out(vty, "Area %s:\n", - area->area_tag ? area->area_tag : "null"); +#ifndef FABRICD + /* + * The shapes of the flex algo spftree 2-dimensional array + * and the area spftree 2-dimensional array are not guaranteed + * to be identical. + */ + fa = NULL; + if (flex_algo_id_valid(algo)) { + fa = flex_algo_lookup(area->flex_algos, algo); + if (!fa) + continue; + fa_data = (struct isis_flex_algo_data *)fa->data; + } else { + fa_data = NULL; + } +#endif /* ifndef FABRICD */ + + if (json) { + jstr = json_object_new_string( + area->area_tag ? area->area_tag : "null"); + json_object_object_add(*json, "area", jstr); + } else { + vty_out(vty, "Area %s:", + area->area_tag ? area->area_tag : "null"); +#ifndef FABRICD + if (algo != SR_ALGORITHM_SPF) + vty_out(vty, " Algorithm %hhu\n", algo); + else +#endif /* ifndef FABRICD */ + vty_out(vty, "\n"); + } for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) { if ((level & levels) == 0) continue; + if (json) { + json_level = json_object_new_object(); + jstr = json_object_new_string( + area->area_tag ? area->area_tag + : "null"); + json_object_object_add(json_level, "area", + jstr); + } + if (area->ip_circuits > 0) { - isis_print_routes( - vty, - area->spftree[SPFTREE_IPV4][level - 1], - prefix_sid, backup); + json_val = NULL; +#ifndef FABRICD + if (fa_data) + spftree = fa_data->spftree[SPFTREE_IPV4] + [level - 1]; + else +#endif /* ifndef FABRICD */ + spftree = area->spftree[SPFTREE_IPV4] + [level - 1]; + + isis_print_spftree(vty, spftree); + + isis_print_routes(vty, spftree, + json ? &json_val : NULL, + prefix_sid, backup); + if (json && json_val) { + json_object_object_add( + json_level, "ipv4", json_val); + } } if (area->ipv6_circuits > 0) { - isis_print_routes( - vty, - area->spftree[SPFTREE_IPV6][level - 1], - prefix_sid, backup); + json_val = NULL; +#ifndef FABRICD + if (fa_data) + spftree = fa_data->spftree[SPFTREE_IPV6] + [level - 1]; + else +#endif /* ifndef FABRICD */ + spftree = area->spftree[SPFTREE_IPV6] + [level - 1]; + + isis_print_spftree(vty, spftree); + + isis_print_routes(vty, spftree, + json ? &json_val : NULL, + prefix_sid, backup); + if (json && json_val) { + json_object_object_add( + json_level, "ipv6", json_val); + } } if (isis_area_ipv6_dstsrc_enabled(area)) { - isis_print_routes(vty, - area->spftree[SPFTREE_DSTSRC] - [level - 1], + json_val = NULL; +#ifndef FABRICD + if (fa_data) + spftree = + fa_data->spftree[SPFTREE_DSTSRC] + [level - 1]; + else +#endif /* ifndef FABRICD */ + spftree = area->spftree[SPFTREE_DSTSRC] + [level - 1]; + + isis_print_spftree(vty, spftree); + isis_print_routes(vty, spftree, + json ? &json_val : NULL, prefix_sid, backup); + if (json && json_val) { + json_object_object_add(json_level, + "ipv6-dstsrc", + json_val); + } + } + if (json) { + snprintf(key, sizeof(key), "level-%d", level); + json_object_object_add(*json, key, json_level); } } } @@ -2428,16 +2950,25 @@ DEFUN(show_isis_route, show_isis_route_cmd, " [vrf <NAME|all>] route" #ifndef FABRICD " [<level-1|level-2>]" -#endif - " [<prefix-sid|backup>]", +#endif /* ifndef FABRICD */ + " [<prefix-sid|backup>]" +#ifndef FABRICD + " [algorithm (128-255)]" +#endif /* ifndef FABRICD */ + " [json$uj]", SHOW_STR PROTO_HELP VRF_FULL_CMD_HELP_STR "IS-IS routing table\n" #ifndef FABRICD "level-1 routes\n" "level-2 routes\n" -#endif +#endif /* ifndef FABRICD */ "Show Prefix-SID information\n" - "Show backup routes\n") + "Show backup routes\n" +#ifndef FABRICD + "Show Flex-algo routes\n" + "Algorithm number\n" +#endif /* ifndef FABRICD */ + JSON_STR) { int levels; struct isis *isis; @@ -2446,7 +2977,10 @@ DEFUN(show_isis_route, show_isis_route_cmd, bool all_vrf = false; bool prefix_sid = false; bool backup = false; + bool uj = use_json(argc, argv); int idx = 0; + json_object *json = NULL, *json_vrf = NULL; + uint8_t algorithm = SR_ALGORITHM_SPF; if (argv_find(argv, argc, "level-1", &idx)) levels = ISIS_LEVEL1; @@ -2466,17 +3000,50 @@ DEFUN(show_isis_route, show_isis_route_cmd, if (argv_find(argv, argc, "backup", &idx)) backup = true; +#ifndef FABRICD + if (argv_find(argv, argc, "algorithm", &idx)) + algorithm = (uint8_t)strtoul(argv[idx + 1]->arg, NULL, 10); +#endif /* ifndef FABRICD */ + + if (uj) + json = json_object_new_array(); + if (vrf_name) { if (all_vrf) { - for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) - show_isis_route_common(vty, levels, isis, - prefix_sid, backup); - return CMD_SUCCESS; + for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) { + show_isis_route_common( + vty, levels, isis, prefix_sid, backup, + algorithm, uj ? &json_vrf : NULL); + if (uj) { + json_object_object_add( + json_vrf, "vrf_id", + json_object_new_int( + isis->vrf_id)); + json_object_array_add(json, json_vrf); + } + } + goto out; } isis = isis_lookup_by_vrfname(vrf_name); - if (isis != NULL) + if (isis != NULL) { show_isis_route_common(vty, levels, isis, prefix_sid, - backup); + backup, algorithm, + uj ? &json_vrf : NULL); + if (uj) { + json_object_object_add( + json_vrf, "vrf_id", + json_object_new_int(isis->vrf_id)); + json_object_array_add(json, json_vrf); + } + } + } + +out: + 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; @@ -2696,6 +3263,9 @@ DEFUN(show_isis_frr_summary, show_isis_frr_summary_cmd, void isis_spf_init(void) { +#ifndef FABRICD + install_element(VIEW_NODE, &show_isis_flex_algo_cmd); +#endif /* ifndef FABRICD */ install_element(VIEW_NODE, &show_isis_topology_cmd); install_element(VIEW_NODE, &show_isis_route_cmd); install_element(VIEW_NODE, &show_isis_frr_summary_cmd); diff --git a/isisd/isis_spf.h b/isisd/isis_spf.h index 7f4ab707e7..7e9754d9bf 100644 --- a/isisd/isis_spf.h +++ b/isisd/isis_spf.h @@ -12,6 +12,7 @@ #define _ZEBRA_ISIS_SPF_H #include "isisd/isis_lfa.h" +#include "lib/json.h" struct isis_spftree; @@ -37,16 +38,15 @@ struct isis_spf_adj { #define F_ISIS_SPF_ADJ_METRIC_INFINITY 0x04 }; -struct isis_spftree *isis_spftree_new(struct isis_area *area, - struct lspdb_head *lspdb, - const uint8_t *sysid, int level, - enum spf_tree_id tree_id, - enum spf_type type, uint8_t flags); +struct isis_spftree * +isis_spftree_new(struct isis_area *area, struct lspdb_head *lspdb, + const uint8_t *sysid, int level, enum spf_tree_id tree_id, + enum spf_type type, uint8_t flags, uint8_t algorithm); struct isis_vertex *isis_spf_prefix_sid_lookup(struct isis_spftree *spftree, struct isis_prefix_sid *psid); void isis_spf_invalidate_routes(struct isis_spftree *tree); -void isis_spf_verify_routes(struct isis_area *area, - struct isis_spftree **trees); +void isis_spf_verify_routes(struct isis_area *area, struct isis_spftree **trees, + int tree); void isis_spf_switchover_routes(struct isis_area *area, struct isis_spftree **trees, int family, union g_addr *nexthop_ip, ifindex_t ifindex, @@ -63,7 +63,7 @@ int _isis_spf_schedule(struct isis_area *area, int level, const char *func, const char *file, int line); void isis_print_spftree(struct vty *vty, struct isis_spftree *spftree); void isis_print_routes(struct vty *vty, struct isis_spftree *spftree, - bool prefix_sid, bool backup); + json_object **json, bool prefix_sid, bool backup); void isis_spf_init(void); void isis_spf_print(struct isis_spftree *spftree, struct vty *vty); void isis_spf_print_json(struct isis_spftree *spftree, diff --git a/isisd/isis_spf_private.h b/isisd/isis_spf_private.h index d829372702..763673063c 100644 --- a/isisd/isis_spf_private.h +++ b/isisd/isis_spf_private.h @@ -349,11 +349,16 @@ struct isis_spftree { uint32_t total[SPF_PREFIX_PRIO_MAX]; } protection_counters; } lfa; + uint8_t algorithm; uint8_t flags; }; #define F_SPFTREE_HOPCOUNT_METRIC 0x01 #define F_SPFTREE_NO_ROUTES 0x02 #define F_SPFTREE_NO_ADJACENCIES 0x04 +#ifndef FABRICD +/* flex-algo */ +#define F_SPFTREE_DISABLED 0x08 +#endif /* ifndef FABRICD */ __attribute__((__unused__)) static void isis_vertex_id_init(struct isis_vertex *vertex, const void *id, diff --git a/isisd/isis_sr.c b/isisd/isis_sr.c index cb330603e4..f928185ffb 100644 --- a/isisd/isis_sr.c +++ b/isisd/isis_sr.c @@ -57,7 +57,17 @@ static void sr_adj_sid_del(struct sr_adjacency *sra); static inline int sr_prefix_sid_cfg_compare(const struct sr_prefix_cfg *a, const struct sr_prefix_cfg *b) { - return prefix_cmp(&a->prefix, &b->prefix); + int ret; + + ret = prefix_cmp(&a->prefix, &b->prefix); + if (ret != 0) + return ret; + + ret = a->algorithm - b->algorithm; + if (ret != 0) + return ret; + + return 0; } DECLARE_RBTREE_UNIQ(srdb_prefix_cfg, struct sr_prefix_cfg, entry, sr_prefix_sid_cfg_compare); @@ -331,7 +341,8 @@ int isis_sr_cfg_srlb_update(struct isis_area *area, uint32_t lower_bound, * @return Newly added Prefix-SID configuration structure */ struct sr_prefix_cfg *isis_sr_cfg_prefix_add(struct isis_area *area, - const struct prefix *prefix) + const struct prefix *prefix, + uint8_t algorithm) { struct sr_prefix_cfg *pcfg; struct interface *ifp; @@ -341,6 +352,7 @@ struct sr_prefix_cfg *isis_sr_cfg_prefix_add(struct isis_area *area, pcfg = XCALLOC(MTYPE_ISIS_SR_INFO, sizeof(*pcfg)); pcfg->prefix = *prefix; pcfg->area = area; + pcfg->algorithm = algorithm; /* Pull defaults from the YANG module. */ pcfg->sid_type = yang_get_default_enum( @@ -386,11 +398,13 @@ void isis_sr_cfg_prefix_del(struct sr_prefix_cfg *pcfg) * @return Configured Prefix-SID structure if found, NULL otherwise */ struct sr_prefix_cfg *isis_sr_cfg_prefix_find(struct isis_area *area, - union prefixconstptr prefix) + union prefixconstptr prefix, + uint8_t algorithm) { struct sr_prefix_cfg pcfg = {}; prefix_copy(&pcfg.prefix, prefix.p); + pcfg.algorithm = algorithm; return srdb_prefix_cfg_find(&area->srdb.config.prefix_sids, &pcfg); } @@ -405,7 +419,7 @@ void isis_sr_prefix_cfg2subtlv(const struct sr_prefix_cfg *pcfg, bool external, struct isis_prefix_sid *psid) { /* Set SID algorithm. */ - psid->algorithm = SR_ALGORITHM_SPF; + psid->algorithm = pcfg->algorithm; /* Set SID flags. */ psid->flags = 0; @@ -917,10 +931,12 @@ static int sr_adj_ip_disabled(struct isis_adjacency *adj, int family, */ static int sr_if_new_hook(struct interface *ifp) { + struct sr_prefix_cfg *pcfgs[SR_ALGORITHM_COUNT] = {NULL}; struct isis_circuit *circuit; struct isis_area *area; struct connected *connected; struct listnode *node; + bool need_lsp_regenerate = false; /* Get corresponding circuit */ circuit = circuit_scan_by_ifp(ifp); @@ -937,18 +953,24 @@ static int sr_if_new_hook(struct interface *ifp) * configuration before receiving interface information from zebra. */ FOR_ALL_INTERFACES_ADDRESSES (ifp, connected, node) { - struct sr_prefix_cfg *pcfg; - pcfg = isis_sr_cfg_prefix_find(area, connected->address); - if (!pcfg) - continue; + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) { + pcfgs[i] = isis_sr_cfg_prefix_find( + area, connected->address, i); + + if (!pcfgs[i]) + continue; - if (sr_prefix_is_node_sid(ifp, &pcfg->prefix)) { - pcfg->node_sid = true; - lsp_regenerate_schedule(area, area->is_type, 0); + if (sr_prefix_is_node_sid(ifp, &pcfgs[i]->prefix)) { + pcfgs[i]->node_sid = true; + need_lsp_regenerate = true; + } } } + if (need_lsp_regenerate) + lsp_regenerate_schedule(area, area->is_type, 0); + return 0; } @@ -998,10 +1020,12 @@ char *sr_op2str(char *buf, size_t size, mpls_label_t label_in, * @param area IS-IS area * @param level IS-IS level */ -static void show_node(struct vty *vty, struct isis_area *area, int level) +static void show_node(struct vty *vty, struct isis_area *area, int level, + uint8_t algo) { struct isis_lsp *lsp; struct ttable *tt; + char buf[128]; vty_out(vty, " IS-IS %s SR-Nodes:\n\n", circuit_t2string(level)); @@ -1021,15 +1045,24 @@ static void show_node(struct vty *vty, struct isis_area *area, int level) cap = lsp->tlvs->router_cap; if (!cap) continue; + if (cap->algo[algo] == SR_ALGORITHM_UNSET) + continue; - ttable_add_row( - tt, "%s|%u - %u|%u - %u|%s|%u", - sysid_print(lsp->hdr.lsp_id), cap->srgb.lower_bound, - cap->srgb.lower_bound + cap->srgb.range_size - 1, - cap->srlb.lower_bound, - cap->srlb.lower_bound + cap->srlb.range_size - 1, - cap->algo[0] == SR_ALGORITHM_SPF ? "SPF" : "S-SPF", - cap->msd); + if (cap->algo[algo] == SR_ALGORITHM_SPF) + snprintf(buf, sizeof(buf), "SPF"); + else if (cap->algo[algo] == SR_ALGORITHM_STRICT_SPF) + snprintf(buf, sizeof(buf), "S-SPF"); +#ifndef FABRICD + else + snprintf(buf, sizeof(buf), "Flex-Algo %d", algo); +#endif /* ifndef FABRICD */ + + ttable_add_row(tt, "%pSY|%u - %u|%u - %u|%s|%u", + lsp->hdr.lsp_id, cap->srgb.lower_bound, + cap->srgb.lower_bound + cap->srgb.range_size - 1, + cap->srlb.lower_bound, + cap->srlb.lower_bound + cap->srlb.range_size - 1, + buf, cap->msd); } /* Dump the generated table. */ @@ -1044,15 +1077,31 @@ static void show_node(struct vty *vty, struct isis_area *area, int level) } DEFUN(show_sr_node, show_sr_node_cmd, - "show " PROTO_NAME " segment-routing node", - SHOW_STR - PROTO_HELP + "show " PROTO_NAME + " segment-routing node" +#ifndef FABRICD + " [algorithm (128-255)]" +#endif /* ifndef FABRICD */ + , + SHOW_STR PROTO_HELP "Segment-Routing\n" - "Segment-Routing node\n") + "Segment-Routing node\n" +#ifndef FABRICD + "Show Flex-algo nodes\n" + "Algorithm number\n" +#endif /* ifndef FABRICD */ +) { struct listnode *node, *inode; struct isis_area *area; + uint8_t algorithm = SR_ALGORITHM_SPF; struct isis *isis; +#ifndef FABRICD + int idx = 0; + + if (argv_find(argv, argc, "algorithm", &idx)) + algorithm = (uint8_t)strtoul(argv[idx + 1]->arg, NULL, 10); +#endif /* ifndef FABRICD */ for (ALL_LIST_ELEMENTS_RO(im->isis, inode, isis)) { for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) { @@ -1064,7 +1113,7 @@ DEFUN(show_sr_node, show_sr_node_cmd, } for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) - show_node(vty, area, level); + show_node(vty, area, level, algorithm); } } diff --git a/isisd/isis_sr.h b/isisd/isis_sr.h index 4ced5f4e83..f5f0adf241 100644 --- a/isisd/isis_sr.h +++ b/isisd/isis_sr.h @@ -61,6 +61,11 @@ struct isis_sr_psid_info { /* Indicates whether the Prefix-SID is present or not. */ bool present; + + uint8_t algorithm; + + struct list *nexthops; + struct list *nexthops_backup; }; /* Segment Routing Local Block allocation */ @@ -147,6 +152,9 @@ struct sr_prefix_cfg { /* Backpointer to IS-IS area. */ struct isis_area *area; + + /* SR Algorithm number */ + uint8_t algorithm; }; /* Per-area IS-IS Segment Routing Data Base (SRDB). */ @@ -198,11 +206,13 @@ extern int isis_sr_cfg_srgb_update(struct isis_area *area, uint32_t lower_bound, uint32_t upper_bound); extern int isis_sr_cfg_srlb_update(struct isis_area *area, uint32_t lower_bound, uint32_t upper_bound); -extern struct sr_prefix_cfg * -isis_sr_cfg_prefix_add(struct isis_area *area, const struct prefix *prefix); +extern struct sr_prefix_cfg *isis_sr_cfg_prefix_add(struct isis_area *area, + const struct prefix *prefix, + uint8_t algorithm); extern void isis_sr_cfg_prefix_del(struct sr_prefix_cfg *pcfg); extern struct sr_prefix_cfg * -isis_sr_cfg_prefix_find(struct isis_area *area, union prefixconstptr prefix); +isis_sr_cfg_prefix_find(struct isis_area *area, union prefixconstptr prefix, + uint8_t algorithm); extern void isis_sr_prefix_cfg2subtlv(const struct sr_prefix_cfg *pcfg, bool external, struct isis_prefix_sid *psid); diff --git a/isisd/isis_te.c b/isisd/isis_te.c index 71f1825629..90b53c540e 100644 --- a/isisd/isis_te.c +++ b/isisd/isis_te.c @@ -164,6 +164,154 @@ void isis_mpls_te_term(struct isis_area *area) XFREE(MTYPE_ISIS_MPLS_TE, area->mta); } +static void isis_link_params_update_asla(struct isis_circuit *circuit, + struct interface *ifp) +{ + struct isis_asla_subtlvs *asla; + struct listnode *node, *nnode; + struct isis_ext_subtlvs *ext = circuit->ext; + int i; + + if (!HAS_LINK_PARAMS(ifp)) { + list_delete_all_node(ext->aslas); + return; + } + +#ifndef FABRICD + /* RFC 8919 Application Specific Link-Attributes + * is required by flex-algo application ISIS_SABM_FLAG_X + */ + if (list_isempty(circuit->area->flex_algos->flex_algos)) + isis_tlvs_free_asla(ext, ISIS_SABM_FLAG_X); + else + isis_tlvs_find_alloc_asla(ext, ISIS_SABM_FLAG_X); +#endif /* ifndef FABRICD */ + + if (list_isempty(ext->aslas)) + return; + + for (ALL_LIST_ELEMENTS(ext->aslas, node, nnode, asla)) { + asla->legacy = circuit->area->asla_legacy_flag; + RESET_SUBTLV(asla); + + if (asla->legacy) + continue; + + /* Fulfill ASLA subTLVs from interface link parameters */ + if (IS_PARAM_SET(ifp->link_params, LP_ADM_GRP)) { + asla->admin_group = ifp->link_params->admin_grp; + SET_SUBTLV(asla, EXT_ADM_GRP); + } else + UNSET_SUBTLV(asla, EXT_ADM_GRP); + + if (IS_PARAM_SET(ifp->link_params, LP_EXTEND_ADM_GRP)) { + admin_group_copy(&asla->ext_admin_group, + &ifp->link_params->ext_admin_grp); + SET_SUBTLV(asla, EXT_EXTEND_ADM_GRP); + } else + UNSET_SUBTLV(asla, EXT_EXTEND_ADM_GRP); + + /* Send admin-group zero for better compatibility + * https://www.rfc-editor.org/rfc/rfc7308#section-2.3.2 + */ + if (circuit->area->admin_group_send_zero && + !IS_SUBTLV(asla, EXT_ADM_GRP) && + !IS_SUBTLV(asla, EXT_EXTEND_ADM_GRP)) { + asla->admin_group = 0; + SET_SUBTLV(asla, EXT_ADM_GRP); + admin_group_clear(&asla->ext_admin_group); + admin_group_allow_explicit_zero(&asla->ext_admin_group); + SET_SUBTLV(asla, EXT_EXTEND_ADM_GRP); + } + + if (IS_PARAM_SET(ifp->link_params, LP_TE_METRIC)) { + asla->te_metric = ifp->link_params->te_metric; + SET_SUBTLV(asla, EXT_TE_METRIC); + } else + UNSET_SUBTLV(asla, EXT_TE_METRIC); + + if (IS_PARAM_SET(ifp->link_params, LP_DELAY)) { + asla->delay = ifp->link_params->av_delay; + SET_SUBTLV(asla, EXT_DELAY); + } else + UNSET_SUBTLV(asla, EXT_DELAY); + + if (IS_PARAM_SET(ifp->link_params, LP_MM_DELAY)) { + asla->min_delay = ifp->link_params->min_delay; + asla->max_delay = ifp->link_params->max_delay; + SET_SUBTLV(asla, EXT_MM_DELAY); + } else { + UNSET_SUBTLV(asla, EXT_MM_DELAY); + } + + if (asla->standard_apps == ISIS_SABM_FLAG_X) + /* Flex-Algo ASLA does not need the following TE + * sub-TLVs + */ + continue; + + if (IS_PARAM_SET(ifp->link_params, LP_MAX_BW)) { + asla->max_bw = ifp->link_params->max_bw; + SET_SUBTLV(asla, EXT_MAX_BW); + } else + UNSET_SUBTLV(asla, EXT_MAX_BW); + + if (IS_PARAM_SET(ifp->link_params, LP_MAX_RSV_BW)) { + asla->max_rsv_bw = ifp->link_params->max_rsv_bw; + SET_SUBTLV(asla, EXT_MAX_RSV_BW); + } else + UNSET_SUBTLV(asla, EXT_MAX_RSV_BW); + + if (IS_PARAM_SET(ifp->link_params, LP_UNRSV_BW)) { + for (i = 0; i < MAX_CLASS_TYPE; i++) + asla->unrsv_bw[i] = + ifp->link_params->unrsv_bw[i]; + SET_SUBTLV(asla, EXT_UNRSV_BW); + } else + UNSET_SUBTLV(asla, EXT_UNRSV_BW); + + if (IS_PARAM_SET(ifp->link_params, LP_DELAY_VAR)) { + asla->delay_var = ifp->link_params->delay_var; + SET_SUBTLV(asla, EXT_DELAY_VAR); + } else + UNSET_SUBTLV(asla, EXT_DELAY_VAR); + + if (IS_PARAM_SET(ifp->link_params, LP_PKT_LOSS)) { + asla->pkt_loss = ifp->link_params->pkt_loss; + SET_SUBTLV(asla, EXT_PKT_LOSS); + } else + UNSET_SUBTLV(asla, EXT_PKT_LOSS); + + if (IS_PARAM_SET(ifp->link_params, LP_RES_BW)) { + asla->res_bw = ifp->link_params->res_bw; + SET_SUBTLV(asla, EXT_RES_BW); + } else + UNSET_SUBTLV(asla, EXT_RES_BW); + + if (IS_PARAM_SET(ifp->link_params, LP_AVA_BW)) { + asla->ava_bw = ifp->link_params->ava_bw; + SET_SUBTLV(asla, EXT_AVA_BW); + } else + UNSET_SUBTLV(asla, EXT_AVA_BW); + + if (IS_PARAM_SET(ifp->link_params, LP_USE_BW)) { + asla->use_bw = ifp->link_params->use_bw; + SET_SUBTLV(asla, EXT_USE_BW); + } else + UNSET_SUBTLV(asla, EXT_USE_BW); + } + + + for (ALL_LIST_ELEMENTS(ext->aslas, node, nnode, asla)) { + if (!asla->legacy && NO_SUBTLV(asla) && + admin_group_nb_words(&asla->ext_admin_group) == 0) + /* remove ASLA without info from the list of ASLAs to + * not send void ASLA + */ + isis_tlvs_del_asla_flex_algo(ext, asla); + } +} + /* Main initialization / update function of the MPLS TE Circuit context */ /* Call when interface TE Link parameters are modified */ void isis_link_params_update(struct isis_circuit *circuit, @@ -210,6 +358,19 @@ void isis_link_params_update(struct isis_circuit *circuit, } else UNSET_SUBTLV(ext, EXT_EXTEND_ADM_GRP); + /* Send admin-group zero for better compatibility + * https://www.rfc-editor.org/rfc/rfc7308#section-2.3.2 + */ + if (circuit->area->admin_group_send_zero && + !IS_SUBTLV(ext, EXT_ADM_GRP) && + !IS_SUBTLV(ext, EXT_EXTEND_ADM_GRP)) { + ext->adm_group = 0; + SET_SUBTLV(ext, EXT_ADM_GRP); + admin_group_clear(&ext->ext_admin_group); + admin_group_allow_explicit_zero(&ext->ext_admin_group); + SET_SUBTLV(ext, EXT_EXTEND_ADM_GRP); + } + /* If known, register local IPv4 addr from ip_addr list */ if (listcount(circuit->ip_addrs) != 0) { addr = (struct prefix_ipv4 *)listgetdata( @@ -331,6 +492,8 @@ void isis_link_params_update(struct isis_circuit *circuit, ext->status = 0; } + isis_link_params_update_asla(circuit, ifp); + return; } @@ -503,7 +666,12 @@ int isis_mpls_te_update(struct interface *ifp) isis_link_params_update(circuit, ifp); /* ... and LSP */ - if (circuit->area && IS_MPLS_TE(circuit->area->mta)) + if (circuit->area && + (IS_MPLS_TE(circuit->area->mta) +#ifndef FABRICD + || !list_isempty(circuit->area->flex_algos->flex_algos) +#endif /* ifndef FABRICD */ + )) lsp_regenerate_schedule(circuit->area, circuit->is_type, 0); rc = 0; @@ -611,7 +779,7 @@ static struct ls_vertex *lsp_to_vertex(struct ls_ted *ted, struct isis_lsp *lsp) lnode.srgb.flag = cap->srgb.flags; lnode.srgb.lower_bound = cap->srgb.lower_bound; lnode.srgb.range_size = cap->srgb.range_size; - for (int i = 0; i < SR_ALGORITHM_COUNT; i++) + for (int i = 0; i < LIB_LS_SR_ALGO_COUNT; i++) lnode.algo[i] = cap->algo[i]; } @@ -905,7 +1073,7 @@ static int lsp_to_edge_cb(const uint8_t *id, uint32_t metric, bool old_metric, struct ls_edge *edge, *dst; struct ls_attributes *attr; - te_debug(" |- Process Extended IS for %s", sysid_print(id)); + te_debug(" |- Process Extended IS for %pSY", id); /* Check parameters */ if (old_metric || !args || !tlvs) @@ -1092,7 +1260,7 @@ static int lsp_to_subnet_cb(const struct prefix *prefix, uint32_t metric, prefix_copy(&p, prefix); else { /* Remove old subnet if any before prefix adjustment */ - subnet = ls_find_subnet(args->ted, *prefix); + subnet = ls_find_subnet(args->ted, prefix); if (subnet) { if (args->export) { subnet->status = DELETE; @@ -1107,10 +1275,10 @@ static int lsp_to_subnet_cb(const struct prefix *prefix, uint32_t metric, } /* Search existing Subnet in TED ... */ - subnet = ls_find_subnet(args->ted, p); + subnet = ls_find_subnet(args->ted, &p); /* ... and create a new Subnet if not found */ if (!subnet) { - ls_pref = ls_prefix_new(vertex->node->adv, p); + ls_pref = ls_prefix_new(vertex->node->adv, &p); subnet = ls_subnet_add(args->ted, ls_pref); /* Stop processing if we are unable to create a new subnet */ if (!subnet) @@ -1195,14 +1363,14 @@ static void isis_te_parse_lsp(struct mpls_te_area *mta, struct isis_lsp *lsp) ted = mta->ted; - te_debug("ISIS-TE(%s): Parse LSP %s", lsp->area->area_tag, - sysid_print(lsp->hdr.lsp_id)); + te_debug("ISIS-TE(%s): Parse LSP %pSY", lsp->area->area_tag, + lsp->hdr.lsp_id); /* First parse LSP to obtain the corresponding Vertex */ vertex = lsp_to_vertex(ted, lsp); if (!vertex) { - zlog_warn("Unable to build Vertex from LSP %s. Abort!", - sysid_print(lsp->hdr.lsp_id)); + zlog_warn("Unable to build Vertex from LSP %pSY. Abort!", + lsp->hdr.lsp_id); return; } @@ -1266,8 +1434,8 @@ static void isis_te_delete_lsp(struct mpls_te_area *mta, struct isis_lsp *lsp) if (!IS_MPLS_TE(mta) || !mta->ted || !lsp) return; - te_debug("ISIS-TE(%s): Delete Link State TED objects from LSP %s", - lsp->area->area_tag, sysid_print(lsp->hdr.lsp_id)); + te_debug("ISIS-TE(%s): Delete Link State TED objects from LSP %pSY", + lsp->area->area_tag, lsp->hdr.lsp_id); /* Compute Link State Node ID from IS-IS sysID ... */ if (lsp->level == ISIS_LEVEL1) @@ -1851,7 +2019,7 @@ static int show_ted(struct vty *vty, struct cmd_token *argv[], int argc, return CMD_WARNING_CONFIG_FAILED; } /* Get the Subnet from the Link State Database */ - subnet = ls_find_subnet(ted, pref); + subnet = ls_find_subnet(ted, &pref); if (!subnet) { vty_out(vty, "No subnet found for ID %pFX\n", &pref); @@ -1864,7 +2032,7 @@ static int show_ted(struct vty *vty, struct cmd_token *argv[], int argc, return CMD_WARNING_CONFIG_FAILED; } /* Get the Subnet from the Link State Database */ - subnet = ls_find_subnet(ted, pref); + subnet = ls_find_subnet(ted, &pref); if (!subnet) { vty_out(vty, "No subnet found for ID %pFX\n", &pref); diff --git a/isisd/isis_tlvs.c b/isisd/isis_tlvs.c index b52a38be7f..4ad877ce0f 100644 --- a/isisd/isis_tlvs.c +++ b/isisd/isis_tlvs.c @@ -29,9 +29,15 @@ #include "isisd/isis_lsp.h" #include "isisd/isis_te.h" #include "isisd/isis_sr.h" +#include "isisd/isis_flex_algo.h" + +#define TLV_SIZE_MISMATCH(log, indent, target) \ + sbuf_push(log, indent, \ + "TLV size does not match expected size for " target "!\n") DEFINE_MTYPE_STATIC(ISISD, ISIS_TLV, "ISIS TLVs"); DEFINE_MTYPE(ISISD, ISIS_SUBTLV, "ISIS Sub-TLVs"); +DEFINE_MTYPE_STATIC(ISISD, ISIS_SUBSUBTLV, "ISIS Sub-Sub-TLVs"); DEFINE_MTYPE_STATIC(ISISD, ISIS_MT_ITEM_LIST, "ISIS MT Item Lists"); typedef int (*unpack_tlv_func)(enum isis_tlv_context context, uint8_t tlv_type, @@ -119,6 +125,7 @@ struct isis_ext_subtlvs *isis_alloc_ext_subtlvs(void) ext = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(struct isis_ext_subtlvs)); init_item_list(&ext->adj_sid); init_item_list(&ext->lan_sid); + ext->aslas = list_new(); admin_group_init(&ext->ext_admin_group); @@ -128,6 +135,8 @@ struct isis_ext_subtlvs *isis_alloc_ext_subtlvs(void) void isis_del_ext_subtlvs(struct isis_ext_subtlvs *ext) { struct isis_item *item, *next_item; + struct listnode *node, *nnode; + struct isis_asla_subtlvs *asla; if (!ext) return; @@ -142,6 +151,11 @@ void isis_del_ext_subtlvs(struct isis_ext_subtlvs *ext) XFREE(MTYPE_ISIS_SUBTLV, item); } + for (ALL_LIST_ELEMENTS(ext->aslas, node, nnode, asla)) + isis_tlvs_del_asla_flex_algo(ext, asla); + + list_delete(&ext->aslas); + admin_group_term(&ext->ext_admin_group); XFREE(MTYPE_ISIS_SUBTLV, ext); @@ -158,6 +172,8 @@ copy_item_ext_subtlvs(struct isis_ext_subtlvs *exts, uint16_t mtid) struct isis_ext_subtlvs *rv = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*rv)); struct isis_adj_sid *adj; struct isis_lan_adj_sid *lan; + struct listnode *node, *nnode; + struct isis_asla_subtlvs *new_asla, *asla; /* Copy the Extended IS main part */ memcpy(rv, exts, sizeof(struct isis_ext_subtlvs)); @@ -222,12 +238,133 @@ copy_item_ext_subtlvs(struct isis_ext_subtlvs *exts, uint16_t mtid) SET_SUBTLV(rv, EXT_LAN_ADJ_SID); } + rv->aslas = list_new(); + + for (ALL_LIST_ELEMENTS(exts->aslas, node, nnode, asla)) { + new_asla = XCALLOC(MTYPE_ISIS_SUBTLV, + sizeof(struct isis_asla_subtlvs)); + memcpy(new_asla, asla, sizeof(struct isis_asla_subtlvs)); + + new_asla->ext_admin_group.bitmap.data = NULL; + admin_group_copy(&new_asla->ext_admin_group, + &asla->ext_admin_group); + + listnode_add(rv->aslas, new_asla); + } + rv->ext_admin_group.bitmap.data = NULL; admin_group_copy(&rv->ext_admin_group, &exts->ext_admin_group); return rv; } +static void format_item_asla_subtlvs(struct isis_asla_subtlvs *asla, + struct sbuf *buf, int indent) +{ + char admin_group_buf[ADMIN_GROUP_PRINT_MAX_SIZE]; + + sbuf_push(buf, indent, "Application Specific Link Attributes:\n"); + sbuf_push(buf, indent + 2, + "L flag: %u, SA-Length: %u, UDA-Length: %u\n", asla->legacy, + asla->standard_apps_length, asla->user_def_apps_length); + sbuf_push(buf, indent + 2, "Standard Applications: 0x%02x", + asla->standard_apps); + if (asla->standard_apps) { + uint8_t bit = asla->standard_apps; + if (bit & ISIS_SABM_FLAG_R) + sbuf_push(buf, 0, " RSVP-TE"); + if (bit & ISIS_SABM_FLAG_S) + sbuf_push(buf, 0, " SR-Policy"); + if (bit & ISIS_SABM_FLAG_L) + sbuf_push(buf, 0, " Loop-Free-Alternate"); + if (bit & ISIS_SABM_FLAG_X) + sbuf_push(buf, 0, " Flex-Algo"); + } + sbuf_push(buf, 0, "\n"); + sbuf_push(buf, indent + 2, "User Defined Applications: 0x%02x\n", + asla->user_def_apps); + + if (IS_SUBTLV(asla, EXT_ADM_GRP)) { + sbuf_push(buf, indent + 2, "Admin Group: 0x%08x\n", + asla->admin_group); + sbuf_push(buf, indent + 4, "Bit positions: %s\n", + admin_group_standard_print( + admin_group_buf, + indent + 2 + strlen("Admin Group: "), + asla->admin_group)); + } + if (IS_SUBTLV(asla, EXT_EXTEND_ADM_GRP) && + admin_group_nb_words(&asla->ext_admin_group) != 0) { + sbuf_push(buf, indent + 2, "Ext Admin Group: %s\n", + admin_group_string( + admin_group_buf, ADMIN_GROUP_PRINT_MAX_SIZE, + indent + 2 + strlen("Ext Admin Group: "), + &asla->ext_admin_group)); + admin_group_print(admin_group_buf, + indent + 2 + strlen("Ext Admin Group: "), + &asla->ext_admin_group); + if (admin_group_buf[0] != '\0' && + (buf->pos + strlen(admin_group_buf) + + SBUF_DEFAULT_SIZE / 2) < buf->size) + sbuf_push(buf, indent + 4, "Bit positions: %s\n", + admin_group_buf); + } + if (IS_SUBTLV(asla, EXT_MAX_BW)) + sbuf_push(buf, indent + 2, + "Maximum Bandwidth: %g (Bytes/sec)\n", asla->max_bw); + if (IS_SUBTLV(asla, EXT_MAX_RSV_BW)) + sbuf_push(buf, indent + 2, + "Maximum Reservable Bandwidth: %g (Bytes/sec)\n", + asla->max_rsv_bw); + if (IS_SUBTLV(asla, EXT_UNRSV_BW)) { + sbuf_push(buf, indent + 2, "Unreserved Bandwidth:\n"); + for (int j = 0; j < MAX_CLASS_TYPE; j += 2) { + sbuf_push( + buf, indent + 2, + "[%d]: %g (Bytes/sec),\t[%d]: %g (Bytes/sec)\n", + j, asla->unrsv_bw[j], j + 1, + asla->unrsv_bw[j + 1]); + } + } + if (IS_SUBTLV(asla, EXT_TE_METRIC)) + sbuf_push(buf, indent + 2, "Traffic Engineering Metric: %u\n", + asla->te_metric); + /* Extended metrics */ + if (IS_SUBTLV(asla, EXT_DELAY)) + sbuf_push(buf, indent + 2, + "%s Average Link Delay: %u (micro-sec)\n", + IS_ANORMAL(asla->delay) ? "Anomalous" : "Normal", + asla->delay); + if (IS_SUBTLV(asla, EXT_MM_DELAY)) { + sbuf_push(buf, indent + 2, + "%s Min/Max Link Delay: %u / %u (micro-sec)\n", + IS_ANORMAL(asla->min_delay) ? "Anomalous" : "Normal", + asla->min_delay & TE_EXT_MASK, + asla->max_delay & TE_EXT_MASK); + } + if (IS_SUBTLV(asla, EXT_DELAY_VAR)) { + sbuf_push(buf, indent + 2, "Delay Variation: %u (micro-sec)\n", + asla->delay_var & TE_EXT_MASK); + } + if (IS_SUBTLV(asla, EXT_PKT_LOSS)) + sbuf_push(buf, indent + 2, "%s Link Packet Loss: %g (%%)\n", + IS_ANORMAL(asla->pkt_loss) ? "Anomalous" : "Normal", + (float)((asla->pkt_loss & TE_EXT_MASK) * + LOSS_PRECISION)); + if (IS_SUBTLV(asla, EXT_RES_BW)) + sbuf_push(buf, indent + 2, + "Unidir. Residual Bandwidth: %g (Bytes/sec)\n", + asla->res_bw); + if (IS_SUBTLV(asla, EXT_AVA_BW)) + sbuf_push(buf, indent + 2, + "Unidir. Available Bandwidth: %g (Bytes/sec)\n", + asla->ava_bw); + if (IS_SUBTLV(asla, EXT_USE_BW)) + sbuf_push(buf, indent + 2, + "Unidir. Utilized Bandwidth: %g (Bytes/sec)\n", + asla->use_bw); +} + /* mtid parameter is used to manage multi-topology i.e. IPv4 / IPv6 */ static void format_item_ext_subtlvs(struct isis_ext_subtlvs *exts, struct sbuf *buf, struct json_object *json, @@ -236,6 +373,8 @@ static void format_item_ext_subtlvs(struct isis_ext_subtlvs *exts, char admin_group_buf[ADMIN_GROUP_PRINT_MAX_SIZE]; char aux_buf[255]; char cnt_buf[255]; + struct isis_asla_subtlvs *asla; + struct listnode *node; /* Standard metrics */ if (IS_SUBTLV(exts, EXT_ADM_GRP)) { @@ -244,7 +383,7 @@ static void format_item_ext_subtlvs(struct isis_ext_subtlvs *exts, exts->adm_group); json_object_string_add(json, "adm-group", aux_buf); } else { - sbuf_push(buf, indent, "Administrative Group: 0x%x\n", + sbuf_push(buf, indent, "Admin Group: 0x%08x\n", exts->adm_group); sbuf_push(buf, indent + 2, "Bit positions: %s\n", admin_group_standard_print( @@ -661,7 +800,7 @@ static void format_item_ext_subtlvs(struct isis_ext_subtlvs *exts, sbuf_push( buf, indent, "Lan-Adjacency-SID: %u, Weight: %hhu, Flags: F:%c B:%c, V:%c, L:%c, S:%c, P:%c\n" - " Neighbor-ID: %s\n", + " Neighbor-ID: %pSY\n", lan->sid, lan->weight, lan->flags & EXT_SUBTLV_LINK_ADJ_SID_FFLG ? '1' @@ -681,9 +820,11 @@ static void format_item_ext_subtlvs(struct isis_ext_subtlvs *exts, lan->flags & EXT_SUBTLV_LINK_ADJ_SID_PFLG ? '1' : '0', - isis_format_id(lan->neighbor_id, 6)); + lan->neighbor_id); } } + for (ALL_LIST_ELEMENTS_RO(exts->aslas, node, asla)) + format_item_asla_subtlvs(asla, buf, indent); } static void free_item_ext_subtlvs(struct isis_ext_subtlvs *exts) @@ -691,10 +832,124 @@ static void free_item_ext_subtlvs(struct isis_ext_subtlvs *exts) isis_del_ext_subtlvs(exts); } +static int pack_item_ext_subtlv_asla(struct isis_asla_subtlvs *asla, + struct stream *s, size_t *min_len) +{ + size_t subtlv_len; + size_t subtlv_len_pos; + + /* Sub TLV header */ + stream_putc(s, ISIS_SUBTLV_ASLA); + + subtlv_len_pos = stream_get_endp(s); + stream_putc(s, 0); /* length will be filled later */ + + /* SABM Flag/Length */ + if (asla->legacy) + stream_putc(s, ASLA_LEGACY_FLAG | asla->standard_apps_length); + else + stream_putc(s, asla->standard_apps_length); + stream_putc(s, asla->user_def_apps_length); /* UDABM Flag/Length */ + stream_putc(s, asla->standard_apps); + stream_putc(s, asla->user_def_apps); + + /* Administrative Group */ + if (IS_SUBTLV(asla, EXT_ADM_GRP)) { + stream_putc(s, ISIS_SUBTLV_ADMIN_GRP); + stream_putc(s, ISIS_SUBTLV_DEF_SIZE); + stream_putl(s, asla->admin_group); + } + + /* Extended Administrative Group */ + if (IS_SUBTLV(asla, EXT_EXTEND_ADM_GRP) && + admin_group_nb_words(&asla->ext_admin_group) != 0) { + size_t ag_length; + size_t ag_length_pos; + struct admin_group *ag; + + stream_putc(s, ISIS_SUBTLV_EXT_ADMIN_GRP); + ag_length_pos = stream_get_endp(s); + stream_putc(s, 0); /* length will be filled later*/ + + ag = &asla->ext_admin_group; + for (size_t i = 0; i < admin_group_nb_words(ag); i++) + stream_putl(s, ag->bitmap.data[i]); + + ag_length = stream_get_endp(s) - ag_length_pos - 1; + stream_putc_at(s, ag_length_pos, ag_length); + } + + if (IS_SUBTLV(asla, EXT_MAX_BW)) { + stream_putc(s, ISIS_SUBTLV_MAX_BW); + stream_putc(s, ISIS_SUBTLV_DEF_SIZE); + stream_putf(s, asla->max_bw); + } + if (IS_SUBTLV(asla, EXT_MAX_RSV_BW)) { + stream_putc(s, ISIS_SUBTLV_MAX_RSV_BW); + stream_putc(s, ISIS_SUBTLV_DEF_SIZE); + stream_putf(s, asla->max_rsv_bw); + } + if (IS_SUBTLV(asla, EXT_UNRSV_BW)) { + stream_putc(s, ISIS_SUBTLV_UNRSV_BW); + stream_putc(s, ISIS_SUBTLV_UNRSV_BW_SIZE); + for (int j = 0; j < MAX_CLASS_TYPE; j++) + stream_putf(s, asla->unrsv_bw[j]); + } + if (IS_SUBTLV(asla, EXT_TE_METRIC)) { + stream_putc(s, ISIS_SUBTLV_TE_METRIC); + stream_putc(s, ISIS_SUBTLV_TE_METRIC_SIZE); + stream_put3(s, asla->te_metric); + } + if (IS_SUBTLV(asla, EXT_DELAY)) { + stream_putc(s, ISIS_SUBTLV_AV_DELAY); + stream_putc(s, ISIS_SUBTLV_DEF_SIZE); + stream_putl(s, asla->delay); + } + if (IS_SUBTLV(asla, EXT_MM_DELAY)) { + stream_putc(s, ISIS_SUBTLV_MM_DELAY); + stream_putc(s, ISIS_SUBTLV_MM_DELAY_SIZE); + stream_putl(s, asla->min_delay); + stream_putl(s, asla->max_delay); + } + if (IS_SUBTLV(asla, EXT_DELAY_VAR)) { + stream_putc(s, ISIS_SUBTLV_DELAY_VAR); + stream_putc(s, ISIS_SUBTLV_DEF_SIZE); + stream_putl(s, asla->delay_var); + } + if (IS_SUBTLV(asla, EXT_PKT_LOSS)) { + stream_putc(s, ISIS_SUBTLV_PKT_LOSS); + stream_putc(s, ISIS_SUBTLV_DEF_SIZE); + stream_putl(s, asla->pkt_loss); + } + if (IS_SUBTLV(asla, EXT_RES_BW)) { + stream_putc(s, ISIS_SUBTLV_RES_BW); + stream_putc(s, ISIS_SUBTLV_DEF_SIZE); + stream_putf(s, asla->res_bw); + } + if (IS_SUBTLV(asla, EXT_AVA_BW)) { + stream_putc(s, ISIS_SUBTLV_AVA_BW); + stream_putc(s, ISIS_SUBTLV_DEF_SIZE); + stream_putf(s, asla->ava_bw); + } + if (IS_SUBTLV(asla, EXT_USE_BW)) { + stream_putc(s, ISIS_SUBTLV_USE_BW); + stream_putc(s, ISIS_SUBTLV_DEF_SIZE); + stream_putf(s, asla->use_bw); + } + + subtlv_len = stream_get_endp(s) - subtlv_len_pos - 1; + stream_putc_at(s, subtlv_len_pos, subtlv_len); + + return 0; +} + static int pack_item_ext_subtlvs(struct isis_ext_subtlvs *exts, struct stream *s, size_t *min_len) { + struct isis_asla_subtlvs *asla; + struct listnode *node; uint8_t size; + int ret; if (STREAM_WRITEABLE(s) < ISIS_SUBTLV_MAX_SIZE) { *min_len = ISIS_SUBTLV_MAX_SIZE; @@ -858,6 +1113,219 @@ static int pack_item_ext_subtlvs(struct isis_ext_subtlvs *exts, } } + for (ALL_LIST_ELEMENTS_RO(exts->aslas, node, asla)) { + ret = pack_item_ext_subtlv_asla(asla, s, min_len); + if (ret < 0) + return ret; + } + + return 0; +} + +static int unpack_item_ext_subtlv_asla(uint16_t mtid, uint8_t subtlv_len, + struct stream *s, struct sbuf *log, + int indent, + struct isis_ext_subtlvs *exts) +{ + /* Standard App Identifier Bit Flags/Length */ + uint8_t sabm_flag_len; + /* User-defined App Identifier Bit Flags/Length */ + uint8_t uabm_flag_len; + uint8_t sabm[ASLA_APP_IDENTIFIER_BIT_LENGTH] = {0}; + uint8_t uabm[ASLA_APP_IDENTIFIER_BIT_LENGTH] = {0}; + uint8_t readable; + uint8_t subsubtlv_type; + uint8_t subsubtlv_len; + size_t nb_groups; + struct isis_asla_subtlvs *asla; + + if (subtlv_len < ISIS_SUBSUBTLV_HDR_SIZE) { + TLV_SIZE_MISMATCH(log, indent, "ASLA"); + return -1; + } + + + asla = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*asla)); + + admin_group_init(&asla->ext_admin_group); + + + sabm_flag_len = stream_getc(s); + uabm_flag_len = stream_getc(s); + asla->legacy = CHECK_FLAG(sabm_flag_len, ASLA_LEGACY_FLAG); + asla->standard_apps_length = ASLA_APPS_LENGTH_MASK & sabm_flag_len; + asla->user_def_apps_length = ASLA_APPS_LENGTH_MASK & uabm_flag_len; + + for (int i = 0; i < asla->standard_apps_length; i++) + sabm[i] = stream_getc(s); + for (int i = 0; i < asla->user_def_apps_length; i++) + uabm[i] = stream_getc(s); + + asla->standard_apps = sabm[0]; + asla->user_def_apps = uabm[0]; + + readable = subtlv_len - 4; + while (readable > 0) { + if (readable < ISIS_SUBSUBTLV_HDR_SIZE) { + TLV_SIZE_MISMATCH(log, indent, "ASLA Sub TLV"); + return -1; + } + + subsubtlv_type = stream_getc(s); + subsubtlv_len = stream_getc(s); + readable -= ISIS_SUBSUBTLV_HDR_SIZE; + + + switch (subsubtlv_type) { + case ISIS_SUBTLV_ADMIN_GRP: + if (subsubtlv_len != ISIS_SUBTLV_DEF_SIZE) { + TLV_SIZE_MISMATCH(log, indent, + "ASLA Adm Group"); + stream_forward_getp(s, subsubtlv_len); + } else { + asla->admin_group = stream_getl(s); + SET_SUBTLV(asla, EXT_ADM_GRP); + } + break; + + case ISIS_SUBTLV_EXT_ADMIN_GRP: + nb_groups = subsubtlv_len / sizeof(uint32_t); + for (size_t i = 0; i < nb_groups; i++) { + uint32_t val = stream_getl(s); + + admin_group_bulk_set(&asla->ext_admin_group, + val, i); + } + SET_SUBTLV(asla, EXT_EXTEND_ADM_GRP); + break; + case ISIS_SUBTLV_MAX_BW: + if (subsubtlv_len != ISIS_SUBTLV_DEF_SIZE) { + TLV_SIZE_MISMATCH(log, indent, + "Maximum Bandwidth"); + stream_forward_getp(s, subsubtlv_len); + } else { + asla->max_bw = stream_getf(s); + SET_SUBTLV(asla, EXT_MAX_BW); + } + break; + case ISIS_SUBTLV_MAX_RSV_BW: + if (subsubtlv_len != ISIS_SUBTLV_DEF_SIZE) { + TLV_SIZE_MISMATCH( + log, indent, + "Maximum Reservable Bandwidth"); + stream_forward_getp(s, subsubtlv_len); + } else { + asla->max_rsv_bw = stream_getf(s); + SET_SUBTLV(asla, EXT_MAX_RSV_BW); + } + break; + case ISIS_SUBTLV_UNRSV_BW: + if (subsubtlv_len != ISIS_SUBTLV_UNRSV_BW_SIZE) { + TLV_SIZE_MISMATCH(log, indent, + "Unreserved Bandwidth"); + stream_forward_getp(s, subsubtlv_len); + } else { + for (int i = 0; i < MAX_CLASS_TYPE; i++) + asla->unrsv_bw[i] = stream_getf(s); + SET_SUBTLV(asla, EXT_UNRSV_BW); + } + break; + case ISIS_SUBTLV_TE_METRIC: + if (subsubtlv_len != ISIS_SUBTLV_TE_METRIC_SIZE) { + TLV_SIZE_MISMATCH(log, indent, + "Traffic Engineering Metric"); + stream_forward_getp(s, subsubtlv_len); + } else { + asla->te_metric = stream_get3(s); + SET_SUBTLV(asla, EXT_TE_METRIC); + } + break; + /* Extended Metrics as defined in RFC 7810 */ + case ISIS_SUBTLV_AV_DELAY: + if (subsubtlv_len != ISIS_SUBTLV_DEF_SIZE) { + TLV_SIZE_MISMATCH(log, indent, + "Average Link Delay"); + stream_forward_getp(s, subsubtlv_len); + } else { + asla->delay = stream_getl(s); + SET_SUBTLV(asla, EXT_DELAY); + } + break; + case ISIS_SUBTLV_MM_DELAY: + if (subsubtlv_len != ISIS_SUBTLV_MM_DELAY_SIZE) { + TLV_SIZE_MISMATCH(log, indent, + "Min/Max Link Delay"); + stream_forward_getp(s, subsubtlv_len); + } else { + asla->min_delay = stream_getl(s); + asla->max_delay = stream_getl(s); + SET_SUBTLV(asla, EXT_MM_DELAY); + } + break; + case ISIS_SUBTLV_DELAY_VAR: + if (subsubtlv_len != ISIS_SUBTLV_DEF_SIZE) { + TLV_SIZE_MISMATCH(log, indent, + "Delay Variation"); + stream_forward_getp(s, subsubtlv_len); + } else { + asla->delay_var = stream_getl(s); + SET_SUBTLV(asla, EXT_DELAY_VAR); + } + break; + case ISIS_SUBTLV_PKT_LOSS: + if (subsubtlv_len != ISIS_SUBTLV_DEF_SIZE) { + TLV_SIZE_MISMATCH(log, indent, + "Link Packet Loss"); + stream_forward_getp(s, subsubtlv_len); + } else { + asla->pkt_loss = stream_getl(s); + SET_SUBTLV(asla, EXT_PKT_LOSS); + } + break; + case ISIS_SUBTLV_RES_BW: + if (subsubtlv_len != ISIS_SUBTLV_DEF_SIZE) { + TLV_SIZE_MISMATCH( + log, indent, + "Unidirectional Residual Bandwidth"); + stream_forward_getp(s, subsubtlv_len); + } else { + asla->res_bw = stream_getf(s); + SET_SUBTLV(asla, EXT_RES_BW); + } + break; + case ISIS_SUBTLV_AVA_BW: + if (subsubtlv_len != ISIS_SUBTLV_DEF_SIZE) { + TLV_SIZE_MISMATCH( + log, indent, + "Unidirectional Available Bandwidth"); + stream_forward_getp(s, subsubtlv_len); + } else { + asla->ava_bw = stream_getf(s); + SET_SUBTLV(asla, EXT_AVA_BW); + } + break; + case ISIS_SUBTLV_USE_BW: + if (subsubtlv_len != ISIS_SUBTLV_DEF_SIZE) { + TLV_SIZE_MISMATCH( + log, indent, + "Unidirectional Utilized Bandwidth"); + stream_forward_getp(s, subsubtlv_len); + } else { + asla->use_bw = stream_getf(s); + SET_SUBTLV(asla, EXT_USE_BW); + } + break; + default: + zlog_debug("unknown (t,l)=(%u,%u)", subsubtlv_type, + subsubtlv_len); + stream_forward_getp(s, subsubtlv_len); + break; + } + readable -= subsubtlv_len; + } + + listnode_add(exts->aslas, asla); + return 0; } @@ -896,8 +1364,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, /* Standard Metric as defined in RFC5305 */ case ISIS_SUBTLV_ADMIN_GRP: if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) { - sbuf_push(log, indent, - "TLV size does not match expected size for Administrative Group!\n"); + TLV_SIZE_MISMATCH(log, indent, + "Administrative Group"); stream_forward_getp(s, subtlv_len); } else { exts->adm_group = stream_getl(s); @@ -915,8 +1383,7 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, break; case ISIS_SUBTLV_LLRI: if (subtlv_len != ISIS_SUBTLV_LLRI_SIZE) { - sbuf_push(log, indent, - "TLV size does not match expected size for Link ID!\n"); + TLV_SIZE_MISMATCH(log, indent, "Link ID"); stream_forward_getp(s, subtlv_len); } else { exts->local_llri = stream_getl(s); @@ -926,8 +1393,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, break; case ISIS_SUBTLV_LOCAL_IPADDR: if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) { - sbuf_push(log, indent, - "TLV size does not match expected size for Local IP address!\n"); + TLV_SIZE_MISMATCH(log, indent, + "Local IP address"); stream_forward_getp(s, subtlv_len); } else { stream_get(&exts->local_addr.s_addr, s, 4); @@ -936,8 +1403,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, break; case ISIS_SUBTLV_RMT_IPADDR: if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) { - sbuf_push(log, indent, - "TLV size does not match expected size for Remote IP address!\n"); + TLV_SIZE_MISMATCH(log, indent, + "Remote IP address"); stream_forward_getp(s, subtlv_len); } else { stream_get(&exts->neigh_addr.s_addr, s, 4); @@ -946,8 +1413,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, break; case ISIS_SUBTLV_LOCAL_IPADDR6: if (subtlv_len != ISIS_SUBTLV_IPV6_ADDR_SIZE) { - sbuf_push(log, indent, - "TLV size does not match expected size for Local IPv6 address!\n"); + TLV_SIZE_MISMATCH(log, indent, + "Local IPv6 address"); stream_forward_getp(s, subtlv_len); } else { stream_get(&exts->local_addr6, s, 16); @@ -956,8 +1423,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, break; case ISIS_SUBTLV_RMT_IPADDR6: if (subtlv_len != ISIS_SUBTLV_IPV6_ADDR_SIZE) { - sbuf_push(log, indent, - "TLV size does not match expected size for Remote IPv6 address!\n"); + TLV_SIZE_MISMATCH(log, indent, + "Remote IPv6 address"); stream_forward_getp(s, subtlv_len); } else { stream_get(&exts->neigh_addr6, s, 16); @@ -966,8 +1433,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, break; case ISIS_SUBTLV_MAX_BW: if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) { - sbuf_push(log, indent, - "TLV size does not match expected size for Maximum Bandwidth!\n"); + TLV_SIZE_MISMATCH(log, indent, + "Maximum Bandwidth"); stream_forward_getp(s, subtlv_len); } else { exts->max_bw = stream_getf(s); @@ -976,8 +1443,9 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, break; case ISIS_SUBTLV_MAX_RSV_BW: if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) { - sbuf_push(log, indent, - "TLV size does not match expected size for Maximum Reservable Bandwidth!\n"); + TLV_SIZE_MISMATCH( + log, indent, + "Maximum Reservable Bandwidth"); stream_forward_getp(s, subtlv_len); } else { exts->max_rsv_bw = stream_getf(s); @@ -986,8 +1454,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, break; case ISIS_SUBTLV_UNRSV_BW: if (subtlv_len != ISIS_SUBTLV_UNRSV_BW_SIZE) { - sbuf_push(log, indent, - "TLV size does not match expected size for Unreserved Bandwidth!\n"); + TLV_SIZE_MISMATCH(log, indent, + "Unreserved Bandwidth"); stream_forward_getp(s, subtlv_len); } else { for (int i = 0; i < MAX_CLASS_TYPE; i++) @@ -997,8 +1465,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, break; case ISIS_SUBTLV_TE_METRIC: if (subtlv_len != ISIS_SUBTLV_TE_METRIC_SIZE) { - sbuf_push(log, indent, - "TLV size does not match expected size for Traffic Engineering Metric!\n"); + TLV_SIZE_MISMATCH(log, indent, + "Traffic Engineering Metric"); stream_forward_getp(s, subtlv_len); } else { exts->te_metric = stream_get3(s); @@ -1007,8 +1475,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, break; case ISIS_SUBTLV_RAS: if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) { - sbuf_push(log, indent, - "TLV size does not match expected size for Remote AS number!\n"); + TLV_SIZE_MISMATCH(log, indent, + "Remote AS number"); stream_forward_getp(s, subtlv_len); } else { exts->remote_as = stream_getl(s); @@ -1017,8 +1485,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, break; case ISIS_SUBTLV_RIP: if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) { - sbuf_push(log, indent, - "TLV size does not match expected size for Remote ASBR IP Address!\n"); + TLV_SIZE_MISMATCH(log, indent, + "Remote ASBR IP Address"); stream_forward_getp(s, subtlv_len); } else { stream_get(&exts->remote_ip.s_addr, s, 4); @@ -1028,8 +1496,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, /* Extended Metrics as defined in RFC 7810 */ case ISIS_SUBTLV_AV_DELAY: if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) { - sbuf_push(log, indent, - "TLV size does not match expected size for Average Link Delay!\n"); + TLV_SIZE_MISMATCH(log, indent, + "Average Link Delay"); stream_forward_getp(s, subtlv_len); } else { exts->delay = stream_getl(s); @@ -1038,8 +1506,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, break; case ISIS_SUBTLV_MM_DELAY: if (subtlv_len != ISIS_SUBTLV_MM_DELAY_SIZE) { - sbuf_push(log, indent, - "TLV size does not match expected size for Min/Max Link Delay!\n"); + TLV_SIZE_MISMATCH(log, indent, + "Min/Max Link Delay"); stream_forward_getp(s, subtlv_len); } else { exts->min_delay = stream_getl(s); @@ -1049,8 +1517,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, break; case ISIS_SUBTLV_DELAY_VAR: if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) { - sbuf_push(log, indent, - "TLV size does not match expected size for Delay Variation!\n"); + TLV_SIZE_MISMATCH(log, indent, + "Delay Variation"); stream_forward_getp(s, subtlv_len); } else { exts->delay_var = stream_getl(s); @@ -1059,8 +1527,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, break; case ISIS_SUBTLV_PKT_LOSS: if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) { - sbuf_push(log, indent, - "TLV size does not match expected size for Link Packet Loss!\n"); + TLV_SIZE_MISMATCH(log, indent, + "Link Packet Loss"); stream_forward_getp(s, subtlv_len); } else { exts->pkt_loss = stream_getl(s); @@ -1069,8 +1537,9 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, break; case ISIS_SUBTLV_RES_BW: if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) { - sbuf_push(log, indent, - "TLV size does not match expected size for Unidirectional Residual Bandwidth!\n"); + TLV_SIZE_MISMATCH( + log, indent, + "Unidirectional Residual Bandwidth"); stream_forward_getp(s, subtlv_len); } else { exts->res_bw = stream_getf(s); @@ -1079,8 +1548,9 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, break; case ISIS_SUBTLV_AVA_BW: if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) { - sbuf_push(log, indent, - "TLV size does not match expected size for Unidirectional Available Bandwidth!\n"); + TLV_SIZE_MISMATCH( + log, indent, + "Unidirectional Available Bandwidth"); stream_forward_getp(s, subtlv_len); } else { exts->ava_bw = stream_getf(s); @@ -1089,8 +1559,9 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, break; case ISIS_SUBTLV_USE_BW: if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) { - sbuf_push(log, indent, - "TLV size does not match expected size for Unidirectional Utilized Bandwidth!\n"); + TLV_SIZE_MISMATCH( + log, indent, + "Unidirectional Utilized Bandwidth"); stream_forward_getp(s, subtlv_len); } else { exts->use_bw = stream_getf(s); @@ -1101,8 +1572,7 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, case ISIS_SUBTLV_ADJ_SID: if (subtlv_len != ISIS_SUBTLV_ADJ_SID_SIZE && subtlv_len != ISIS_SUBTLV_ADJ_SID_SIZE + 1) { - sbuf_push(log, indent, - "TLV size does not match expected size for Adjacency SID!\n"); + TLV_SIZE_MISMATCH(log, indent, "Adjacency SID"); stream_forward_getp(s, subtlv_len); } else { struct isis_adj_sid *adj; @@ -1113,9 +1583,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, adj->weight = stream_getc(s); if (adj->flags & EXT_SUBTLV_LINK_ADJ_SID_VFLG && subtlv_len != ISIS_SUBTLV_ADJ_SID_SIZE) { - sbuf_push( - log, indent, - "TLV size does not match expected size for Adjacency SID!\n"); + TLV_SIZE_MISMATCH(log, indent, + "Adjacency SID"); stream_forward_getp(s, subtlv_len - 2); XFREE(MTYPE_ISIS_SUBTLV, adj); break; @@ -1125,9 +1594,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, && subtlv_len != ISIS_SUBTLV_ADJ_SID_SIZE + 1) { - sbuf_push( - log, indent, - "TLV size does not match expected size for Adjacency SID!\n"); + TLV_SIZE_MISMATCH(log, indent, + "Adjacency SID"); stream_forward_getp(s, subtlv_len - 2); XFREE(MTYPE_ISIS_SUBTLV, adj); break; @@ -1152,8 +1620,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, case ISIS_SUBTLV_LAN_ADJ_SID: if (subtlv_len != ISIS_SUBTLV_LAN_ADJ_SID_SIZE && subtlv_len != ISIS_SUBTLV_LAN_ADJ_SID_SIZE + 1) { - sbuf_push(log, indent, - "TLV size does not match expected size for LAN-Adjacency SID!\n"); + TLV_SIZE_MISMATCH(log, indent, + "LAN-Adjacency SID"); stream_forward_getp(s, subtlv_len); } else { struct isis_lan_adj_sid *lan; @@ -1168,9 +1636,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, if (lan->flags & EXT_SUBTLV_LINK_ADJ_SID_VFLG && subtlv_len != ISIS_SUBTLV_LAN_ADJ_SID_SIZE) { - sbuf_push( - log, indent, - "TLV size does not match expected size for LAN-Adjacency SID!\n"); + TLV_SIZE_MISMATCH(log, indent, + "LAN-Adjacency SID"); stream_forward_getp( s, subtlv_len - 2 - ISIS_SYS_ID_LEN); @@ -1182,9 +1649,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, && subtlv_len != ISIS_SUBTLV_LAN_ADJ_SID_SIZE + 1) { - sbuf_push( - log, indent, - "TLV size does not match expected size for LAN-Adjacency SID!\n"); + TLV_SIZE_MISMATCH(log, indent, + "LAN-Adjacency SID"); stream_forward_getp( s, subtlv_len - 2 - ISIS_SYS_ID_LEN); @@ -1207,6 +1673,13 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, SET_SUBTLV(exts, EXT_LAN_ADJ_SID); } break; + case ISIS_SUBTLV_ASLA: + if (unpack_item_ext_subtlv_asla(mtid, subtlv_len, s, + log, indent, + exts) < 0) { + sbuf_push(log, indent, "TLV parse error"); + } + break; default: /* Skip unknown TLV */ stream_forward_getp(s, subtlv_len); @@ -1590,14 +2063,14 @@ static void format_item_area_address(uint16_t mtid, struct isis_item *i, int indent) { struct isis_area_address *addr = (struct isis_area_address *)i; + struct iso_address iso_addr; - if (json) { - json_object_string_add(json, "area-addr", - isonet_print(addr->addr, addr->len)); - } else { - sbuf_push(buf, indent, "Area Address: %s\n", - isonet_print(addr->addr, addr->len)); - } + memcpy(iso_addr.area_addr, addr->addr, ISO_ADDR_SIZE); + iso_addr.addr_len = addr->len; + if (json) + json_object_string_addf(json, "area-addr", "%pIS", &iso_addr); + else + sbuf_push(buf, indent, "Area Address: %pIS\n", &iso_addr); } static void free_item_area_address(struct isis_item *i) @@ -1678,17 +2151,18 @@ static void format_item_oldstyle_reach(uint16_t mtid, struct isis_item *i, struct json_object *json, int indent) { struct isis_oldstyle_reach *r = (struct isis_oldstyle_reach *)i; + char sys_id[ISO_SYSID_STRLEN]; + snprintfrr(sys_id, ISO_SYSID_STRLEN, "%pPN", r->id); if (json) { struct json_object *old_json; old_json = json_object_new_object(); json_object_object_add(json, "old-reach-style", old_json); - json_object_string_add(old_json, "is-reach", - isis_format_id(r->id, 7)); + json_object_string_add(old_json, "is-reach", sys_id); json_object_int_add(old_json, "metric", r->metric); } else sbuf_push(buf, indent, "IS Reachability: %s (Metric: %hhu)\n", - isis_format_id(r->id, 7), r->metric); + sys_id, r->metric); } static void free_item_oldstyle_reach(struct isis_item *i) @@ -1760,13 +2234,13 @@ static void format_item_lan_neighbor(uint16_t mtid, struct isis_item *i, int indent) { struct isis_lan_neighbor *n = (struct isis_lan_neighbor *)i; + char sys_id[ISO_SYSID_STRLEN]; - if (json) { - json_object_string_add(json, "lan-neighbor", - isis_format_id(n->mac, 6)); - } else - sbuf_push(buf, indent, "LAN Neighbor: %s\n", - isis_format_id(n->mac, 6)); + snprintfrr(sys_id, ISO_SYSID_STRLEN, "%pSY", n->mac); + if (json) + json_object_string_add(json, "lan-neighbor", sys_id); + else + sbuf_push(buf, indent, "LAN Neighbor: %s\n", sys_id); } static void free_item_lan_neighbor(struct isis_item *i) @@ -1831,23 +2305,25 @@ static void format_item_lsp_entry(uint16_t mtid, struct isis_item *i, int indent) { struct isis_lsp_entry *e = (struct isis_lsp_entry *)i; + char sys_id[ISO_SYSID_STRLEN]; + snprintfrr(sys_id, ISO_SYSID_STRLEN, "%pLS", e->id); if (json) { char buf[255]; struct json_object *lsp_json; lsp_json = json_object_new_object(); json_object_object_add(json, "lsp-entry", lsp_json); - json_object_string_add(lsp_json, "id", isis_format_id(e->id, 8)); + json_object_string_add(lsp_json, "id", sys_id); snprintfrr(buf,sizeof(buf),"0x%08x",e->seqno); json_object_string_add(lsp_json, "seq", buf); snprintfrr(buf,sizeof(buf),"0x%04hx",e->checksum); json_object_string_add(lsp_json, "chksum", buf); json_object_int_add(lsp_json, "lifetime", e->checksum); } else - sbuf_push(buf, indent, - "LSP Entry: %s, seq 0x%08x, cksum 0x%04hx, lifetime %hus\n", - isis_format_id(e->id, 8), e->seqno, e->checksum, - e->rem_lifetime); + sbuf_push( + buf, indent, + "LSP Entry: %s, seq 0x%08x, cksum 0x%04hx, lifetime %hus\n", + sys_id, e->seqno, e->checksum, e->rem_lifetime); } static void free_item_lsp_entry(struct isis_item *i) @@ -1919,7 +2395,9 @@ static void format_item_extended_reach(uint16_t mtid, struct isis_item *i, struct json_object *json, int indent) { struct isis_extended_reach *r = (struct isis_extended_reach *)i; + char sys_id[ISO_SYSID_STRLEN]; + snprintfrr(sys_id, ISO_SYSID_STRLEN, "%pPN", r->id); if (json) { struct json_object *reach_json; reach_json = json_object_new_object(); @@ -1927,8 +2405,7 @@ static void format_item_extended_reach(uint16_t mtid, struct isis_item *i, json_object_string_add( reach_json, "mt-id", (mtid == ISIS_MT_IPV4_UNICAST) ? "Extended" : "MT"); - json_object_string_add(reach_json, "id", - isis_format_id(r->id, 7)); + json_object_string_add(reach_json, "id", sys_id); json_object_int_add(reach_json, "metric", r->metric); if (mtid != ISIS_MT_IPV4_UNICAST) json_object_string_add(reach_json, "mt-name", @@ -1940,7 +2417,7 @@ static void format_item_extended_reach(uint16_t mtid, struct isis_item *i, } else { sbuf_push(buf, indent, "%s Reachability: %s (Metric: %u)", (mtid == ISIS_MT_IPV4_UNICAST) ? "Extended" : "MT", - isis_format_id(r->id, 7), r->metric); + sys_id, r->metric); if (mtid != ISIS_MT_IPV4_UNICAST) sbuf_push(buf, 0, " %s", isis_mtid2str(mtid)); sbuf_push(buf, 0, "\n"); @@ -3125,9 +3602,12 @@ static void format_tlv_threeway_adj(const struct isis_threeway_adj *threeway_adj, struct sbuf *buf, struct json_object *json, int indent) { + char sys_id[ISO_SYSID_STRLEN]; + if (!threeway_adj) return; + snprintfrr(sys_id, ISO_SYSID_STRLEN, "%pSY", threeway_adj->neighbor_id); if (json) { struct json_object *three_json; three_json = json_object_new_object(); @@ -3140,9 +3620,7 @@ format_tlv_threeway_adj(const struct isis_threeway_adj *threeway_adj, threeway_adj->local_circuit_id); if (!threeway_adj->neighbor_set) return; - json_object_string_add( - three_json, "neigh-system-id", - isis_format_id(threeway_adj->neighbor_id, 6)); + json_object_string_add(three_json, "neigh-system-id", sys_id); json_object_int_add(three_json, "neigh-ext-circuit-id", threeway_adj->neighbor_circuit_id); } else { @@ -3155,8 +3633,7 @@ format_tlv_threeway_adj(const struct isis_threeway_adj *threeway_adj, if (!threeway_adj->neighbor_set) return; - sbuf_push(buf, indent, " Neighbor System ID: %s\n", - isis_format_id(threeway_adj->neighbor_id, 6)); + sbuf_push(buf, indent, " Neighbor System ID: %s\n", sys_id); sbuf_push(buf, indent, " Neighbor Extended Circuit ID: %u\n", threeway_adj->neighbor_circuit_id); } @@ -3437,6 +3914,39 @@ static struct isis_router_cap *copy_tlv_router_cap( memcpy(rv, router_cap, sizeof(*rv)); +#ifndef FABRICD + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) { + struct isis_router_cap_fad *sc_fad; + struct isis_router_cap_fad *rv_fad; + + sc_fad = router_cap->fads[i]; + if (!sc_fad) + continue; + rv_fad = XMALLOC(MTYPE_ISIS_TLV, + sizeof(struct isis_router_cap_fad)); + *rv_fad = *sc_fad; + rv_fad->fad.admin_group_exclude_any.bitmap.data = NULL; + rv_fad->fad.admin_group_include_any.bitmap.data = NULL; + rv_fad->fad.admin_group_include_all.bitmap.data = NULL; + + assert(bf_is_inited( + sc_fad->fad.admin_group_exclude_any.bitmap)); + assert(bf_is_inited( + sc_fad->fad.admin_group_include_any.bitmap)); + assert(bf_is_inited( + sc_fad->fad.admin_group_include_all.bitmap)); + + admin_group_copy(&rv_fad->fad.admin_group_exclude_any, + &sc_fad->fad.admin_group_exclude_any); + admin_group_copy(&rv_fad->fad.admin_group_include_any, + &sc_fad->fad.admin_group_include_any); + admin_group_copy(&rv_fad->fad.admin_group_include_all, + &sc_fad->fad.admin_group_include_all); + + rv->fads[i] = rv_fad; + } +#endif /* ifndef FABRICD */ + return rv; } @@ -3548,46 +4058,186 @@ static void format_tlv_router_cap(const struct isis_router_cap *router_cap, for (int i = 0; i < SR_ALGORITHM_COUNT; i++) if (router_cap->algo[i] != SR_ALGORITHM_UNSET) sbuf_push(buf, indent, " %u: %s\n", i, - router_cap->algo[i] == 0 - ? "SPF" - : "Strict SPF"); + sr_algorithm_string( + router_cap->algo[i])); } /* Segment Routing Node MSD as per RFC8491 section #2 */ if (router_cap->msd != 0) sbuf_push(buf, indent, " Node Maximum SID Depth: %u\n", router_cap->msd); + +#ifndef FABRICD + /* Flex-Algo */ + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) { + char admin_group_buf[ADMIN_GROUP_PRINT_MAX_SIZE]; + int indent2; + struct admin_group *admin_group; + struct isis_router_cap_fad *fad; + + fad = router_cap->fads[i]; + if (!fad) + continue; + + sbuf_push(buf, indent, " Flex-Algo Definition: %d\n", + fad->fad.algorithm); + sbuf_push(buf, indent, " Metric-Type: %d\n", + fad->fad.metric_type); + sbuf_push(buf, indent, " Calc-Type: %d\n", + fad->fad.calc_type); + sbuf_push(buf, indent, " Priority: %d\n", fad->fad.priority); + + indent2 = indent + strlen(" Exclude-Any: "); + admin_group = &fad->fad.admin_group_exclude_any; + sbuf_push(buf, indent, " Exclude-Any: "); + sbuf_push(buf, 0, "%s\n", + admin_group_string(admin_group_buf, + ADMIN_GROUP_PRINT_MAX_SIZE, + indent2, admin_group)); + + indent2 = indent + strlen(" Include-Any: "); + admin_group = &fad->fad.admin_group_include_any; + sbuf_push(buf, indent, " Include-Any: "); + sbuf_push(buf, 0, "%s\n", + admin_group_string(admin_group_buf, + ADMIN_GROUP_PRINT_MAX_SIZE, + indent2, admin_group)); + + indent2 = indent + strlen(" Include-All: "); + admin_group = &fad->fad.admin_group_include_all; + sbuf_push(buf, indent, " Include-All: "); + sbuf_push(buf, 0, "%s\n", + admin_group_string(admin_group_buf, + ADMIN_GROUP_PRINT_MAX_SIZE, + indent2, admin_group)); + + sbuf_push(buf, indent, " M-Flag: %c\n", + CHECK_FLAG(fad->fad.flags, FAD_FLAG_M) ? '1' : '0'); + + if (fad->fad.flags != 0 && fad->fad.flags != FAD_FLAG_M) + sbuf_push(buf, indent, " Flags: 0x%x\n", + fad->fad.flags); + if (fad->fad.exclude_srlg) + sbuf_push(buf, indent, " Exclude SRLG: Enabled\n"); + if (fad->fad.unsupported_subtlv) + sbuf_push(buf, indent, + " Got an unsupported sub-TLV: Yes\n"); + } +#endif /* ifndef FABRICD */ } static void free_tlv_router_cap(struct isis_router_cap *router_cap) { + if (!router_cap) + return; + +#ifndef FABRICD + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) { + struct isis_router_cap_fad *fad; + + fad = router_cap->fads[i]; + if (!fad) + continue; + admin_group_term(&fad->fad.admin_group_exclude_any); + admin_group_term(&fad->fad.admin_group_include_any); + admin_group_term(&fad->fad.admin_group_include_all); + XFREE(MTYPE_ISIS_TLV, fad); + } +#endif /* ifndef FABRICD */ + XFREE(MTYPE_ISIS_TLV, router_cap); } +#ifndef FABRICD +static size_t +isis_router_cap_fad_sub_tlv_len(const struct isis_router_cap_fad *fad) +{ + size_t sz = ISIS_SUBTLV_FAD_MIN_SIZE; + uint32_t admin_group_length; + + admin_group_length = + admin_group_nb_words(&fad->fad.admin_group_exclude_any); + if (admin_group_length) + sz += sizeof(uint32_t) * admin_group_length + 2; + + admin_group_length = + admin_group_nb_words(&fad->fad.admin_group_include_any); + if (admin_group_length) + sz += sizeof(uint32_t) * admin_group_length + 2; + + admin_group_length = + admin_group_nb_words(&fad->fad.admin_group_include_all); + if (admin_group_length) + sz += sizeof(uint32_t) * admin_group_length + 2; + + if (fad->fad.flags != 0) + sz += ISIS_SUBTLV_FAD_SUBSUBTLV_FLAGS_SIZE + 2; + + /* TODO: add exclude SRLG sub-sub-TLV length when supported */ + + return sz; +} +#endif /* ifndef FABRICD */ + +static size_t isis_router_cap_tlv_size(const struct isis_router_cap *router_cap) +{ + size_t sz = 2 + ISIS_ROUTER_CAP_SIZE; +#ifndef FABRICD + size_t fad_sz; +#endif /* ifndef FABRICD */ + int nb_algo; + + if ((router_cap->srgb.range_size != 0) && + (router_cap->srgb.lower_bound != 0)) { + sz += 2 + ISIS_SUBTLV_SID_LABEL_RANGE_SIZE; + sz += 2 + ISIS_SUBTLV_SID_LABEL_SIZE; + + nb_algo = isis_tlvs_sr_algo_count(router_cap); + if (nb_algo != 0) + sz += 2 + nb_algo; + + if ((router_cap->srlb.range_size != 0) && + (router_cap->srlb.lower_bound != 0)) { + sz += 2 + ISIS_SUBTLV_SID_LABEL_RANGE_SIZE; + sz += 2 + ISIS_SUBTLV_SID_LABEL_SIZE; + } + + if (router_cap->msd != 0) + sz += 2 + ISIS_SUBTLV_NODE_MSD_SIZE; + } + +#ifndef FABRICD + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) { + if (!router_cap->fads[i]) + continue; + fad_sz = 2 + + isis_router_cap_fad_sub_tlv_len(router_cap->fads[i]); + if (((sz + fad_sz) % 256) < (sz % 256)) + sz += 2 + ISIS_ROUTER_CAP_SIZE + fad_sz; + else + sz += fad_sz; + } +#endif /* ifndef FABRICD */ + + return sz; +} + static int pack_tlv_router_cap(const struct isis_router_cap *router_cap, struct stream *s) { - size_t tlv_len = ISIS_ROUTER_CAP_SIZE; - size_t len_pos; + size_t tlv_len, len_pos; uint8_t nb_algo; if (!router_cap) return 0; - /* Compute Maximum TLV size */ - tlv_len += ISIS_SUBTLV_SID_LABEL_RANGE_SIZE - + ISIS_SUBTLV_HDR_SIZE - + ISIS_SUBTLV_ALGORITHM_SIZE - + ISIS_SUBTLV_NODE_MSD_SIZE; - - if (STREAM_WRITEABLE(s) < (unsigned int)(2 + tlv_len)) + if (STREAM_WRITEABLE(s) < isis_router_cap_tlv_size(router_cap)) return 1; /* Add Router Capability TLV 242 with Router ID and Flags */ stream_putc(s, ISIS_TLV_ROUTER_CAPABILITY); - /* Real length will be adjusted later */ len_pos = stream_get_endp(s); - stream_putc(s, tlv_len); + stream_putc(s, 0); /* Real length will be adjusted later */ stream_put_ipv4(s, router_cap->router_id.s_addr); stream_putc(s, router_cap->flags); @@ -3603,14 +4253,13 @@ static int pack_tlv_router_cap(const struct isis_router_cap *router_cap, stream_put3(s, router_cap->srgb.lower_bound); /* Then SR Algorithm if set as per RFC8667 section #3.2 */ - for (nb_algo = 0; nb_algo < SR_ALGORITHM_COUNT; nb_algo++) - if (router_cap->algo[nb_algo] == SR_ALGORITHM_UNSET) - break; + nb_algo = isis_tlvs_sr_algo_count(router_cap); if (nb_algo > 0) { stream_putc(s, ISIS_SUBTLV_ALGORITHM); stream_putc(s, nb_algo); - for (int i = 0; i < nb_algo; i++) - stream_putc(s, router_cap->algo[i]); + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) + if (router_cap->algo[i] != SR_ALGORITHM_UNSET) + stream_putc(s, router_cap->algo[i]); } /* Local Block if defined as per RFC8667 section #3.3 */ @@ -3635,6 +4284,80 @@ static int pack_tlv_router_cap(const struct isis_router_cap *router_cap, } } +#ifndef FABRICD + /* Flex Algo Definitions */ + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) { + struct isis_router_cap_fad *fad; + size_t subtlv_len; + struct admin_group *ag; + uint32_t admin_group_length; + + fad = router_cap->fads[i]; + if (!fad) + continue; + + subtlv_len = isis_router_cap_fad_sub_tlv_len(fad); + + if ((stream_get_endp(s) - len_pos - 1) > 250) { + /* Adjust TLV length which depends on subTLVs presence + */ + tlv_len = stream_get_endp(s) - len_pos - 1; + stream_putc_at(s, len_pos, tlv_len); + + /* Add Router Capability TLV 242 with Router ID and + * Flags + */ + stream_putc(s, ISIS_TLV_ROUTER_CAPABILITY); + /* Real length will be adjusted later */ + len_pos = stream_get_endp(s); + stream_putc(s, 0); + stream_put_ipv4(s, router_cap->router_id.s_addr); + stream_putc(s, router_cap->flags); + } + + stream_putc(s, ISIS_SUBTLV_FAD); + stream_putc(s, subtlv_len); /* length will be filled later */ + + stream_putc(s, fad->fad.algorithm); + stream_putc(s, fad->fad.metric_type); + stream_putc(s, fad->fad.calc_type); + stream_putc(s, fad->fad.priority); + + ag = &fad->fad.admin_group_exclude_any; + admin_group_length = admin_group_nb_words(ag); + if (admin_group_length) { + stream_putc(s, ISIS_SUBTLV_FAD_SUBSUBTLV_EXCAG); + stream_putc(s, sizeof(uint32_t) * admin_group_length); + for (size_t i = 0; i < admin_group_length; i++) + stream_putl(s, admin_group_get_offset(ag, i)); + } + + ag = &fad->fad.admin_group_include_any; + admin_group_length = admin_group_nb_words(ag); + if (admin_group_length) { + stream_putc(s, ISIS_SUBTLV_FAD_SUBSUBTLV_INCANYAG); + stream_putc(s, sizeof(uint32_t) * admin_group_length); + for (size_t i = 0; i < admin_group_length; i++) + stream_putl(s, admin_group_get_offset(ag, i)); + } + + ag = &fad->fad.admin_group_include_all; + admin_group_length = admin_group_nb_words(ag); + if (admin_group_length) { + stream_putc(s, ISIS_SUBTLV_FAD_SUBSUBTLV_INCALLAG); + stream_putc(s, sizeof(uint32_t) * admin_group_length); + for (size_t i = 0; i < admin_group_length; i++) + stream_putl(s, admin_group_get_offset(ag, i)); + } + + if (fad->fad.flags != 0) { + stream_putc(s, ISIS_SUBTLV_FAD_SUBSUBTLV_FLAGS); + stream_putc(s, ISIS_SUBTLV_FAD_SUBSUBTLV_FLAGS_SIZE); + stream_putc(s, fad->fad.flags); + } + } +#endif /* ifndef FABRICD */ + /* Adjust TLV length which depends on subTLVs presence */ tlv_len = stream_get_endp(s) - len_pos - 1; stream_putc_at(s, len_pos, tlv_len); @@ -3661,18 +4384,16 @@ static int unpack_tlv_router_cap(enum isis_tlv_context context, return 0; } - if (tlvs->router_cap) { - sbuf_push(log, indent, - "WARNING: Router Capability TLV present multiple times.\n"); - stream_forward_getp(s, tlv_len); - return 0; + if (tlvs->router_cap) + /* Multiple Router Capability found */ + rcap = tlvs->router_cap; + else { + /* Allocate router cap structure and initialize SR Algorithms */ + rcap = XCALLOC(MTYPE_ISIS_TLV, sizeof(struct isis_router_cap)); + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) + rcap->algo[i] = SR_ALGORITHM_UNSET; } - /* Allocate router cap structure and initialize SR Algorithms */ - rcap = XCALLOC(MTYPE_ISIS_TLV, sizeof(struct isis_router_cap)); - for (int i = 0; i < SR_ALGORITHM_COUNT; i++) - rcap->algo[i] = SR_ALGORITHM_UNSET; - /* Get Router ID and Flags */ rcap->router_id.s_addr = stream_get_ipv4(s); rcap->flags = stream_getc(s); @@ -3680,6 +4401,10 @@ static int unpack_tlv_router_cap(enum isis_tlv_context context, /* Parse remaining part of the TLV if present */ subtlv_len = tlv_len - ISIS_ROUTER_CAP_SIZE; while (subtlv_len > 2) { +#ifndef FABRICD + struct isis_router_cap_fad *fad; + uint8_t subsubtlvs_len; +#endif /* ifndef FABRICD */ uint8_t msd_type; type = stream_getc(s); @@ -3752,14 +4477,16 @@ static int unpack_tlv_router_cap(enum isis_tlv_context context, case ISIS_SUBTLV_ALGORITHM: if (length == 0) break; - /* Only 2 algorithms are supported: SPF & Strict SPF */ - stream_get(&rcap->algo, s, - length > SR_ALGORITHM_COUNT - ? SR_ALGORITHM_COUNT - : length); - if (length > SR_ALGORITHM_COUNT) - stream_forward_getp( - s, length - SR_ALGORITHM_COUNT); + + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) + rcap->algo[i] = SR_ALGORITHM_UNSET; + + for (int i = 0; i < length; i++) { + uint8_t algo; + + algo = stream_getc(s); + rcap->algo[algo] = algo; + } break; case ISIS_SUBTLV_SRLB: /* Check that SRLB is correctly formated */ @@ -3831,6 +4558,80 @@ static int unpack_tlv_router_cap(enum isis_tlv_context context, if (length > MSD_TLV_SIZE) stream_forward_getp(s, length - MSD_TLV_SIZE); break; +#ifndef FABRICD + case ISIS_SUBTLV_FAD: + fad = XCALLOC(MTYPE_ISIS_TLV, + sizeof(struct isis_router_cap_fad)); + fad->fad.algorithm = stream_getc(s); + fad->fad.metric_type = stream_getc(s); + fad->fad.calc_type = stream_getc(s); + fad->fad.priority = stream_getc(s); + rcap->fads[fad->fad.algorithm] = fad; + admin_group_init(&fad->fad.admin_group_exclude_any); + admin_group_init(&fad->fad.admin_group_include_any); + admin_group_init(&fad->fad.admin_group_include_all); + + subsubtlvs_len = length - 4; + while (subsubtlvs_len > 2) { + struct admin_group *ag; + uint8_t subsubtlv_type; + uint8_t subsubtlv_len; + uint32_t v; + int n_ag, i; + + subsubtlv_type = stream_getc(s); + subsubtlv_len = stream_getc(s); + + switch (subsubtlv_type) { + case ISIS_SUBTLV_FAD_SUBSUBTLV_EXCAG: + ag = &fad->fad.admin_group_exclude_any; + n_ag = subsubtlv_len / sizeof(uint32_t); + for (i = 0; i < n_ag; i++) { + v = stream_getl(s); + admin_group_bulk_set(ag, v, i); + } + break; + case ISIS_SUBTLV_FAD_SUBSUBTLV_INCANYAG: + ag = &fad->fad.admin_group_include_any; + n_ag = subsubtlv_len / sizeof(uint32_t); + for (i = 0; i < n_ag; i++) { + v = stream_getl(s); + admin_group_bulk_set(ag, v, i); + } + break; + case ISIS_SUBTLV_FAD_SUBSUBTLV_INCALLAG: + ag = &fad->fad.admin_group_include_all; + n_ag = subsubtlv_len / sizeof(uint32_t); + for (i = 0; i < n_ag; i++) { + v = stream_getl(s); + admin_group_bulk_set(ag, v, i); + } + break; + case ISIS_SUBTLV_FAD_SUBSUBTLV_FLAGS: + if (subsubtlv_len == 0) + break; + + fad->fad.flags = stream_getc(s); + for (i = subsubtlv_len - 1; i > 0; --i) + stream_getc(s); + break; + case ISIS_SUBTLV_FAD_SUBSUBTLV_ESRLG: + fad->fad.exclude_srlg = true; + stream_forward_getp(s, subsubtlv_len); + break; + default: + sbuf_push( + log, indent, + "Received an unsupported Flex-Algo sub-TLV type %u\n", + subsubtlv_type); + fad->fad.unsupported_subtlv = true; + stream_forward_getp(s, subsubtlv_len); + break; + } + subsubtlvs_len -= 2 + subsubtlv_len; + } + break; +#endif /* ifndef FABRICD */ default: stream_forward_getp(s, length); break; @@ -3988,33 +4789,29 @@ static void format_tlv_purge_originator(struct isis_purge_originator *poi, struct sbuf *buf, struct json_object *json, int indent) { + char sen_id[ISO_SYSID_STRLEN]; + char gen_id[ISO_SYSID_STRLEN]; + if (!poi) return; + snprintfrr(gen_id, ISO_SYSID_STRLEN, "%pSY", poi->generator); + if (poi->sender_set) + snprintfrr(sen_id, ISO_SYSID_STRLEN, "%pSY", poi->sender); + if (json) { struct json_object *purge_json; purge_json = json_object_new_object(); json_object_object_add(json, "purge_originator", purge_json); - json_object_string_add( - purge_json, "id", - isis_format_id(poi->generator, sizeof(poi->generator))); - if (poi->sender_set) { - json_object_string_add( - purge_json, "rec-from", - isis_format_id(poi->sender, - sizeof(poi->sender))); - } + json_object_string_add(purge_json, "id", gen_id); + if (poi->sender_set) + json_object_string_add(purge_json, "rec-from", sen_id); } else { sbuf_push(buf, indent, "Purge Originator Identification:\n"); - sbuf_push( - buf, indent, " Generator: %s\n", - isis_format_id(poi->generator, sizeof(poi->generator))); - if (poi->sender_set) { - sbuf_push(buf, indent, " Received-From: %s\n", - isis_format_id(poi->sender, - sizeof(poi->sender))); - } + sbuf_push(buf, indent, " Generator: %s\n", gen_id); + if (poi->sender_set) + sbuf_push(buf, indent, " Received-From: %s\n", sen_id); } } @@ -5271,14 +6068,14 @@ void isis_tlvs_add_area_addresses(struct isis_tlvs *tlvs, struct list *addresses) { struct listnode *node; - struct area_addr *area_addr; + struct iso_address *area_addr; for (ALL_LIST_ELEMENTS_RO(addresses, node, area_addr)) { struct isis_area_address *a = XCALLOC(MTYPE_ISIS_TLV, sizeof(*a)); a->len = area_addr->addr_len; - memcpy(a->addr, area_addr->area_addr, 20); + memcpy(a->addr, area_addr->area_addr, ISO_ADDR_SIZE); append_item(&tlvs->area_addresses, (struct isis_item *)a); } } @@ -5475,7 +6272,7 @@ bool isis_tlvs_area_addresses_match(struct isis_tlvs *tlvs, for (struct isis_area_address *addr = addr_head; addr; addr = addr->next) { struct listnode *node; - struct area_addr *a; + struct iso_address *a; for (ALL_LIST_ELEMENTS_RO(addresses, node, a)) { if (a->addr_len == addr->len @@ -5782,16 +6579,61 @@ void isis_tlvs_set_dynamic_hostname(struct isis_tlvs *tlvs, tlvs->hostname = XSTRDUP(MTYPE_ISIS_TLV, hostname); } -/* Set Router Capability TLV parameters */ -void isis_tlvs_set_router_capability(struct isis_tlvs *tlvs, - const struct isis_router_cap *cap) +/* Init Router Capability TLV parameters */ +struct isis_router_cap *isis_tlvs_init_router_capability(struct isis_tlvs *tlvs) { - XFREE(MTYPE_ISIS_TLV, tlvs->router_cap); - if (!cap) - return; - tlvs->router_cap = XCALLOC(MTYPE_ISIS_TLV, sizeof(*tlvs->router_cap)); - *tlvs->router_cap = *cap; + + /* init SR algo list content to the default value */ + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) + tlvs->router_cap->algo[i] = SR_ALGORITHM_UNSET; + + return tlvs->router_cap; +} + +#ifndef FABRICD +void isis_tlvs_set_router_capability_fad(struct isis_tlvs *tlvs, + struct flex_algo *fa, int algorithm, + uint8_t *sysid) +{ + struct isis_router_cap_fad *rcap_fad; + + assert(tlvs->router_cap); + + rcap_fad = tlvs->router_cap->fads[algorithm]; + + if (!rcap_fad) + rcap_fad = XCALLOC(MTYPE_ISIS_TLV, + sizeof(struct isis_router_cap_fad)); + + memset(rcap_fad->sysid, 0, ISIS_SYS_ID_LEN + 2); + memcpy(rcap_fad->sysid, sysid, ISIS_SYS_ID_LEN); + + memcpy(&rcap_fad->fad, fa, sizeof(struct flex_algo)); + + rcap_fad->fad.admin_group_exclude_any.bitmap.data = NULL; + rcap_fad->fad.admin_group_include_any.bitmap.data = NULL; + rcap_fad->fad.admin_group_include_all.bitmap.data = NULL; + + admin_group_copy(&rcap_fad->fad.admin_group_exclude_any, + &fa->admin_group_exclude_any); + admin_group_copy(&rcap_fad->fad.admin_group_include_any, + &fa->admin_group_include_any); + admin_group_copy(&rcap_fad->fad.admin_group_include_all, + &fa->admin_group_include_all); + + tlvs->router_cap->fads[algorithm] = rcap_fad; +} +#endif /* ifndef FABRICD */ + +int isis_tlvs_sr_algo_count(const struct isis_router_cap *cap) +{ + int count = 0; + + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) + if (cap->algo[i] != SR_ALGORITHM_UNSET) + count++; + return count; } void isis_tlvs_set_te_router_id(struct isis_tlvs *tlvs, @@ -5861,42 +6703,107 @@ void isis_tlvs_del_lan_adj_sid(struct isis_ext_subtlvs *exts, UNSET_SUBTLV(exts, EXT_LAN_ADJ_SID); } +void isis_tlvs_del_asla_flex_algo(struct isis_ext_subtlvs *ext, + struct isis_asla_subtlvs *asla) +{ + admin_group_term(&asla->ext_admin_group); + listnode_delete(ext->aslas, asla); + XFREE(MTYPE_ISIS_SUBTLV, asla); +} + +struct isis_asla_subtlvs * +isis_tlvs_find_alloc_asla(struct isis_ext_subtlvs *ext, uint8_t standard_apps) +{ + struct isis_asla_subtlvs *asla; + struct listnode *node; + + if (!list_isempty(ext->aslas)) { + for (ALL_LIST_ELEMENTS_RO(ext->aslas, node, asla)) { + if (CHECK_FLAG(asla->standard_apps, standard_apps)) + return asla; + } + } + + asla = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(struct isis_asla_subtlvs)); + admin_group_init(&asla->ext_admin_group); + SET_FLAG(asla->standard_apps, standard_apps); + SET_FLAG(asla->user_def_apps, standard_apps); + asla->standard_apps_length = ASLA_APP_IDENTIFIER_BIT_LENGTH; + asla->user_def_apps_length = ASLA_APP_IDENTIFIER_BIT_LENGTH; + + listnode_add(ext->aslas, asla); + return asla; +} + +void isis_tlvs_free_asla(struct isis_ext_subtlvs *ext, uint8_t standard_apps) +{ + struct isis_asla_subtlvs *asla; + struct listnode *node; + + if (!ext) + return; + + for (ALL_LIST_ELEMENTS_RO(ext->aslas, node, asla)) { + if (!CHECK_FLAG(asla->standard_apps, standard_apps)) + continue; + isis_tlvs_del_asla_flex_algo(ext, asla); + break; + } +} + void isis_tlvs_add_extended_ip_reach(struct isis_tlvs *tlvs, struct prefix_ipv4 *dest, uint32_t metric, - bool external, struct sr_prefix_cfg *pcfg) + bool external, + struct sr_prefix_cfg **pcfgs) { struct isis_extended_ip_reach *r = XCALLOC(MTYPE_ISIS_TLV, sizeof(*r)); r->metric = metric; memcpy(&r->prefix, dest, sizeof(*dest)); apply_mask_ipv4(&r->prefix); - if (pcfg) { - struct isis_prefix_sid *psid = - XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*psid)); - isis_sr_prefix_cfg2subtlv(pcfg, external, psid); + if (pcfgs) { r->subtlvs = isis_alloc_subtlvs(ISIS_CONTEXT_SUBTLV_IP_REACH); - append_item(&r->subtlvs->prefix_sids, (struct isis_item *)psid); + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) { + struct isis_prefix_sid *psid; + struct sr_prefix_cfg *pcfg = pcfgs[i]; + + if (!pcfg) + continue; + + psid = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*psid)); + isis_sr_prefix_cfg2subtlv(pcfg, external, psid); + append_item(&r->subtlvs->prefix_sids, + (struct isis_item *)psid); + } } + append_item(&tlvs->extended_ip_reach, (struct isis_item *)r); } void isis_tlvs_add_ipv6_reach(struct isis_tlvs *tlvs, uint16_t mtid, struct prefix_ipv6 *dest, uint32_t metric, - bool external, struct sr_prefix_cfg *pcfg) + bool external, struct sr_prefix_cfg **pcfgs) { struct isis_ipv6_reach *r = XCALLOC(MTYPE_ISIS_TLV, sizeof(*r)); r->metric = metric; memcpy(&r->prefix, dest, sizeof(*dest)); apply_mask_ipv6(&r->prefix); - if (pcfg) { - struct isis_prefix_sid *psid = - XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*psid)); - - isis_sr_prefix_cfg2subtlv(pcfg, external, psid); + if (pcfgs) { r->subtlvs = isis_alloc_subtlvs(ISIS_CONTEXT_SUBTLV_IP_REACH); - append_item(&r->subtlvs->prefix_sids, (struct isis_item *)psid); + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) { + struct isis_prefix_sid *psid; + struct sr_prefix_cfg *pcfg = pcfgs[i]; + + if (!pcfg) + continue; + + psid = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*psid)); + isis_sr_prefix_cfg2subtlv(pcfg, external, psid); + append_item(&r->subtlvs->prefix_sids, + (struct isis_item *)psid); + } } struct isis_item_list *l; diff --git a/isisd/isis_tlvs.h b/isisd/isis_tlvs.h index 51058f1af1..03e2b2edcc 100644 --- a/isisd/isis_tlvs.h +++ b/isisd/isis_tlvs.h @@ -9,8 +9,12 @@ #ifndef ISIS_TLVS_H #define ISIS_TLVS_H +#include "segment_routing.h" #include "openbsd-tree.h" #include "prefix.h" +#include "flex_algo.h" +#include "affinitymap.h" + DECLARE_MTYPE(ISIS_SUBTLV); @@ -102,7 +106,7 @@ struct isis_spine_leaf { enum isis_threeway_state { ISIS_THREEWAY_DOWN = 2, ISIS_THREEWAY_INITIALIZING = 1, - ISIS_THREEWAY_UP = 0 + ISIS_THREEWAY_UP = 0, }; struct isis_threeway_adj { @@ -177,19 +181,18 @@ struct isis_lan_adj_sid { #define ISIS_ROUTER_CAP_FLAG_D 0x02 #define ISIS_ROUTER_CAP_SIZE 5 -/* Number of supported algorithm for Segment Routing. - * Right now only 2 have been standardized: - * - 0: SPF - * - 1: Strict SPF - */ -#define SR_ALGORITHM_COUNT 2 -#define SR_ALGORITHM_SPF 0 -#define SR_ALGORITHM_STRICT_SPF 1 -#define SR_ALGORITHM_UNSET 255 - #define MSD_TYPE_BASE_MPLS_IMPOSITION 0x01 #define MSD_TLV_SIZE 2 +#ifndef FABRICD +struct isis_router_cap_fad; +struct isis_router_cap_fad { + uint8_t sysid[ISIS_SYS_ID_LEN + 2]; + + struct flex_algo fad; +}; +#endif /* ifndef FABRICD */ + struct isis_router_cap { struct in_addr router_id; uint8_t flags; @@ -200,6 +203,11 @@ struct isis_router_cap { uint8_t algo[SR_ALGORITHM_COUNT]; /* RFC 8491 */ uint8_t msd; + +#ifndef FABRICD + /* RFC9350 Flex-Algorithm */ + struct isis_router_cap_fad *fads[SR_ALGORITHM_COUNT]; +#endif /* ifndef FABRICD */ }; struct isis_item { @@ -309,7 +317,7 @@ enum isis_tlv_context { ISIS_CONTEXT_SUBTLV_NE_REACH, ISIS_CONTEXT_SUBTLV_IP_REACH, ISIS_CONTEXT_SUBTLV_IPV6_REACH, - ISIS_CONTEXT_MAX + ISIS_CONTEXT_MAX, }; struct isis_subtlvs { @@ -394,7 +402,22 @@ enum isis_tlv_type { ISIS_SUBTLV_AVA_BW = 38, ISIS_SUBTLV_USE_BW = 39, - ISIS_SUBTLV_MAX = 40 + /* RFC 7308 */ + ISIS_SUBTLV_EXT_ADMIN_GRP = 14, + + /* RFC 8919 */ + ISIS_SUBTLV_ASLA = 16, + + /* draft-ietf-lsr-isis-srv6-extensions */ + ISIS_SUBTLV_SID_END = 5, + ISIS_SUBTLV_SID_END_X = 43, + + ISIS_SUBTLV_MAX = 40, + + /* draft-ietf-lsr-isis-srv6-extensions */ + ISIS_SUBSUBTLV_SID_STRUCTURE = 1, + + ISIS_SUBSUBTLV_MAX = 256, }; /* subTLVs size for TE and SR */ @@ -422,19 +445,39 @@ enum ext_subtlv_size { /* RFC 7810 */ ISIS_SUBTLV_MM_DELAY_SIZE = 8, + /* RFC9350 - Flex-Algorithm */ + ISIS_SUBTLV_FAD = 26, + ISIS_SUBTLV_FAD_MIN_SIZE = 4, + ISIS_SUBTLV_HDR_SIZE = 2, ISIS_SUBTLV_DEF_SIZE = 4, - /* RFC 7308 */ - ISIS_SUBTLV_EXT_ADMIN_GRP = 14, + ISIS_SUBTLV_MAX_SIZE = 180, + + /* draft-ietf-lsr-isis-srv6-extensions */ + ISIS_SUBSUBTLV_SID_STRUCTURE_SIZE = 4, + + ISIS_SUBSUBTLV_HDR_SIZE = 2, + ISIS_SUBSUBTLV_MAX_SIZE = 180, + + /* RFC9350 - Flex-Algorithm */ + ISIS_SUBTLV_FAD_SUBSUBTLV_FLAGS_SIZE = 1, +}; - ISIS_SUBTLV_MAX_SIZE = 180 +enum ext_subsubtlv_types { + ISIS_SUBTLV_FAD_SUBSUBTLV_EXCAG = 1, + ISIS_SUBTLV_FAD_SUBSUBTLV_INCANYAG = 2, + ISIS_SUBTLV_FAD_SUBSUBTLV_INCALLAG = 3, + ISIS_SUBTLV_FAD_SUBSUBTLV_FLAGS = 4, + ISIS_SUBTLV_FAD_SUBSUBTLV_ESRLG = 5, }; /* Macros to manage the optional presence of EXT subTLVs */ #define SET_SUBTLV(s, t) ((s->status) |= (t)) #define UNSET_SUBTLV(s, t) ((s->status) &= ~(t)) #define IS_SUBTLV(s, t) (s->status & t) +#define RESET_SUBTLV(s) (s->status = 0) +#define NO_SUBTLV(s) (s->status == 0) #define EXT_DISABLE 0x000000 #define EXT_ADM_GRP 0x000001 @@ -506,6 +549,45 @@ struct isis_ext_subtlvs { /* Segment Routing Adjacency & LAN Adjacency Segment ID */ struct isis_item_list adj_sid; struct isis_item_list lan_sid; + + struct list *aslas; +}; + +/* RFC 8919 */ +#define ISIS_SABM_FLAG_R 0x80 /* RSVP-TE */ +#define ISIS_SABM_FLAG_S 0x40 /* Segment Routing Policy */ +#define ISIS_SABM_FLAG_L 0x20 /* Loop-Free Alternate */ +#define ISIS_SABM_FLAG_X 0x10 /* Flex-Algorithm - RFC9350 */ + +#define ASLA_APP_IDENTIFIER_BIT_LENGTH 1 +#define ASLA_LEGACY_FLAG 0x80 +#define ASLA_APPS_LENGTH_MASK 0x7f + +struct isis_asla_subtlvs { + uint32_t status; + + /* Application Specific Link Attribute - RFC 8919 */ + bool legacy; /* L-Flag */ + uint8_t standard_apps_length; + uint8_t user_def_apps_length; + uint8_t standard_apps; + uint8_t user_def_apps; + + /* Sub-TLV list - rfc8919 section-3.1 */ + uint32_t admin_group; + struct admin_group ext_admin_group; /* Res. Class/Color - RFC 7308 */ + float max_bw; /* Maximum Bandwidth - RFC 5305 */ + float max_rsv_bw; /* Maximum Reservable Bandwidth - RFC 5305 */ + float unrsv_bw[8]; /* Unreserved Bandwidth - RFC 5305 */ + uint32_t te_metric; /* Traffic Engineering Metric - RFC 5305 */ + uint32_t delay; /* Average Link Delay - RFC 8570 */ + uint32_t min_delay; /* Low Link Delay - RFC 8570 */ + uint32_t max_delay; /* High Link Delay - RFC 8570 */ + uint32_t delay_var; /* Link Delay Variation i.e. Jitter - RFC 8570 */ + uint32_t pkt_loss; /* Unidirectional Link Packet Loss - RFC 8570 */ + float res_bw; /* Unidirectional Residual Bandwidth - RFC 8570 */ + float ava_bw; /* Unidirectional Available Bandwidth - RFC 8570 */ + float use_bw; /* Unidirectional Utilized Bandwidth - RFC 8570 */ }; #define IS_COMPAT_MT_TLV(tlv_type) \ @@ -536,6 +618,12 @@ struct list *isis_fragment_tlvs(struct isis_tlvs *tlvs, size_t size); #define ISIS_MT_AT_MASK 0x4000 #endif +/* RFC 8919 */ +#define ISIS_SABM_FLAG_R 0x80 /* RSVP-TE */ +#define ISIS_SABM_FLAG_S 0x40 /* Segment Routing Policy */ +#define ISIS_SABM_FLAG_L 0x20 /* Loop-Free Alternate */ +#define ISIS_SABM_FLAG_X 0x10 /* Flex-Algorithm - RFC9350 */ + void isis_tlvs_add_auth(struct isis_tlvs *tlvs, struct isis_passwd *passwd); void isis_tlvs_add_area_addresses(struct isis_tlvs *tlvs, struct list *addresses); @@ -567,8 +655,19 @@ void isis_tlvs_add_csnp_entries(struct isis_tlvs *tlvs, uint8_t *start_id, struct isis_lsp **last_lsp); void isis_tlvs_set_dynamic_hostname(struct isis_tlvs *tlvs, const char *hostname); -void isis_tlvs_set_router_capability(struct isis_tlvs *tlvs, - const struct isis_router_cap *cap); +struct isis_router_cap * +isis_tlvs_init_router_capability(struct isis_tlvs *tlvs); + +struct isis_area; +struct isis_flex_algo; +void isis_tlvs_set_router_capability_fad(struct isis_tlvs *tlvs, + struct flex_algo *fa, int algorithm, + uint8_t *sysid); + +struct isis_area; + +int isis_tlvs_sr_algo_count(const struct isis_router_cap *cap); + void isis_tlvs_set_te_router_id(struct isis_tlvs *tlvs, const struct in_addr *id); void isis_tlvs_set_te_router_id_ipv6(struct isis_tlvs *tlvs, @@ -577,10 +676,11 @@ void isis_tlvs_add_oldstyle_ip_reach(struct isis_tlvs *tlvs, struct prefix_ipv4 *dest, uint8_t metric); void isis_tlvs_add_extended_ip_reach(struct isis_tlvs *tlvs, struct prefix_ipv4 *dest, uint32_t metric, - bool external, struct sr_prefix_cfg *pcfg); + bool external, + struct sr_prefix_cfg **pcfgs); void isis_tlvs_add_ipv6_reach(struct isis_tlvs *tlvs, uint16_t mtid, struct prefix_ipv6 *dest, uint32_t metric, - bool external, struct sr_prefix_cfg *pcfg); + bool external, struct sr_prefix_cfg **pcfgs); void isis_tlvs_add_ipv6_dstsrc_reach(struct isis_tlvs *tlvs, uint16_t mtid, struct prefix_ipv6 *dest, struct prefix_ipv6 *src, @@ -596,6 +696,12 @@ void isis_tlvs_add_lan_adj_sid(struct isis_ext_subtlvs *exts, void isis_tlvs_del_lan_adj_sid(struct isis_ext_subtlvs *exts, struct isis_lan_adj_sid *lan); +void isis_tlvs_del_asla_flex_algo(struct isis_ext_subtlvs *ext, + struct isis_asla_subtlvs *asla); +struct isis_asla_subtlvs * +isis_tlvs_find_alloc_asla(struct isis_ext_subtlvs *ext, uint8_t standard_apps); +void isis_tlvs_free_asla(struct isis_ext_subtlvs *ext, uint8_t standard_apps); + void isis_tlvs_add_oldstyle_reach(struct isis_tlvs *tlvs, uint8_t *id, uint8_t metric); void isis_tlvs_add_extended_reach(struct isis_tlvs *tlvs, uint16_t mtid, diff --git a/isisd/isis_tx_queue.c b/isisd/isis_tx_queue.c index ec2d50d60a..caf97f1174 100644 --- a/isisd/isis_tx_queue.c +++ b/isisd/isis_tx_queue.c @@ -126,12 +126,12 @@ void _isis_tx_queue_add(struct isis_tx_queue *queue, return; if (IS_DEBUG_TX_QUEUE) { - zlog_debug("Add LSP %s to %s queue as %s LSP. (From %s %s:%d)", - rawlspid_print(lsp->hdr.lsp_id), - queue->circuit->interface->name, - (type == TX_LSP_CIRCUIT_SCOPED) ? - "circuit scoped" : "regular", - func, file, line); + zlog_debug( + "Add LSP %pLS to %s queue as %s LSP. (From %s %s:%d)", + lsp->hdr.lsp_id, queue->circuit->interface->name, + (type == TX_LSP_CIRCUIT_SCOPED) ? "circuit scoped" + : "regular", + func, file, line); } struct isis_tx_queue_entry *e = tx_queue_find(queue, lsp); @@ -164,9 +164,8 @@ void _isis_tx_queue_del(struct isis_tx_queue *queue, struct isis_lsp *lsp, return; if (IS_DEBUG_TX_QUEUE) { - zlog_debug("Remove LSP %s from %s queue. (From %s %s:%d)", - rawlspid_print(lsp->hdr.lsp_id), - queue->circuit->interface->name, + zlog_debug("Remove LSP %pLS from %s queue. (From %s %s:%d)", + lsp->hdr.lsp_id, queue->circuit->interface->name, func, file, line); } diff --git a/isisd/isis_zebra.c b/isisd/isis_zebra.c index 8cd8f57c47..59b80c1e20 100644 --- a/isisd/isis_zebra.c +++ b/isisd/isis_zebra.c @@ -327,14 +327,13 @@ void isis_zebra_route_del_route(struct isis *isis, */ void isis_zebra_prefix_sid_install(struct isis_area *area, struct prefix *prefix, - struct isis_route_info *rinfo, struct isis_sr_psid_info *psid) { struct zapi_labels zl; int count = 0; - sr_debug("ISIS-Sr (%s): update label %u for prefix %pFX", - area->area_tag, psid->label, prefix); + sr_debug("ISIS-Sr (%s): update label %u for prefix %pFX algorithm %u", + area->area_tag, psid->label, prefix, psid->algorithm); /* Prepare message. */ memset(&zl, 0, sizeof(zl)); @@ -342,7 +341,7 @@ void isis_zebra_prefix_sid_install(struct isis_area *area, zl.local_label = psid->label; /* Local routes don't have any nexthop and require special handling. */ - if (list_isempty(rinfo->nexthops)) { + if (list_isempty(psid->nexthops)) { struct zapi_nexthop *znh; struct interface *ifp; @@ -361,9 +360,9 @@ void isis_zebra_prefix_sid_install(struct isis_area *area, znh->labels[0] = MPLS_LABEL_IMPLICIT_NULL; } else { /* Add backup nexthops first. */ - if (rinfo->backup) { + if (psid->nexthops_backup) { count = isis_zebra_add_nexthops( - area->isis, rinfo->backup->nexthops, + area->isis, psid->nexthops_backup, zl.backup_nexthops, ISIS_NEXTHOP_BACKUP, true, 0); if (count > 0) { @@ -373,7 +372,7 @@ void isis_zebra_prefix_sid_install(struct isis_area *area, } /* Add primary nexthops. */ - count = isis_zebra_add_nexthops(area->isis, rinfo->nexthops, + count = isis_zebra_add_nexthops(area->isis, psid->nexthops, zl.nexthops, ISIS_NEXTHOP_MAIN, true, count); if (!count) @@ -400,8 +399,8 @@ void isis_zebra_prefix_sid_uninstall(struct isis_area *area, { struct zapi_labels zl; - sr_debug("ISIS-Sr (%s): delete label %u for prefix %pFX", - area->area_tag, psid->label, prefix); + sr_debug("ISIS-Sr (%s): delete label %u for prefix %pFX algorithm %u", + area->area_tag, psid->label, prefix, psid->algorithm); /* Prepare message. */ memset(&zl, 0, sizeof(zl)); diff --git a/isisd/isis_zebra.h b/isisd/isis_zebra.h index 359e39b59d..045c75874a 100644 --- a/isisd/isis_zebra.h +++ b/isisd/isis_zebra.h @@ -36,7 +36,6 @@ void isis_zebra_route_del_route(struct isis *isis, struct isis_route_info *route_info); void isis_zebra_prefix_sid_install(struct isis_area *area, struct prefix *prefix, - struct isis_route_info *rinfo, struct isis_sr_psid_info *psid); void isis_zebra_prefix_sid_uninstall(struct isis_area *area, struct prefix *prefix, diff --git a/isisd/isisd.c b/isisd/isisd.c index c6369a884e..f65d21405b 100644 --- a/isisd/isisd.c +++ b/isisd/isisd.c @@ -27,6 +27,7 @@ #include "zclient.h" #include "vrf.h" #include "spf_backoff.h" +#include "flex_algo.h" #include "lib/northbound_cli.h" #include "bfd.h" @@ -49,6 +50,7 @@ #include "isisd/isis_te.h" #include "isisd/isis_mt.h" #include "isisd/isis_sr.h" +#include "isisd/isis_flex_algo.h" #include "isisd/fabricd.h" #include "isisd/isis_nb.h" @@ -272,7 +274,7 @@ void isis_area_del_circuit(struct isis_area *area, struct isis_circuit *circuit) static void delete_area_addr(void *arg) { - struct area_addr *addr = (struct area_addr *)arg; + struct iso_address *addr = (struct iso_address *)arg; XFREE(MTYPE_ISIS_AREA_ADDR, addr); } @@ -317,6 +319,12 @@ struct isis_area *isis_area_create(const char *area_tag, const char *vrf_name) if (area->is_type & IS_LEVEL_2) lsp_db_init(&area->lspdb[1]); +#ifndef FABRICD + /* Flex-Algo */ + area->flex_algos = flex_algos_alloc(isis_flex_algo_data_alloc, + isis_flex_algo_data_free); +#endif /* ifndef FABRICD */ + spftree_area_init(area); area->circuit_list = list_new(); @@ -809,8 +817,8 @@ static void area_set_mt_overload(struct isis_area *area, uint16_t mtid, int area_net_title(struct vty *vty, const char *net_title) { VTY_DECLVAR_CONTEXT(isis_area, area); - struct area_addr *addr; - struct area_addr *addrp; + struct iso_address *addr; + struct iso_address *addrp; struct listnode *node; uint8_t buff[255]; @@ -823,14 +831,14 @@ int area_net_title(struct vty *vty, const char *net_title) return CMD_ERR_NOTHING_TODO; } - addr = XMALLOC(MTYPE_ISIS_AREA_ADDR, sizeof(struct area_addr)); + addr = XMALLOC(MTYPE_ISIS_AREA_ADDR, sizeof(struct iso_address)); addr->addr_len = dotformat2buff(buff, net_title); memcpy(addr->area_addr, buff, addr->addr_len); #ifdef EXTREME_DEBUG zlog_debug("added area address %s for area %s (address length %d)", net_title, area->area_tag, addr->addr_len); #endif /* EXTREME_DEBUG */ - if (addr->addr_len < 8 || addr->addr_len > 20) { + if (addr->addr_len < ISO_ADDR_MIN || addr->addr_len > ISO_ADDR_SIZE) { vty_out(vty, "area address must be at least 8..20 octets long (%d)\n", addr->addr_len); @@ -852,8 +860,8 @@ int area_net_title(struct vty *vty, const char *net_title) memcpy(area->isis->sysid, GETSYSID(addr), ISIS_SYS_ID_LEN); area->isis->sysid_set = 1; if (IS_DEBUG_EVENTS) - zlog_debug("Router has SystemID %s", - sysid_print(area->isis->sysid)); + zlog_debug("Router has SystemID %pSY", + area->isis->sysid); } else { /* * Check that the SystemID portions match @@ -899,12 +907,12 @@ int area_net_title(struct vty *vty, const char *net_title) int area_clear_net_title(struct vty *vty, const char *net_title) { VTY_DECLVAR_CONTEXT(isis_area, area); - struct area_addr addr, *addrp = NULL; + struct iso_address addr, *addrp = NULL; struct listnode *node; uint8_t buff[255]; addr.addr_len = dotformat2buff(buff, net_title); - if (addr.addr_len < 8 || addr.addr_len > 20) { + if (addr.addr_len < ISO_ADDR_MIN || addr.addr_len > ISO_ADDR_SIZE) { vty_out(vty, "Unsupported area address length %d, should be 8...20 \n", addr.addr_len); @@ -2348,11 +2356,11 @@ static void common_isis_summary_json(struct json_object *json, time_t cur; char uptime[MONOTIME_STRLEN]; char stier[5]; + json_object_string_add(json, "vrf", isis->name); json_object_int_add(json, "process-id", isis->process_id); if (isis->sysid_set) - json_object_string_add(json, "system-id", - sysid_print(isis->sysid)); + json_object_string_addf(json, "system-id", "%pSY", isis->sysid); cur = time(NULL); cur -= isis->uptime; @@ -2380,16 +2388,11 @@ static void common_isis_summary_json(struct json_object *json, } if (listcount(area->area_addrs) > 0) { - struct area_addr *area_addr; + struct iso_address *area_addr; for (ALL_LIST_ELEMENTS_RO(area->area_addrs, node2, - area_addr)) { - json_object_string_add( - area_json, "net", - isonet_print(area_addr->area_addr, - area_addr->addr_len + - ISIS_SYS_ID_LEN + - 1)); - } + area_addr)) + json_object_string_addf(area_json, "net", + "%pISl", area_addr); } tx_pdu_json = json_object_new_object(); @@ -2462,8 +2465,7 @@ static void common_isis_summary_vty(struct vty *vty, struct isis *isis) vty_out(vty, "vrf : %s\n", isis->name); vty_out(vty, "Process Id : %ld\n", isis->process_id); if (isis->sysid_set) - vty_out(vty, "System Id : %s\n", - sysid_print(isis->sysid)); + vty_out(vty, "System Id : %pSY\n", isis->sysid); vty_out(vty, "Up time : "); vty_out_timestr(vty, isis->uptime); @@ -2485,15 +2487,10 @@ static void common_isis_summary_vty(struct vty *vty, struct isis *isis) } if (listcount(area->area_addrs) > 0) { - struct area_addr *area_addr; + struct iso_address *area_addr; for (ALL_LIST_ELEMENTS_RO(area->area_addrs, node2, - area_addr)) { - vty_out(vty, " Net: %s\n", - isonet_print(area_addr->area_addr, - area_addr->addr_len - + ISIS_SYS_ID_LEN - + 1)); - } + area_addr)) + vty_out(vty, " Net: %pISl\n", area_addr); } vty_out(vty, " TX counters per PDU type:\n"); @@ -3072,12 +3069,27 @@ int isis_area_passwd_hmac_md5_set(struct isis_area *area, int level, void isis_area_invalidate_routes(struct isis_area *area, int levels) { +#ifndef FABRICD + struct flex_algo *fa; + struct listnode *node; + struct isis_flex_algo_data *data; +#endif /* ifndef FABRICD */ + for (int level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) { if (!(level & levels)) continue; for (int tree = SPFTREE_IPV4; tree < SPFTREE_COUNT; tree++) { isis_spf_invalidate_routes( area->spftree[tree][level - 1]); + +#ifndef FABRICD + for (ALL_LIST_ELEMENTS_RO(area->flex_algos->flex_algos, + node, fa)) { + data = fa->data; + isis_spf_invalidate_routes( + data->spftree[tree][level - 1]); + } +#endif /* ifndef FABRICD */ } } } @@ -3085,7 +3097,7 @@ void isis_area_invalidate_routes(struct isis_area *area, int levels) void isis_area_verify_routes(struct isis_area *area) { for (int tree = SPFTREE_IPV4; tree < SPFTREE_COUNT; tree++) - isis_spf_verify_routes(area, area->spftree[tree]); + isis_spf_verify_routes(area, area->spftree[tree], tree); } void isis_area_switchover_routes(struct isis_area *area, int family, @@ -3109,6 +3121,12 @@ void isis_area_switchover_routes(struct isis_area *area, int family, static void area_resign_level(struct isis_area *area, int level) { +#ifndef FABRICD + struct flex_algo *fa; + struct listnode *node; + struct isis_flex_algo_data *data; +#endif /* ifndef FABRICD */ + isis_area_invalidate_routes(area, level); isis_area_verify_routes(area); @@ -3121,6 +3139,20 @@ static void area_resign_level(struct isis_area *area, int level) } } +#ifndef FABRICD + for (int tree = SPFTREE_IPV4; tree < SPFTREE_COUNT; tree++) { + for (ALL_LIST_ELEMENTS_RO(area->flex_algos->flex_algos, node, + fa)) { + data = fa->data; + if (data->spftree[level - 1]) { + isis_spftree_del( + data->spftree[tree][level - 1]); + data->spftree[tree][level - 1] = NULL; + } + } + } +#endif /* ifndef FABRICD */ + if (area->spf_timer[level - 1]) isis_spf_timer_free(EVENT_ARG(area->spf_timer[level - 1])); @@ -3497,15 +3529,10 @@ static int isis_config_write(struct vty *vty) write++; /* ISIS - Net */ if (listcount(area->area_addrs) > 0) { - struct area_addr *area_addr; + struct iso_address *area_addr; for (ALL_LIST_ELEMENTS_RO(area->area_addrs, node2, area_addr)) { - vty_out(vty, " net %s\n", - isonet_print( - area_addr->area_addr, - area_addr->addr_len - + ISIS_SYS_ID_LEN - + 1)); + vty_out(vty, " net %pISl\n", area_addr); write++; } } @@ -3764,7 +3791,8 @@ struct cmd_node router_node = { .prompt = "%s(config-router)# ", .config_write = isis_config_write, }; -#else +#endif /* ifdef FABRICD */ +#ifndef FABRICD /* IS-IS configuration write function */ static int isis_config_write(struct vty *vty) { @@ -3787,7 +3815,14 @@ struct cmd_node router_node = { .prompt = "%s(config-router)# ", .config_write = isis_config_write, }; -#endif /* ifdef FABRICD */ + +struct cmd_node isis_flex_algo_node = { + .name = "isis-flex-algo", + .node = ISIS_FLEX_ALGO_NODE, + .parent_node = ISIS_NODE, + .prompt = "%s(config-router-flex-algo)# ", +}; +#endif /* ifdnef FABRICD */ void isis_init(void) { @@ -3896,6 +3931,10 @@ void isis_init(void) install_element(ROUTER_NODE, &log_adj_changes_cmd); install_element(ROUTER_NODE, &no_log_adj_changes_cmd); #endif /* ifdef FABRICD */ +#ifndef FABRICD + install_node(&isis_flex_algo_node); + install_default(ISIS_FLEX_ALGO_NODE); +#endif /* ifdnef FABRICD */ spf_backoff_cmd_init(); } diff --git a/isisd/isisd.h b/isisd/isisd.h index 0b1f1cb620..f0d236b643 100644 --- a/isisd/isisd.h +++ b/isisd/isisd.h @@ -24,6 +24,7 @@ #include "isis_lfa.h" #include "qobj.h" #include "ldp_sync.h" +#include "iso.h" DECLARE_MGROUP(ISISD); @@ -87,7 +88,7 @@ struct isis { uint32_t router_id; /* Router ID from zebra */ struct list *area_list; /* list of IS-IS areas */ uint8_t max_area_addrs; /* maximumAreaAdresses */ - struct area_addr *man_area_addrs; /* manualAreaAddresses */ + struct iso_address *man_area_addrs; /* manualAreaAddresses */ time_t uptime; /* when did we start */ struct event *t_dync_clean; /* dynamic hostname cache cleanup thread */ uint32_t circuit_ids_used[8]; /* 256 bits to track circuit ids 1 through 255 */ @@ -162,6 +163,10 @@ struct isis_area { /* do we support new style metrics? */ char newmetric; char oldmetric; + /* Allow sending the default admin-group value of 0x00000000. */ + bool admin_group_send_zero; + /* Set the legacy flag (aka. L-FLAG) in the ASLA Sub-TLV */ + bool asla_legacy_flag; /* identifies the routing instance */ char *area_tag; /* area addresses for this area */ @@ -195,6 +200,8 @@ struct isis_area { int ip_circuits; /* logging adjacency changes? */ uint8_t log_adj_changes; + /* logging pdu drops? */ + uint8_t log_pdu_drops; /* multi topology settings */ struct list *mt_settings; /* MPLS-TE settings */ @@ -217,6 +224,10 @@ struct isis_area { size_t tilfa_protected_links[ISIS_LEVELS]; /* MPLS LDP-IGP Sync */ struct ldp_sync_info_cmd ldp_sync_cmd; +#ifndef FABRICD + /* Flex-Algo */ + struct flex_algos *flex_algos; +#endif /* ifndef FABRICD */ /* Counters */ uint32_t circuit_state_changes; struct isis_redist redist_settings[REDIST_PROTOCOL_COUNT] diff --git a/isisd/subdir.am b/isisd/subdir.am index dabf6a925e..6bd2477b19 100644 --- a/isisd/subdir.am +++ b/isisd/subdir.am @@ -19,6 +19,7 @@ vtysh_daemons += fabricd endif noinst_HEADERS += \ + isisd/isis_affinitymap.h \ isisd/isis_adjacency.h \ isisd/isis_bfd.h \ isisd/isis_circuit.h \ @@ -45,6 +46,7 @@ noinst_HEADERS += \ isisd/isis_spf.h \ isisd/isis_spf_private.h \ isisd/isis_sr.h \ + isisd/isis_flex_algo.h \ isisd/isis_te.h \ isisd/isis_tlvs.h \ isisd/isis_tx_queue.h \ @@ -55,6 +57,7 @@ noinst_HEADERS += \ # end LIBISIS_SOURCES = \ + isisd/isis_affinitymap.c \ isisd/isis_adjacency.c \ isisd/isis_bfd.c \ isisd/isis_circuit.c \ @@ -76,6 +79,7 @@ LIBISIS_SOURCES = \ isisd/isis_routemap.c \ isisd/isis_spf.c \ isisd/isis_sr.c \ + isisd/isis_flex_algo.c \ isisd/isis_te.c \ isisd/isis_tlvs.c \ isisd/isis_tx_queue.c \ diff --git a/lib/command.h b/lib/command.h index 8856f9f09f..39fbfa661a 100644 --- a/lib/command.h +++ b/lib/command.h @@ -129,6 +129,7 @@ enum node_type { LDP_L2VPN_NODE, /* LDP L2VPN node */ LDP_PSEUDOWIRE_NODE, /* LDP Pseudowire node */ ISIS_NODE, /* ISIS protocol mode */ + ISIS_FLEX_ALGO_NODE, /* ISIS Flex Algo mode */ ACCESS_NODE, /* Access list node. */ PREFIX_NODE, /* Prefix list node. */ ACCESS_IPV6_NODE, /* Access list node. */ diff --git a/lib/compiler.h b/lib/compiler.h index d12e282832..29fcfbefbf 100644 --- a/lib/compiler.h +++ b/lib/compiler.h @@ -439,6 +439,14 @@ _Static_assert(sizeof(_uint64_t) == 8 && sizeof(_int64_t) == 8, #pragma diag_suppress 167 #endif /* __INTELISENSE__ */ +#if defined(__GNUC__) && (__GNUC__ >= 3) +#define likely(_x) __builtin_expect(!!(_x), 1) +#define unlikely(_x) __builtin_expect(!!(_x), 0) +#else +#define likely(_x) !!(_x) +#define unlikely(_x) !!(_x) +#endif + #ifdef __cplusplus } #endif diff --git a/lib/cspf.c b/lib/cspf.c index b92c9cb395..6a0fb7f63c 100644 --- a/lib/cspf.c +++ b/lib/cspf.c @@ -88,7 +88,7 @@ static struct c_path *cpath_copy(struct c_path *dest, const struct c_path *src) * * @param path Constrained Path structure to be deleted */ -static void cpath_del(struct c_path *path) +void cpath_del(struct c_path *path) { if (!path) return; diff --git a/lib/cspf.h b/lib/cspf.h index 3eceaa04af..bba685a617 100644 --- a/lib/cspf.h +++ b/lib/cspf.h @@ -191,6 +191,8 @@ extern void cspf_del(struct cspf *algo); */ extern struct c_path *compute_p2p_path(struct cspf *algo, struct ls_ted *ted); +extern void cpath_del(struct c_path *path); + #ifdef __cplusplus } #endif diff --git a/lib/flex_algo.c b/lib/flex_algo.c new file mode 100644 index 0000000000..bafbf8b779 --- /dev/null +++ b/lib/flex_algo.c @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/********************************************************************* + * Copyright 2022 Hiroki Shirokura, LINE Corporation + * Copyright 2022 Masakazu Asama + * Copyright 2022 6WIND S.A. + * + * flex_algo.c: Flexible Algorithm library + * + * Authors + * ------- + * Hiroki Shirokura + * Masakazu Asama + * Louis Scalbert + */ + +#include "zebra.h" + +#include "flex_algo.h" + +DEFINE_MTYPE_STATIC(LIB, FLEX_ALGO, "Flex-Algo Definition"); + +struct flex_algos *flex_algos_alloc(flex_algo_allocator_t allocator, + flex_algo_releaser_t releaser) +{ + struct flex_algos *flex_algos; + + flex_algos = XCALLOC(MTYPE_FLEX_ALGO, sizeof(*flex_algos)); + flex_algos->flex_algos = list_new(); + flex_algos->allocator = allocator; + flex_algos->releaser = releaser; + return flex_algos; +} + +struct flex_algo *flex_algo_alloc(struct flex_algos *flex_algos, + uint8_t algorithm, void *arg) +{ + struct flex_algo *fa; + + fa = XCALLOC(MTYPE_FLEX_ALGO, sizeof(*fa)); + fa->algorithm = algorithm; + if (flex_algos->allocator) + fa->data = flex_algos->allocator(arg); + admin_group_init(&fa->admin_group_exclude_any); + admin_group_init(&fa->admin_group_include_any); + admin_group_init(&fa->admin_group_include_all); + listnode_add(flex_algos->flex_algos, fa); + return fa; +} + +/** + * @brief Look up the local flex-algo object by its algorithm number. + * @param algorithm flex-algo algorithm number + * @param area area pointer of flex-algo + * @return local flex-algo object if exist, else NULL + */ +struct flex_algo *flex_algo_lookup(struct flex_algos *flex_algos, + uint8_t algorithm) +{ + struct listnode *node; + struct flex_algo *fa; + + for (ALL_LIST_ELEMENTS_RO(flex_algos->flex_algos, node, fa)) + if (fa->algorithm == algorithm) + return fa; + return NULL; +} + +/** + * @brief Compare two Flex-Algo Definitions (FAD) + * @param Flex algo 1 + * @param Flex algo 2 + * @return true if the definition is equal, else false + */ +bool flex_algo_definition_cmp(struct flex_algo *fa1, struct flex_algo *fa2) +{ + if (fa1->algorithm != fa2->algorithm) + return false; + if (fa1->calc_type != fa2->calc_type) + return false; + if (fa1->metric_type != fa2->metric_type) + return false; + + if (!admin_group_cmp(&fa1->admin_group_exclude_any, + &fa2->admin_group_exclude_any)) + return false; + if (!admin_group_cmp(&fa1->admin_group_include_all, + &fa2->admin_group_include_all)) + return false; + if (!admin_group_cmp(&fa1->admin_group_include_any, + &fa2->admin_group_include_any)) + return false; + + return true; +} + +void flex_algo_delete(struct flex_algos *flex_algos, uint8_t algorithm) +{ + struct listnode *node, *nnode; + struct flex_algo *fa; + + for (ALL_LIST_ELEMENTS(flex_algos->flex_algos, node, nnode, fa)) { + if (fa->algorithm != algorithm) + continue; + if (flex_algos->releaser) + flex_algos->releaser(fa->data); + admin_group_term(&fa->admin_group_exclude_any); + admin_group_term(&fa->admin_group_include_any); + admin_group_term(&fa->admin_group_include_all); + listnode_delete(flex_algos->flex_algos, fa); + XFREE(MTYPE_FLEX_ALGO, fa); + return; + } +} + +/** + * Check SR Algorithm is Flex-Algo + * according to RFC9350 section 4 + * + * @param algorithm SR Algorithm + */ +bool flex_algo_id_valid(uint16_t algorithm) +{ + return algorithm >= SR_ALGORITHM_FLEX_MIN && + algorithm <= SR_ALGORITHM_FLEX_MAX; +} + +char *flex_algo_metric_type_print(char *type_str, size_t sz, + enum flex_algo_metric_type metric_type) +{ + switch (metric_type) { + case MT_IGP: + snprintf(type_str, sz, "igp"); + break; + case MT_MIN_UNI_LINK_DELAY: + snprintf(type_str, sz, "delay"); + break; + case MT_TE_DEFAULT: + snprintf(type_str, sz, "te"); + break; + } + return type_str; +} diff --git a/lib/flex_algo.h b/lib/flex_algo.h new file mode 100644 index 0000000000..e012f46862 --- /dev/null +++ b/lib/flex_algo.h @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/********************************************************************* + * Copyright 2022 Hiroki Shirokura, LINE Corporation + * Copyright 2022 Masakazu Asama + * Copyright 2022 6WIND S.A. + * + * flex_algo.h: Flexible Algorithm library + * + * Authors + * ------- + * Hiroki Shirokura + * Masakazu Asama + * Louis Scalbert + */ + +#ifndef _FRR_FLEX_ALGO_H +#define _FRR_FLEX_ALGO_H + +#include "admin_group.h" +#include "linklist.h" +#include "prefix.h" +#include "segment_routing.h" + +#define FLEX_ALGO_PRIO_DEFAULT 128 + +#define CALC_TYPE_SPF 0 + +/* flex-algo definition flags */ + +/* M-flag (aka. prefix-metric) + * Flex-Algorithm specific prefix and ASBR metric MUST be used + */ +#define FAD_FLAG_M 0x80 + +/* + * Metric Type values from RFC9350 section 5.1 + */ +enum flex_algo_metric_type { + MT_IGP = 0, + MT_MIN_UNI_LINK_DELAY = 1, + MT_TE_DEFAULT = 2, +}; + + +/* Flex-Algo data about a given algorithm. + * It includes the definition and some local data. + */ +struct flex_algo { + /* Flex-Algo definition */ + uint8_t algorithm; + enum flex_algo_metric_type metric_type; + uint8_t calc_type; + uint8_t priority; + uint8_t flags; + + /* extended admin-groups */ + struct admin_group admin_group_exclude_any; + struct admin_group admin_group_include_any; + struct admin_group admin_group_include_all; + + /* Exclude SRLG Sub-TLV is not yet supported by IS-IS + * True if a Exclude SRLG Sub-TLV has been found + */ + bool exclude_srlg; + + /* True if an unsupported sub-TLV other Exclude SRLG + * has been received. + * A router that receives an unsupported definition + * that is elected must not participate in the algorithm. + * This boolean prevents future sub-TLV from being considered + * as supported. + */ + bool unsupported_subtlv; + + /* Flex-Algo local data */ + + /* True if the local definition must be advertised */ + bool advertise_definition; + + /* which dataplane must be used for the algorithm */ +#define FLEX_ALGO_SR_MPLS 0x01 +#define FLEX_ALGO_SRV6 0x02 +#define FLEX_ALGO_IP 0x04 + uint8_t dataplanes; + + /* + * This property can be freely extended among different routing + * protocols. Since Flex-Algo is an IGP protocol agnostic, both IS-IS + * and OSPF can implement Flex-Algo. The struct flex_algo thus provides + * the general data structure of Flex-Algo, and the value of extending + * it with the IGP protocol is provided by this property. + */ + void *data; +}; + +typedef void *(*flex_algo_allocator_t)(void *); +typedef void (*flex_algo_releaser_t)(void *); + +struct flex_algos { + flex_algo_allocator_t allocator; + flex_algo_releaser_t releaser; + struct list *flex_algos; +}; + +/* + * Flex-Algo Utilities + */ +struct flex_algos *flex_algos_alloc(flex_algo_allocator_t allocator, + flex_algo_releaser_t releaser); +struct flex_algo *flex_algo_alloc(struct flex_algos *flex_algos, + uint8_t algorithm, void *arg); +struct flex_algo *flex_algo_lookup(struct flex_algos *flex_algos, + uint8_t algorithm); +void flex_algos_free(struct flex_algos *flex_algos); +bool flex_algo_definition_cmp(struct flex_algo *fa1, struct flex_algo *fa2); +void flex_algo_delete(struct flex_algos *flex_algos, uint8_t algorithm); +bool flex_algo_id_valid(uint16_t algorithm); +char *flex_algo_metric_type_print(char *type_str, size_t sz, + enum flex_algo_metric_type metric_type); + +#endif /* _FRR_FLEX_ALGO_H */ diff --git a/lib/if_rmap.c b/lib/if_rmap.c index 27c41aaa27..42e162072f 100644 --- a/lib/if_rmap.c +++ b/lib/if_rmap.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* route-map for interface. * Copyright (C) 1999 Kunihiro Ishiguro + * Copyright (C) 2023 LabN Consulting, L.L.C. */ #include <zebra.h> @@ -10,6 +11,9 @@ #include "memory.h" #include "if.h" #include "if_rmap.h" +#include "northbound_cli.h" + +#include "lib/if_rmap_clippy.c" DEFINE_MTYPE_STATIC(LIB, IF_RMAP_CTX, "Interface route map container"); DEFINE_MTYPE_STATIC(LIB, IF_RMAP_CTX_NAME, @@ -17,8 +21,6 @@ DEFINE_MTYPE_STATIC(LIB, IF_RMAP_CTX_NAME, DEFINE_MTYPE_STATIC(LIB, IF_RMAP, "Interface route map"); DEFINE_MTYPE_STATIC(LIB, IF_RMAP_NAME, "I.f. route map name"); -static struct list *if_rmap_ctx_list; - static struct if_rmap *if_rmap_new(void) { struct if_rmap *new; @@ -30,7 +32,9 @@ static struct if_rmap *if_rmap_new(void) static void if_rmap_free(struct if_rmap *if_rmap) { - XFREE(MTYPE_IF_RMAP_NAME, if_rmap->ifname); + char *no_const_ifname = (char *)if_rmap->ifname; + + XFREE(MTYPE_IF_RMAP_NAME, no_const_ifname); XFREE(MTYPE_IF_RMAP_NAME, if_rmap->routemap[IF_RMAP_IN]); XFREE(MTYPE_IF_RMAP_NAME, if_rmap->routemap[IF_RMAP_OUT]); @@ -40,22 +44,16 @@ static void if_rmap_free(struct if_rmap *if_rmap) struct if_rmap *if_rmap_lookup(struct if_rmap_ctx *ctx, const char *ifname) { - struct if_rmap key; + struct if_rmap key = {.ifname = ifname}; struct if_rmap *if_rmap; - /* temporary copy */ - key.ifname = (ifname) ? XSTRDUP(MTYPE_IF_RMAP_NAME, ifname) : NULL; - if_rmap = hash_lookup(ctx->ifrmaphash, &key); - XFREE(MTYPE_IF_RMAP_NAME, key.ifname); - return if_rmap; } void if_rmap_hook_add(struct if_rmap_ctx *ctx, - void (*func)(struct if_rmap_ctx *ctx, - struct if_rmap *)) + void (*func)(struct if_rmap_ctx *ctx, struct if_rmap *)) { ctx->if_rmap_add_hook = func; } @@ -80,16 +78,11 @@ static void *if_rmap_hash_alloc(void *arg) static struct if_rmap *if_rmap_get(struct if_rmap_ctx *ctx, const char *ifname) { - struct if_rmap key; + struct if_rmap key = {.ifname = ifname}; struct if_rmap *ret; - /* temporary copy */ - key.ifname = (ifname) ? XSTRDUP(MTYPE_IF_RMAP_NAME, ifname) : NULL; - ret = hash_get(ctx->ifrmaphash, &key, if_rmap_hash_alloc); - XFREE(MTYPE_IF_RMAP_NAME, key.ifname); - return ret; } @@ -108,177 +101,184 @@ static bool if_rmap_hash_cmp(const void *arg1, const void *arg2) return strcmp(if_rmap1->ifname, if_rmap2->ifname) == 0; } -static struct if_rmap *if_rmap_set(struct if_rmap_ctx *ctx, - const char *ifname, enum if_rmap_type type, - const char *routemap_name) +static void if_rmap_set(struct if_rmap_ctx *ctx, const char *ifname, + enum if_rmap_type type, const char *routemap_name) { - struct if_rmap *if_rmap; - - if_rmap = if_rmap_get(ctx, ifname); + struct if_rmap *if_rmap = if_rmap_get(ctx, ifname); - if (type == IF_RMAP_IN) { - XFREE(MTYPE_IF_RMAP_NAME, if_rmap->routemap[IF_RMAP_IN]); - if_rmap->routemap[IF_RMAP_IN] = - XSTRDUP(MTYPE_IF_RMAP_NAME, routemap_name); - } - if (type == IF_RMAP_OUT) { - XFREE(MTYPE_IF_RMAP_NAME, if_rmap->routemap[IF_RMAP_OUT]); - if_rmap->routemap[IF_RMAP_OUT] = - XSTRDUP(MTYPE_IF_RMAP_NAME, routemap_name); - } + assert(type == IF_RMAP_IN || type == IF_RMAP_OUT); + XFREE(MTYPE_IF_RMAP_NAME, if_rmap->routemap[type]); + if_rmap->routemap[type] = XSTRDUP(MTYPE_IF_RMAP_NAME, routemap_name); if (ctx->if_rmap_add_hook) (ctx->if_rmap_add_hook)(ctx, if_rmap); - - return if_rmap; } -static int if_rmap_unset(struct if_rmap_ctx *ctx, - const char *ifname, enum if_rmap_type type, - const char *routemap_name) +static void if_rmap_unset(struct if_rmap_ctx *ctx, const char *ifname, + enum if_rmap_type type) { - struct if_rmap *if_rmap; + struct if_rmap *if_rmap = if_rmap_lookup(ctx, ifname); - if_rmap = if_rmap_lookup(ctx, ifname); if (!if_rmap) - return 0; - - if (type == IF_RMAP_IN) { - if (!if_rmap->routemap[IF_RMAP_IN]) - return 0; - if (strcmp(if_rmap->routemap[IF_RMAP_IN], routemap_name) != 0) - return 0; - - XFREE(MTYPE_IF_RMAP_NAME, if_rmap->routemap[IF_RMAP_IN]); - } + return; - if (type == IF_RMAP_OUT) { - if (!if_rmap->routemap[IF_RMAP_OUT]) - return 0; - if (strcmp(if_rmap->routemap[IF_RMAP_OUT], routemap_name) != 0) - return 0; + assert(type == IF_RMAP_IN || type == IF_RMAP_OUT); + if (!if_rmap->routemap[type]) + return; - XFREE(MTYPE_IF_RMAP_NAME, if_rmap->routemap[IF_RMAP_OUT]); - } + XFREE(MTYPE_IF_RMAP_NAME, if_rmap->routemap[type]); if (ctx->if_rmap_delete_hook) ctx->if_rmap_delete_hook(ctx, if_rmap); - if (if_rmap->routemap[IF_RMAP_IN] == NULL - && if_rmap->routemap[IF_RMAP_OUT] == NULL) { + if (if_rmap->routemap[IF_RMAP_IN] == NULL && + if_rmap->routemap[IF_RMAP_OUT] == NULL) { hash_release(ctx->ifrmaphash, if_rmap); if_rmap_free(if_rmap); } - - return 1; } -DEFUN (if_rmap, - if_rmap_cmd, - "route-map RMAP_NAME <in|out> IFNAME", - "Route map set\n" - "Route map name\n" - "Route map set for input filtering\n" - "Route map set for output filtering\n" - "Route map interface name\n") +static int if_route_map_handler(struct vty *vty, bool no, const char *dir, + const char *other_dir, const char *ifname, + const char *route_map) { - int idx_rmap_name = 1; - int idx_in_out = 2; - int idx_ifname = 3; - enum if_rmap_type type; - struct if_rmap_ctx *ctx = - (struct if_rmap_ctx *)listnode_head(if_rmap_ctx_list); - - if (strncmp(argv[idx_in_out]->text, "in", 1) == 0) - type = IF_RMAP_IN; - else if (strncmp(argv[idx_in_out]->text, "out", 1) == 0) - type = IF_RMAP_OUT; - else { - vty_out(vty, "route-map direction must be [in|out]\n"); - return CMD_WARNING_CONFIG_FAILED; + enum nb_operation op = no ? NB_OP_DESTROY : NB_OP_MODIFY; + const struct lyd_node *dnode; + char xpath[XPATH_MAXLEN]; + + if (!no) { + snprintf( + xpath, sizeof(xpath), + "./if-route-maps/if-route-map[interface='%s']/%s-route-map", + ifname, dir); + } else { + /* + * If we are deleting the last policy for this interface, + * (i.e., no `in` or `out` policy). delete the interface list + * node instead. + */ + dnode = yang_dnode_get(vty->candidate_config->dnode, + VTY_CURR_XPATH); + if (yang_dnode_existsf( + dnode, + "./if-route-maps/if-route-map[interface='%s']/%s-route-map", + ifname, other_dir)) { + snprintf( + xpath, sizeof(xpath), + "./if-route-maps/if-route-map[interface='%s']/%s-route-map", + ifname, dir); + } else { + /* both dir will be empty so delete the list node */ + snprintf(xpath, sizeof(xpath), + "./if-route-maps/if-route-map[interface='%s']", + ifname); + } } + nb_cli_enqueue_change(vty, xpath, op, route_map); - if_rmap_set(ctx, argv[idx_ifname]->arg, - type, argv[idx_rmap_name]->arg); + return nb_cli_apply_changes(vty, NULL); +} - return CMD_SUCCESS; +DEFPY_YANG(if_ipv4_route_map, if_ipv4_route_map_cmd, + "route-map ROUTE-MAP <in$in|out> IFNAME", + "Route map set\n" + "Route map name\n" + "Route map set for input filtering\n" + "Route map set for output filtering\n" INTERFACE_STR) +{ + const char *dir = in ? "in" : "out"; + const char *other_dir = in ? "out" : "in"; + + return if_route_map_handler(vty, false, dir, other_dir, ifname, + route_map); } -DEFUN (no_if_rmap, - no_if_rmap_cmd, - "no route-map ROUTEMAP_NAME <in|out> IFNAME", - NO_STR - "Route map unset\n" - "Route map name\n" - "Route map for input filtering\n" - "Route map for output filtering\n" - "Route map interface name\n") +DEFPY_YANG(no_if_ipv4_route_map, no_if_ipv4_route_map_cmd, + "no route-map [ROUTE-MAP] <in$in|out> IFNAME", + NO_STR + "Route map set\n" + "Route map name\n" + "Route map set for input filtering\n" + "Route map set for output filtering\n" INTERFACE_STR) { - int idx_routemap_name = 2; - int idx_in_out = 3; - int idx_ifname = 4; - int ret; - enum if_rmap_type type; - struct if_rmap_ctx *ctx = - (struct if_rmap_ctx *)listnode_head(if_rmap_ctx_list); - - if (strncmp(argv[idx_in_out]->arg, "i", 1) == 0) - type = IF_RMAP_IN; - else if (strncmp(argv[idx_in_out]->arg, "o", 1) == 0) - type = IF_RMAP_OUT; - else { - vty_out(vty, "route-map direction must be [in|out]\n"); - return CMD_WARNING_CONFIG_FAILED; - } + const char *dir = in ? "in" : "out"; + const char *other_dir = in ? "out" : "in"; - ret = if_rmap_unset(ctx, argv[idx_ifname]->arg, type, - argv[idx_routemap_name]->arg); - if (!ret) { - vty_out(vty, "route-map doesn't exist\n"); - return CMD_WARNING_CONFIG_FAILED; - } - return CMD_SUCCESS; + return if_route_map_handler(vty, true, dir, other_dir, ifname, + route_map); } +/* + * CLI infra requires new handlers for ripngd + */ +DEFPY_YANG(if_ipv6_route_map, if_ipv6_route_map_cmd, + "route-map ROUTE-MAP <in$in|out> IFNAME", + "Route map set\n" + "Route map name\n" + "Route map set for input filtering\n" + "Route map set for output filtering\n" INTERFACE_STR) +{ + const char *dir = in ? "in" : "out"; + const char *other_dir = in ? "out" : "in"; -/* Configuration write function. */ -int config_write_if_rmap(struct vty *vty, - struct if_rmap_ctx *ctx) + return if_route_map_handler(vty, false, dir, other_dir, ifname, + route_map); +} + +DEFPY_YANG(no_if_ipv6_route_map, no_if_ipv6_route_map_cmd, + "no route-map [ROUTE-MAP] <in$in|out> IFNAME", + NO_STR + "Route map set\n" + "Route map name\n" + "Route map set for input filtering\n" + "Route map set for output filtering\n" INTERFACE_STR) { - unsigned int i; - struct hash_bucket *mp; - int write = 0; - struct hash *ifrmaphash = ctx->ifrmaphash; - - for (i = 0; i < ifrmaphash->size; i++) - for (mp = ifrmaphash->index[i]; mp; mp = mp->next) { - struct if_rmap *if_rmap; - - if_rmap = mp->data; - - if (if_rmap->routemap[IF_RMAP_IN]) { - vty_out(vty, " route-map %s in %s\n", - if_rmap->routemap[IF_RMAP_IN], - if_rmap->ifname); - write++; - } - - if (if_rmap->routemap[IF_RMAP_OUT]) { - vty_out(vty, " route-map %s out %s\n", - if_rmap->routemap[IF_RMAP_OUT], - if_rmap->ifname); - write++; - } - } - return write; + const char *dir = in ? "in" : "out"; + const char *other_dir = in ? "out" : "in"; + + return if_route_map_handler(vty, true, dir, other_dir, ifname, + route_map); +} + +void cli_show_if_route_map(struct vty *vty, const struct lyd_node *dnode, + bool show_defaults) +{ + if (yang_dnode_exists(dnode, "./in-route-map")) + vty_out(vty, " route-map %s in %s\n", + yang_dnode_get_string(dnode, "./in-route-map"), + yang_dnode_get_string(dnode, "./interface")); + if (yang_dnode_exists(dnode, "./out-route-map")) + vty_out(vty, " route-map %s out %s\n", + yang_dnode_get_string(dnode, "./out-route-map"), + yang_dnode_get_string(dnode, "./interface")); +} + +void if_rmap_yang_modify_cb(struct if_rmap_ctx *ctx, + const struct lyd_node *dnode, + enum if_rmap_type type, bool del) +{ + + const char *mapname = yang_dnode_get_string(dnode, NULL); + const char *ifname = yang_dnode_get_string(dnode, "../interface"); + + if (del) + if_rmap_unset(ctx, ifname, type); + else + if_rmap_set(ctx, ifname, type, mapname); +} + +void if_rmap_yang_destroy_cb(struct if_rmap_ctx *ctx, + const struct lyd_node *dnode) +{ + const char *ifname = yang_dnode_get_string(dnode, "interface"); + if_rmap_unset(ctx, ifname, IF_RMAP_IN); + if_rmap_unset(ctx, ifname, IF_RMAP_OUT); } void if_rmap_ctx_delete(struct if_rmap_ctx *ctx) { - listnode_delete(if_rmap_ctx_list, ctx); hash_clean_and_free(&ctx->ifrmaphash, (void (*)(void *))if_rmap_free); - if (ctx->name) - XFREE(MTYPE_IF_RMAP_CTX_NAME, ctx); + XFREE(MTYPE_IF_RMAP_CTX_NAME, ctx->name); XFREE(MTYPE_IF_RMAP_CTX, ctx); } @@ -289,29 +289,24 @@ struct if_rmap_ctx *if_rmap_ctx_create(const char *name) ctx = XCALLOC(MTYPE_IF_RMAP_CTX, sizeof(struct if_rmap_ctx)); - if (ctx->name) - ctx->name = XSTRDUP(MTYPE_IF_RMAP_CTX_NAME, name); - ctx->ifrmaphash = hash_create_size(4, if_rmap_hash_make, if_rmap_hash_cmp, - "Interface Route-Map Hash"); - if (!if_rmap_ctx_list) - if_rmap_ctx_list = list_new(); - listnode_add(if_rmap_ctx_list, ctx); + ctx->name = XSTRDUP(MTYPE_IF_RMAP_CTX_NAME, name); + ctx->ifrmaphash = + hash_create_size(4, if_rmap_hash_make, if_rmap_hash_cmp, + "Interface Route-Map Hash"); return ctx; } void if_rmap_init(int node) { - if (node == RIPNG_NODE) { - } else if (node == RIP_NODE) { - install_element(RIP_NODE, &if_rmap_cmd); - install_element(RIP_NODE, &no_if_rmap_cmd); + if (node == RIP_NODE) { + install_element(RIP_NODE, &if_ipv4_route_map_cmd); + install_element(RIP_NODE, &no_if_ipv4_route_map_cmd); + } else if (node == RIPNG_NODE) { + install_element(RIPNG_NODE, &if_ipv6_route_map_cmd); + install_element(RIPNG_NODE, &no_if_ipv6_route_map_cmd); } - if_rmap_ctx_list = list_new(); } void if_rmap_terminate(void) { - if (!if_rmap_ctx_list) - return; - list_delete(&if_rmap_ctx_list); } diff --git a/lib/if_rmap.h b/lib/if_rmap.h index 3bdbc2a3b2..a9f811e221 100644 --- a/lib/if_rmap.h +++ b/lib/if_rmap.h @@ -6,15 +6,20 @@ #ifndef _ZEBRA_IF_RMAP_H #define _ZEBRA_IF_RMAP_H +#include "typesafe.h" + #ifdef __cplusplus extern "C" { #endif +struct lyd_node; +struct vty; + enum if_rmap_type { IF_RMAP_IN, IF_RMAP_OUT, IF_RMAP_MAX }; struct if_rmap { /* Name of the interface. */ - char *ifname; + const char *ifname; char *routemap[IF_RMAP_MAX]; }; @@ -45,7 +50,14 @@ void if_rmap_hook_delete(struct if_rmap_ctx *ctx, struct if_rmap *)); extern struct if_rmap *if_rmap_lookup(struct if_rmap_ctx *ctx, const char *ifname); +extern void if_rmap_yang_modify_cb(struct if_rmap_ctx *ctx, + const struct lyd_node *dnode, + enum if_rmap_type type, bool del); +extern void if_rmap_yang_destroy_cb(struct if_rmap_ctx *ctx, + const struct lyd_node *dnode); extern int config_write_if_rmap(struct vty *, struct if_rmap_ctx *ctx); +void cli_show_if_route_map(struct vty *vty, const struct lyd_node *dnode, + bool show_defaults); #ifdef __cplusplus } diff --git a/lib/iso.c b/lib/iso.c new file mode 100644 index 0000000000..fe97776ade --- /dev/null +++ b/lib/iso.c @@ -0,0 +1,144 @@ +/* + * ISO Network functions - iso_net.c + * + * Author: Olivier Dugeon <olivier.dugeon@orange.com> + * + * Copyright (C) 2023 Orange http://www.orange.com + * + * This file is part of Free Range Routing (FRR). + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "compiler.h" + +#include <string.h> +#include <ctype.h> +#include <time.h> + +#include "printfrr.h" +#include "iso.h" + +/** + * Print ISO System ID as 0000.0000.0000 + * + * @param Print buffer + * @param Print argument + * @param Pointer to the System ID to be printed + * + * @return Number of printed characters + */ +printfrr_ext_autoreg_p("SY", printfrr_iso_sysid); +static ssize_t printfrr_iso_sysid(struct fbuf *buf, struct printfrr_eargs *ea, + const void *vptr) +{ + const uint8_t *id = vptr; + + if (!id) + return bputs(buf, "(null)"); + + return bprintfrr(buf, "%02x%02x.%02x%02x.%02x%02x", + id[0], id[1], id[2], id[3], id[4], id[5]); +} + +/** + * Print ISO Pseudo Node system ID as 0000.0000.0000.00 + * + * @param Print buffer + * @param Print argument + * @param Pointer to the System ID to be printed + * + * @return Number of printed characters + */ +printfrr_ext_autoreg_p("PN", printfrr_iso_pseudo); +static ssize_t printfrr_iso_pseudo(struct fbuf *buf, struct printfrr_eargs *ea, + const void *vptr) +{ + const uint8_t *id = vptr; + + if (!id) + return bputs(buf, "(null)"); + + return bprintfrr(buf, "%02x%02x.%02x%02x.%02x%02x.%02x", + id[0], id[1], id[2], id[3], id[4], id[5], id[6]); +} + +/** + * Print ISO LSP Fragment System ID as 0000.0000.0000.00-00 + * + * @param Print buffer + * @param Print argument + * @param Pointer to the System ID to be printed + * + * @return Number of printed characters + */ +printfrr_ext_autoreg_p("LS", printfrr_iso_frag_id); +static ssize_t printfrr_iso_frag_id(struct fbuf *buf, struct printfrr_eargs *ea, + const void *vptr) +{ + const uint8_t *id = vptr; + + if (!id) + return bputs(buf, "(null)"); + + return bprintfrr(buf, "%02x%02x.%02x%02x.%02x%02x.%02x-%02x", + id[0], id[1], id[2], id[3], id[4], id[5], id[6], + id[7]); +} + +/** + * Print ISO Network address as 00.0000.0000.0000 ... with the System ID + * as 0000.0000.0000.00 when long 'l' option is added to '%pIS' + * + * @param Print buffer + * @param Print argument + * @param Pointer to the ISO Network address + * + * @return Number of printed characters + */ +printfrr_ext_autoreg_p("IS", printfrr_iso_addr); +static ssize_t printfrr_iso_addr(struct fbuf *buf, struct printfrr_eargs *ea, + const void *vptr) +{ + const struct iso_address *ia = vptr; + uint8_t len = 0; + int i = 0; + ssize_t ret = 0; + + if (ea->fmt[0] == 'l') { + len = 7; /* ISO SYSTEM ID + 1 */ + ea->fmt++; + } + + if (!ia) + return bputs(buf, "(null)"); + + len += ia->addr_len; + while (i < len) { + /* No dot for odd index and at the end of address */ + if ((i & 1) || (i == (len - 1))) + ret += bprintfrr(buf, "%02x", ia->area_addr[i]); + else + ret += bprintfrr(buf, "%02x.", ia->area_addr[i]); + i++; + } + + return ret; +} + diff --git a/lib/iso.h b/lib/iso.h new file mode 100644 index 0000000000..975d3c154b --- /dev/null +++ b/lib/iso.h @@ -0,0 +1,49 @@ +/* + * ISO Network definition - iso_net.h + * + * Author: Olivier Dugeon <olivier.dugeon@orange.com> + * + * Copyright (C) 2023 Orange http://www.orange.com + * + * This file is part of Free Range Routing (FRR). + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LIB_ISO_H_ +#define LIB_ISO_H_ + +#include "compiler.h" + +/* len of "xx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xx" + '\0' */ +#define ISO_ADDR_STRLEN 51 +#define ISO_ADDR_MIN 8 +#define ISO_ADDR_SIZE 20 +struct iso_address { + uint8_t addr_len; + uint8_t area_addr[ISO_ADDR_SIZE]; +}; + +/* len of "xxxx.xxxx.xxxx.xx-xx" + '\0' */ +#define ISO_SYSID_STRLEN 21 + +#ifdef _FRR_ATTRIBUTE_PRINTFRR +#pragma FRR printfrr_ext "%pSY" (uint8_t *) +#pragma FRR printfrr_ext "%pPN" (uint8_t *) +#pragma FRR printfrr_ext "%pLS" (uint8_t *) +#pragma FRR printfrr_ext "%pIS" (struct iso_address *) +#endif + +#endif /* LIB_ISO_H_ */ diff --git a/lib/link_state.c b/lib/link_state.c index aa4f275462..752030cd47 100644 --- a/lib/link_state.c +++ b/lib/link_state.c @@ -26,6 +26,7 @@ #include "printfrr.h" #include <lib/json.h> #include "link_state.h" +#include "iso.h" /* Link State Memory allocation */ DEFINE_MTYPE_STATIC(LIB, LS_DB, "Link State Database"); @@ -333,7 +334,7 @@ int ls_attributes_same(struct ls_attributes *l1, struct ls_attributes *l2) /** * Link State prefix management functions */ -struct ls_prefix *ls_prefix_new(struct ls_node_id adv, struct prefix p) +struct ls_prefix *ls_prefix_new(struct ls_node_id adv, struct prefix *p) { struct ls_prefix *new; @@ -342,7 +343,7 @@ struct ls_prefix *ls_prefix_new(struct ls_node_id adv, struct prefix p) new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_prefix)); new->adv = adv; - new->pref = p; + new->pref = *p; return new; } @@ -897,7 +898,7 @@ struct ls_subnet *ls_subnet_update(struct ls_ted *ted, struct ls_prefix *pref) if (pref == NULL) return NULL; - old = ls_find_subnet(ted, pref->pref); + old = ls_find_subnet(ted, &pref->pref); if (old) { if (!ls_prefix_same(old->ls_pref, pref)) { ls_prefix_del(old->ls_pref); @@ -950,11 +951,12 @@ void ls_subnet_del_all(struct ls_ted *ted, struct ls_subnet *subnet) ls_subnet_del(ted, subnet); } -struct ls_subnet *ls_find_subnet(struct ls_ted *ted, const struct prefix prefix) +struct ls_subnet *ls_find_subnet(struct ls_ted *ted, + const struct prefix *prefix) { struct ls_subnet subnet = {}; - subnet.key = prefix; + subnet.key = *prefix; return subnets_find(&ted->subnets, &subnet); } @@ -1854,7 +1856,7 @@ struct ls_subnet *ls_msg2subnet(struct ls_ted *ted, struct ls_message *msg, subnet->status = UPDATE; break; case LS_MSG_EVENT_DELETE: - subnet = ls_find_subnet(ted, pref->pref); + subnet = ls_find_subnet(ted, &pref->pref); if (subnet) { if (delete) ls_subnet_del_all(ted, subnet); @@ -1913,6 +1915,20 @@ void ls_delete_msg(struct ls_message *msg) if (msg == NULL) return; + if (msg->event == LS_MSG_EVENT_DELETE) { + switch (msg->type) { + case LS_MSG_TYPE_NODE: + ls_node_del(msg->data.node); + break; + case LS_MSG_TYPE_ATTRIBUTES: + ls_attributes_del(msg->data.attr); + break; + case LS_MSG_TYPE_PREFIX: + ls_prefix_del(msg->data.prefix); + break; + } + } + XFREE(MTYPE_LS_DB, msg); } @@ -1973,13 +1989,9 @@ static const char *const status2txt[] = { static const char *ls_node_id_to_text(struct ls_node_id lnid, char *str, size_t size) { - if (lnid.origin == ISIS_L1 || lnid.origin == ISIS_L2) { - uint8_t *id; - - id = lnid.id.iso.sys_id; - snprintfrr(str, size, "%02x%02x.%02x%02x.%02x%02x", id[0], - id[1], id[2], id[3], id[4], id[5]); - } else + if (lnid.origin == ISIS_L1 || lnid.origin == ISIS_L2) + snprintfrr(str, size, "%pSY", lnid.id.iso.sys_id); + else snprintfrr(str, size, "%pI4", &lnid.id.ip.addr); return str; diff --git a/lib/link_state.h b/lib/link_state.h index e4b97a34ed..d3a0ce39da 100644 --- a/lib/link_state.h +++ b/lib/link_state.h @@ -92,6 +92,9 @@ struct ls_node_id { */ extern int ls_node_id_same(struct ls_node_id i1, struct ls_node_id i2); +/* Supported number of algorithm by the link-state library */ +#define LIB_LS_SR_ALGO_COUNT 2 + /* Link State flags to indicate which Node parameters are valid */ #define LS_NODE_UNSET 0x0000 #define LS_NODE_NAME 0x0001 @@ -123,7 +126,7 @@ struct ls_node { uint32_t lower_bound; /* MPLS label lower bound */ uint32_t range_size; /* MPLS label range size */ } srlb; - uint8_t algo[2]; /* Segment Routing Algorithms */ + uint8_t algo[LIB_LS_SR_ALGO_COUNT]; /* Segment Routing Algorithms */ uint8_t msd; /* Maximum Stack Depth */ }; @@ -314,7 +317,7 @@ extern int ls_attributes_same(struct ls_attributes *a1, * * @return New Link State Prefix */ -extern struct ls_prefix *ls_prefix_new(struct ls_node_id adv, struct prefix p); +extern struct ls_prefix *ls_prefix_new(struct ls_node_id adv, struct prefix *p); /** * Remove Link State Prefix. Data Structure is freed. @@ -731,7 +734,7 @@ extern void ls_subnet_del_all(struct ls_ted *ted, struct ls_subnet *subnet); * @return Subnet if found, NULL otherwise */ extern struct ls_subnet *ls_find_subnet(struct ls_ted *ted, - const struct prefix prefix); + const struct prefix *prefix); /** * Create a new Link State Data Base. diff --git a/lib/mgmt_be_client.c b/lib/mgmt_be_client.c index 36c78052bc..7437eedfc7 100644 --- a/lib/mgmt_be_client.c +++ b/lib/mgmt_be_client.c @@ -16,17 +16,17 @@ #include "sockopt.h" #ifdef REDIRECT_DEBUG_TO_STDERR -#define MGMTD_BE_CLIENT_DBG(fmt, ...) \ +#define MGMTD_BE_CLIENT_DBG(fmt, ...) \ fprintf(stderr, "%s: " fmt "\n", __func__, ##__VA_ARGS__) -#define MGMTD_BE_CLIENT_ERR(fmt, ...) \ +#define MGMTD_BE_CLIENT_ERR(fmt, ...) \ fprintf(stderr, "%s: ERROR, " fmt "\n", __func__, ##__VA_ARGS__) #else /* REDIRECT_DEBUG_TO_STDERR */ -#define MGMTD_BE_CLIENT_DBG(fmt, ...) \ +#define MGMTD_BE_CLIENT_DBG(fmt, ...) \ do { \ - if (mgmt_debug_be_client) \ - zlog_debug("%s: " fmt, __func__, ##__VA_ARGS__); \ + if (mgmt_debug_be_client) \ + zlog_debug("%s: " fmt, __func__, ##__VA_ARGS__); \ } while (0) -#define MGMTD_BE_CLIENT_ERR(fmt, ...) \ +#define MGMTD_BE_CLIENT_ERR(fmt, ...) \ zlog_err("%s: ERROR: " fmt, __func__, ##__VA_ARGS__) #endif /* REDIRECT_DEBUG_TO_STDERR */ @@ -597,7 +597,8 @@ static int mgmt_be_txn_cfg_prepare(struct mgmt_be_txn_ctx *txn) MGMTD_BE_CLIENT_DBG( "Avg-nb-edit-duration %lu uSec, nb-prep-duration %lu (avg: %lu) uSec, batch size %u", client_ctx->avg_edit_nb_cfg_tm, prep_nb_cfg_tm, - client_ctx->avg_prep_nb_cfg_tm, (uint32_t)num_processed); + client_ctx->avg_prep_nb_cfg_tm, + (uint32_t)num_processed); if (error) mgmt_be_txn_cfg_abort(txn); diff --git a/lib/mgmt_fe_client.c b/lib/mgmt_fe_client.c index a2c4fd6572..7cb9aa3def 100644 --- a/lib/mgmt_fe_client.c +++ b/lib/mgmt_fe_client.c @@ -109,8 +109,8 @@ mgmt_fe_find_session_by_session_id(struct mgmt_fe_client_ctx *client_ctx, FOREACH_SESSION_IN_LIST (client_ctx, session) { if (session->session_id == session_id) { MGMTD_FE_CLIENT_DBG( - "Found session %p for session-id %llu.", session, - (unsigned long long)session_id); + "Found session %p for session-id %llu.", + session, (unsigned long long)session_id); return session; } } @@ -274,7 +274,8 @@ mgmt_fe_send_lockds_req(struct mgmt_fe_client_ctx *client_ctx, MGMTD_FE_CLIENT_DBG( "Sending %sLOCK_REQ message for Ds:%d session %llu to MGMTD Frontend server", - lock ? "" : "UN", ds_id, (unsigned long long)session->client_id); + lock ? "" : "UN", ds_id, + (unsigned long long)session->client_id); return mgmt_fe_client_send_msg(client_ctx, &fe_msg); } @@ -310,12 +311,12 @@ mgmt_fe_send_setcfg_req(struct mgmt_fe_client_ctx *client_ctx, return mgmt_fe_client_send_msg(client_ctx, &fe_msg); } -static int -mgmt_fe_send_commitcfg_req(struct mgmt_fe_client_ctx *client_ctx, - struct mgmt_fe_client_session *session, - uint64_t req_id, Mgmtd__DatastoreId src_ds_id, - Mgmtd__DatastoreId dest_ds_id, bool validate_only, - bool abort) +static int mgmt_fe_send_commitcfg_req(struct mgmt_fe_client_ctx *client_ctx, + struct mgmt_fe_client_session *session, + uint64_t req_id, + Mgmtd__DatastoreId src_ds_id, + Mgmtd__DatastoreId dest_ds_id, + bool validate_only, bool abort) { (void)req_id; Mgmtd__FeMessage fe_msg; @@ -450,15 +451,17 @@ mgmt_fe_client_handle_msg(struct mgmt_fe_client_ctx *client_ctx, if (session && fe_msg->session_reply->success) { MGMTD_FE_CLIENT_DBG( "Session Create for client-id %llu successful.", - (unsigned long long)fe_msg - ->session_reply->client_conn_id); + (unsigned long long) + fe_msg->session_reply + ->client_conn_id); session->session_id = fe_msg->session_reply->session_id; } else { MGMTD_FE_CLIENT_ERR( "Session Create for client-id %llu failed.", - (unsigned long long)fe_msg - ->session_reply->client_conn_id); + (unsigned long long) + fe_msg->session_reply + ->client_conn_id); } } else if (!fe_msg->session_reply->create) { MGMTD_FE_CLIENT_DBG( diff --git a/lib/northbound.c b/lib/northbound.c index 307cf0fb49..775f6ff92f 100644 --- a/lib/northbound.c +++ b/lib/northbound.c @@ -167,7 +167,7 @@ struct nb_node *nb_node_find(const char *path) * Use libyang to find the schema node associated to the path and get * the northbound node from there (snode private pointer). */ - snode = lys_find_path(ly_native_ctx, NULL, path, 0); + snode = yang_find_snode(ly_native_ctx, path, 0); if (!snode) return NULL; @@ -2129,8 +2129,8 @@ int nb_oper_data_iterate(const char *xpath, struct yang_translator *translator, * all YANG lists (if any). */ - LY_ERR err = lyd_new_path(NULL, ly_native_ctx, xpath, NULL, - LYD_NEW_PATH_UPDATE, &dnode); + LY_ERR err = lyd_new_path2(NULL, ly_native_ctx, xpath, NULL, 0, 0, + LYD_NEW_PATH_UPDATE, NULL, &dnode); if (err || !dnode) { const char *errmsg = err ? ly_errmsg(ly_native_ctx) : "node not found"; diff --git a/lib/northbound_cli.c b/lib/northbound_cli.c index 5cf5f93b43..c5582fc21c 100644 --- a/lib/northbound_cli.c +++ b/lib/northbound_cli.c @@ -1434,6 +1434,7 @@ DEFPY (show_yang_operational_data, struct lyd_node *dnode; char *strp; uint32_t print_options = LYD_PRINT_WITHSIBLINGS; + int ret; if (xml) format = LYD_XML; @@ -1454,10 +1455,15 @@ DEFPY (show_yang_operational_data, /* Obtain data. */ dnode = yang_dnode_new(ly_ctx, false); - if (nb_oper_data_iterate(xpath, translator, 0, nb_cli_oper_data_cb, - dnode) - != NB_OK) { - vty_out(vty, "%% Failed to fetch operational data.\n"); + ret = nb_oper_data_iterate(xpath, translator, 0, nb_cli_oper_data_cb, + dnode); + if (ret != NB_OK) { + if (format == LYD_JSON) + vty_out(vty, "{}\n"); + else { + /* embed ly_last_errmsg() when we get newer libyang */ + vty_out(vty, "<!-- Not found -->\n"); + } yang_dnode_free(dnode); return CMD_WARNING; } diff --git a/lib/northbound_sysrepo.c b/lib/northbound_sysrepo.c index 86105d2e77..7fd4af8356 100644 --- a/lib/northbound_sysrepo.c +++ b/lib/northbound_sysrepo.c @@ -357,7 +357,7 @@ static int frr_sr_state_data_iter_cb(const struct lysc_node *snode, ly_errno = 0; ly_errno = lyd_new_path(NULL, ly_native_ctx, data->xpath, data->value, 0, &dnode); - if (!dnode && ly_errno) { + if (ly_errno) { flog_warn(EC_LIB_LIBYANG, "%s: lyd_new_path() failed", __func__); yang_data_free(data); diff --git a/lib/prefix.h b/lib/prefix.h index 88a228b55c..90b792b33c 100644 --- a/lib/prefix.h +++ b/lib/prefix.h @@ -598,6 +598,14 @@ static inline bool ipv6_mcast_ssm(const struct in6_addr *addr) return (bits & 0xfff0ffff) == 0xff300000; } +static inline bool ipv6_mcast_reserved(const struct in6_addr *addr) +{ + uint32_t bits = ntohl(addr->s6_addr32[0]); + + /* ffx2::/16 */ + return (bits & 0xff0fffff) == 0xff020000; +} + static inline uint8_t ipv4_mcast_scope(const struct in_addr *addr) { uint32_t bits = ntohl(addr->s_addr); diff --git a/lib/routemap.c b/lib/routemap.c index 16da81fa74..39455841e3 100644 --- a/lib/routemap.c +++ b/lib/routemap.c @@ -394,6 +394,40 @@ void route_map_no_set_metric_hook(int (*func)(struct route_map_index *index, { rmap_match_set_hook.no_set_metric = func; } +/* set min-metric */ +void route_map_set_min_metric_hook(int (*func)(struct route_map_index *index, + const char *command, + const char *arg, char *errmsg, + size_t errmsg_len)) +{ + rmap_match_set_hook.set_min_metric = func; +} + +/* no set min-metric */ +void route_map_no_set_min_metric_hook(int (*func)(struct route_map_index *index, + const char *command, + const char *arg, char *errmsg, + size_t errmsg_len)) +{ + rmap_match_set_hook.no_set_min_metric = func; +} +/* set max-metric */ +void route_map_set_max_metric_hook(int (*func)(struct route_map_index *index, + const char *command, + const char *arg, char *errmsg, + size_t errmsg_len)) +{ + rmap_match_set_hook.set_max_metric = func; +} + +/* no set max-metric */ +void route_map_no_set_max_metric_hook(int (*func)(struct route_map_index *index, + const char *command, + const char *arg, char *errmsg, + size_t errmsg_len)) +{ + rmap_match_set_hook.no_set_max_metric = func; +} /* set tag */ void route_map_set_tag_hook(int (*func)(struct route_map_index *index, @@ -669,7 +703,7 @@ static struct route_map *route_map_add(const char *name) if (!map->ipv6_prefix_table) map->ipv6_prefix_table = route_table_init(); - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)) + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))) zlog_debug("Add route-map %s", name); return map; } @@ -689,7 +723,7 @@ static void route_map_free_map(struct route_map *map) while ((index = map->head) != NULL) route_map_index_delete(index, 0); - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)) + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))) zlog_debug("Deleting route-map %s", map->name); list = &route_map_master; @@ -706,6 +740,9 @@ static void route_map_free_map(struct route_map *map) else list->head = map->next; + route_table_finish(map->ipv4_prefix_table); + route_table_finish(map->ipv6_prefix_table); + hash_release(route_map_master_hash, map); XFREE(MTYPE_ROUTE_MAP_NAME, map->name); XFREE(MTYPE_ROUTE_MAP, map); @@ -1120,7 +1157,7 @@ void route_map_index_delete(struct route_map_index *index, int notify) QOBJ_UNREG(index); - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)) + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))) zlog_debug("Deleting route-map %s sequence %d", index->map->name, index->pref); @@ -1231,7 +1268,7 @@ route_map_index_add(struct route_map *map, enum route_map_type type, int pref) route_map_notify_dependencies(map->name, RMAP_EVENT_CALL_ADDED); } - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)) + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))) zlog_debug("Route-map %s add sequence %d, type: %s", map->name, pref, route_map_type_str(type)); @@ -1811,10 +1848,8 @@ route_map_get_index(struct route_map *map, const struct prefix *prefix, * must be AF_INET or AF_INET6 in order for the lookup to succeed. So if * the AF doesn't line up with the LPM trees, skip the optimization. */ - if (map->optimization_disabled || - (prefix->family == AF_INET && !map->ipv4_prefix_table) || - (prefix->family == AF_INET6 && !map->ipv6_prefix_table)) { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + if (map->optimization_disabled) { + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))) zlog_debug( "Skipping route-map optimization for route-map: %s, pfx: %pFX, family: %d", map->name, prefix, prefix->family); @@ -1826,9 +1861,6 @@ route_map_get_index(struct route_map *map, const struct prefix *prefix, else table = map->ipv6_prefix_table; - if (!table) - return NULL; - do { candidate_rmap_list = route_map_get_index_list(&rn, prefix, table); @@ -1914,19 +1946,10 @@ static void route_map_pfx_table_add_default(afi_t afi, p.family = afi2family(afi); p.prefixlen = 0; - if (p.family == AF_INET) { - table = index->map->ipv4_prefix_table; - if (!table) - index->map->ipv4_prefix_table = route_table_init(); - + if (p.family == AF_INET) table = index->map->ipv4_prefix_table; - } else { - table = index->map->ipv6_prefix_table; - if (!table) - index->map->ipv6_prefix_table = route_table_init(); - + else table = index->map->ipv6_prefix_table; - } /* Add default route to table */ rn = route_node_get(table, &p); @@ -2317,8 +2340,6 @@ static void route_map_pfx_tbl_update(route_map_event_t event, struct route_map_index *index, afi_t afi, const char *plist_name) { - struct route_map *rmap = NULL; - if (!index) return; @@ -2332,19 +2353,6 @@ static void route_map_pfx_tbl_update(route_map_event_t event, route_map_pfx_table_del_default(AFI_IP, index); route_map_pfx_table_del_default(AFI_IP6, index); - if ((index->map->head == NULL) && (index->map->tail == NULL)) { - rmap = index->map; - - if (rmap->ipv4_prefix_table) { - route_table_finish(rmap->ipv4_prefix_table); - rmap->ipv4_prefix_table = NULL; - } - - if (rmap->ipv6_prefix_table) { - route_table_finish(rmap->ipv6_prefix_table); - rmap->ipv6_prefix_table = NULL; - } - } return; } @@ -2569,12 +2577,14 @@ route_map_result_t route_map_apply_ext(struct route_map *map, */ if (prefix->family == AF_EVPN) { if (evpn_prefix2prefix(prefix, &conv) != 0) { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + if (unlikely(CHECK_FLAG(rmap_debug, + DEBUG_ROUTEMAP_DETAIL))) zlog_debug( "Unable to convert EVPN prefix %pFX into IPv4/IPv6 prefix. Falling back to non-optimized route-map lookup", prefix); } else { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + if (unlikely(CHECK_FLAG(rmap_debug, + DEBUG_ROUTEMAP_DETAIL))) zlog_debug( "Converted EVPN prefix %pFX into %pFX for optimized route-map lookup", prefix, &conv); @@ -2586,13 +2596,13 @@ route_map_result_t route_map_apply_ext(struct route_map *map, index = route_map_get_index(map, prefix, match_object, &match_ret); if (index) { index->applied++; - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)) + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))) zlog_debug( "Best match route-map: %s, sequence: %d for pfx: %pFX, result: %s", map->name, index->pref, prefix, route_map_cmd_result_str(match_ret)); } else { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)) + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))) zlog_debug( "No best match sequence for pfx: %pFX in route-map: %s, result: %s", prefix, map->name, @@ -2615,7 +2625,7 @@ route_map_result_t route_map_apply_ext(struct route_map *map, /* Apply this index. */ match_ret = route_map_apply_match(&index->match_list, prefix, match_object); - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)) { + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))) { zlog_debug( "Route-map: %s, sequence: %d, prefix: %pFX, result: %s", map->name, index->pref, prefix, @@ -2728,7 +2738,7 @@ route_map_result_t route_map_apply_ext(struct route_map *map, } route_map_apply_end: - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)) + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))) zlog_debug("Route-map: %s, prefix: %pFX, result: %s", (map ? map->name : "null"), prefix, route_map_result_str(ret)); @@ -2783,7 +2793,7 @@ static void route_map_clear_reference(struct hash_bucket *bucket, void *arg) tmp_dep_data.rname = arg; dep_data = hash_release(dep->dep_rmap_hash, &tmp_dep_data); if (dep_data) { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)) + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))) zlog_debug("Clearing reference for %s to %s count: %d", dep->dep_name, tmp_dep_data.rname, dep_data->refcnt); @@ -2803,7 +2813,7 @@ static void route_map_clear_all_references(char *rmap_name) { int i; - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)) + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))) zlog_debug("Clearing references for %s", rmap_name); for (i = 1; i < ROUTE_MAP_DEP_MAX; i++) { @@ -2879,7 +2889,7 @@ static int route_map_dep_update(struct hash *dephash, const char *dep_name, case RMAP_EVENT_LLIST_ADDED: case RMAP_EVENT_CALL_ADDED: case RMAP_EVENT_FILTER_ADDED: - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)) + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))) zlog_debug("Adding dependency for filter %s in route-map %s", dep_name, rmap_name); dep = (struct route_map_dep *)hash_get( @@ -2908,7 +2918,7 @@ static int route_map_dep_update(struct hash *dephash, const char *dep_name, case RMAP_EVENT_LLIST_DELETED: case RMAP_EVENT_CALL_DELETED: case RMAP_EVENT_FILTER_DELETED: - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)) + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))) zlog_debug("Deleting dependency for filter %s in route-map %s", dep_name, rmap_name); dep = (struct route_map_dep *)hash_get(dephash, dname, NULL); @@ -3034,7 +3044,7 @@ static void route_map_process_dependency(struct hash_bucket *bucket, void *data) dep_data = bucket->data; rmap_name = dep_data->rname; - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)) + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))) zlog_debug("Notifying %s of dependency", rmap_name); if (route_map_master.event_hook) (*route_map_master.event_hook)(rmap_name); @@ -3082,7 +3092,7 @@ void route_map_notify_dependencies(const char *affected_name, if (!dep->this_hash) dep->this_hash = upd8_hash; - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)) + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))) zlog_debug("Filter %s updated", dep->dep_name); hash_iterate(dep->dep_rmap_hash, route_map_process_dependency, (void *)event); diff --git a/lib/routemap.h b/lib/routemap.h index 2197a49e76..770d705837 100644 --- a/lib/routemap.h +++ b/lib/routemap.h @@ -312,6 +312,8 @@ DECLARE_QOBJ_TYPE(route_map); (strmatch(A, "frr-route-map:ipv6-next-hop")) #define IS_SET_METRIC(A) \ (strmatch(A, "frr-route-map:set-metric")) +#define IS_SET_MIN_METRIC(A) (strmatch(A, "frr-route-map:set-min-metric")) +#define IS_SET_MAX_METRIC(A) (strmatch(A, "frr-route-map:set-max-metric")) #define IS_SET_TAG(A) (strmatch(A, "frr-route-map:set-tag")) #define IS_SET_SR_TE_COLOR(A) \ (strmatch(A, "frr-route-map:set-sr-te-color")) @@ -684,6 +686,22 @@ extern void route_map_no_set_metric_hook( int (*func)(struct route_map_index *index, const char *command, const char *arg, char *errmsg, size_t errmsg_len)); +/* set metric */ +extern void route_map_set_max_metric_hook( + int (*func)(struct route_map_index *index, const char *command, + const char *arg, char *errmsg, size_t errmsg_len)); +/* no set metric */ +extern void route_map_no_set_max_metric_hook( + int (*func)(struct route_map_index *index, const char *command, + const char *arg, char *errmsg, size_t errmsg_len)); +/* set metric */ +extern void route_map_set_min_metric_hook( + int (*func)(struct route_map_index *index, const char *command, + const char *arg, char *errmsg, size_t errmsg_len)); +/* no set metric */ +extern void route_map_no_set_min_metric_hook( + int (*func)(struct route_map_index *index, const char *command, + const char *arg, char *errmsg, size_t errmsg_len)); /* set tag */ extern void route_map_set_tag_hook(int (*func)(struct route_map_index *index, const char *command, @@ -920,6 +938,25 @@ struct route_map_match_set_hooks { int (*no_set_metric)(struct route_map_index *index, const char *command, const char *arg, char *errmsg, size_t errmsg_len); + /* set min-metric */ + int (*set_min_metric)(struct route_map_index *index, + const char *command, const char *arg, + char *errmsg, size_t errmsg_len); + + /* no set min-metric */ + int (*no_set_min_metric)(struct route_map_index *index, + const char *command, const char *arg, + char *errmsg, size_t errmsg_len); + + /* set max-metric */ + int (*set_max_metric)(struct route_map_index *index, + const char *command, const char *arg, + char *errmsg, size_t errmsg_len); + + /* no set max-metric */ + int (*no_set_max_metric)(struct route_map_index *index, + const char *command, const char *arg, + char *errmsg, size_t errmsg_len); /* set tag */ int (*set_tag)(struct route_map_index *index, diff --git a/lib/routemap_cli.c b/lib/routemap_cli.c index 4345b74bc0..75d1175886 100644 --- a/lib/routemap_cli.c +++ b/lib/routemap_cli.c @@ -890,6 +890,76 @@ DEFPY_YANG( return nb_cli_apply_changes(vty, NULL); } +DEFPY_YANG(set_min_metric, set_min_metric_cmd, + "set min-metric <(0-4294967295)$metric>", + SET_STR + "Minimum metric value for destination routing protocol\n" + "Minimum metric value\n") +{ + const char *xpath = + "./set-action[action='frr-route-map:set-min-metric']"; + char xpath_value[XPATH_MAXLEN]; + char value[64]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/min-metric", xpath); + snprintf(value, sizeof(value), "%s", metric_str); + + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, value); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG(no_set_min_metric, no_set_min_metric_cmd, + "no set min-metric [(0-4294967295)]", + NO_STR SET_STR + "Minimum metric value for destination routing protocol\n" + "Minumum metric value\n") +{ + const char *xpath = + "./set-action[action='frr-route-map:set-min-metric']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG(set_max_metric, set_max_metric_cmd, + "set max-metric <(0-4294967295)$metric>", + SET_STR + "Maximum metric value for destination routing protocol\n" + "Miximum metric value\n") +{ + const char *xpath = + "./set-action[action='frr-route-map:set-max-metric']"; + char xpath_value[XPATH_MAXLEN]; + char value[64]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/max-metric", xpath); + snprintf(value, sizeof(value), "%s", metric_str); + + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, value); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG(no_set_max_metric, no_set_max_metric_cmd, + "no set max-metric [(0-4294967295)]", + NO_STR SET_STR + "Maximum Metric value for destination routing protocol\n" + "Maximum metric value\n") +{ + const char *xpath = + "./set-action[action='frr-route-map:set-max-metric']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + DEFPY_YANG( set_tag, set_tag_cmd, "set tag (1-4294967295)$tag", @@ -1011,6 +1081,14 @@ void route_map_action_show(struct vty *vty, const struct lyd_node *dnode, yang_dnode_get_string( dnode, "./rmap-set-action/value")); } + } else if (IS_SET_MIN_METRIC(action)) { + vty_out(vty, " set min-metric %s\n", + yang_dnode_get_string(dnode, + "./rmap-set-action/min-metric")); + } else if (IS_SET_MAX_METRIC(action)) { + vty_out(vty, " set max-metric %s\n", + yang_dnode_get_string(dnode, + "./rmap-set-action/max-metric")); } else if (IS_SET_TAG(action)) { vty_out(vty, " set tag %s\n", yang_dnode_get_string(dnode, "./rmap-set-action/tag")); @@ -1551,6 +1629,12 @@ void route_map_cli_init(void) install_element(RMAP_NODE, &set_metric_cmd); install_element(RMAP_NODE, &no_set_metric_cmd); + install_element(RMAP_NODE, &set_min_metric_cmd); + install_element(RMAP_NODE, &no_set_min_metric_cmd); + + install_element(RMAP_NODE, &set_max_metric_cmd); + install_element(RMAP_NODE, &no_set_max_metric_cmd); + install_element(RMAP_NODE, &set_tag_cmd); install_element(RMAP_NODE, &no_set_tag_cmd); diff --git a/lib/routemap_northbound.c b/lib/routemap_northbound.c index ab127dd0fc..4659850994 100644 --- a/lib/routemap_northbound.c +++ b/lib/routemap_northbound.c @@ -1028,6 +1028,110 @@ lib_route_map_entry_set_action_value_destroy(struct nb_cb_destroy_args *args) } /* + * XPath: /frr-route-map:lib/route-map/entry/set-action/min-metric + */ +static int set_action_min_metric_modify(enum nb_event event, + const struct lyd_node *dnode, + union nb_resource *resource, + const char *value, char *errmsg, + size_t errmsg_len) +{ + struct routemap_hook_context *rhc; + int rv; + + if (event != NB_EV_APPLY) + return NB_OK; + + /* Check for hook function. */ + if (rmap_match_set_hook.set_min_metric == NULL) + return NB_OK; + + /* Add configuration. */ + rhc = nb_running_get_entry(dnode, NULL, true); + + /* Set destroy information. */ + rhc->rhc_shook = rmap_match_set_hook.no_set_min_metric; + rhc->rhc_rule = "min-metric"; + + rv = rmap_match_set_hook.set_min_metric(rhc->rhc_rmi, "min-metric", + value, errmsg, errmsg_len); + if (rv != CMD_SUCCESS) { + rhc->rhc_shook = NULL; + return NB_ERR_INCONSISTENCY; + } + + return NB_OK; +} + +static int +lib_route_map_entry_set_action_min_metric_modify(struct nb_cb_modify_args *args) +{ + const char *min_metric = yang_dnode_get_string(args->dnode, NULL); + + return set_action_min_metric_modify(args->event, args->dnode, + args->resource, min_metric, + args->errmsg, args->errmsg_len); +} + +static int lib_route_map_entry_set_action_min_metric_destroy( + struct nb_cb_destroy_args *args) +{ + return lib_route_map_entry_set_destroy(args); +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/set-action/max-metric + */ +static int set_action_max_metric_modify(enum nb_event event, + const struct lyd_node *dnode, + union nb_resource *resource, + const char *value, char *errmsg, + size_t errmsg_len) +{ + struct routemap_hook_context *rhc; + int rv; + + if (event != NB_EV_APPLY) + return NB_OK; + + /* Check for hook function. */ + if (rmap_match_set_hook.set_max_metric == NULL) + return NB_OK; + + /* Add configuration. */ + rhc = nb_running_get_entry(dnode, NULL, true); + + /* Set destroy information. */ + rhc->rhc_shook = rmap_match_set_hook.no_set_max_metric; + rhc->rhc_rule = "max-metric"; + + rv = rmap_match_set_hook.set_max_metric(rhc->rhc_rmi, "max-metric", + value, errmsg, errmsg_len); + if (rv != CMD_SUCCESS) { + rhc->rhc_shook = NULL; + return NB_ERR_INCONSISTENCY; + } + + return NB_OK; +} + +static int +lib_route_map_entry_set_action_max_metric_modify(struct nb_cb_modify_args *args) +{ + const char *max_metric = yang_dnode_get_string(args->dnode, NULL); + + return set_action_max_metric_modify(args->event, args->dnode, + args->resource, max_metric, + args->errmsg, args->errmsg_len); +} + +static int lib_route_map_entry_set_action_max_metric_destroy( + struct nb_cb_destroy_args *args) +{ + return lib_route_map_entry_set_destroy(args); +} + +/* * XPath: /frr-route-map:lib/route-map/entry/set-action/add-metric */ static int @@ -1369,6 +1473,20 @@ const struct frr_yang_module_info frr_route_map_info = { } }, { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/min-metric", + .cbs = { + .modify = lib_route_map_entry_set_action_min_metric_modify, + .destroy = lib_route_map_entry_set_action_min_metric_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/max-metric", + .cbs = { + .modify = lib_route_map_entry_set_action_max_metric_modify, + .destroy = lib_route_map_entry_set_action_max_metric_destroy, + } + }, + { .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/add-metric", .cbs = { .modify = lib_route_map_entry_set_action_add_metric_modify, diff --git a/lib/segment_routing.c b/lib/segment_routing.c new file mode 100644 index 0000000000..f45a64d29a --- /dev/null +++ b/lib/segment_routing.c @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/********************************************************************* + * Copyright 2022 Hiroki Shirokura, LINE Corporation + * Copyright 2022 Masakazu Asama + * Copyright 2022 6WIND S.A. + * + * segment_routing.c: Segment-Routing Library + * + * Authors + * ------- + * Hiroki Shirokura + * Masakazu Asama + * Louis Scalbert + */ + +#include "segment_routing.h" + +const char *sr_algorithm_string(uint8_t algo) +{ + switch (algo) { + case SR_ALGORITHM_SPF: + return "SPF"; + case SR_ALGORITHM_STRICT_SPF: + return "Strict SPF"; + case SR_ALGORITHM_UNSET: + return "Unset"; + default: + return algo >= SR_ALGORITHM_FLEX_MIN ? "Flex-Algo" : "Unknown"; + } +} diff --git a/lib/segment_routing.h b/lib/segment_routing.h new file mode 100644 index 0000000000..5e71466bf1 --- /dev/null +++ b/lib/segment_routing.h @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/********************************************************************* + * Copyright 2022 Hiroki Shirokura, LINE Corporation + * Copyright 2022 Masakazu Asama + * Copyright 2022 6WIND S.A. + * + * segment_routing.h: Segment-Routing Library + * + * Authors + * ------- + * Hiroki Shirokura + * Masakazu Asama + * Louis Scalbert + */ + +#ifndef _FRR_SR_H +#define _FRR_SR_H + +#include <zebra.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * IGP Algorithm Types + * https://www.iana.org/assignments/igp-parameters/igp-parameters.xhtml + */ +#define SR_ALGORITHM_SPF 0 /* RFC8665 */ +#define SR_ALGORITHM_STRICT_SPF 1 /* RFC8665 */ +#define SR_ALGORITHM_UNSET 127 /* FRRouting defined */ +#define SR_ALGORITHM_FLEX_MIN 128 /* RFC9350 Flex-Algorithm */ +#define SR_ALGORITHM_FLEX_MAX 255 /* RFC9350 Flex-Algorithm */ +#define SR_ALGORITHM_COUNT 256 + +const char *sr_algorithm_string(uint8_t algo); + +#ifdef __cplusplus +} +#endif + +#endif /* _FRR_SR_H */ diff --git a/lib/subdir.am b/lib/subdir.am index 0f5bbef0b2..0cd68c0e70 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -33,6 +33,7 @@ lib_libfrr_la_SOURCES = \ lib/filter.c \ lib/filter_cli.c \ lib/filter_nb.c \ + lib/flex_algo.c \ lib/frrcu.c \ lib/frrlua.c \ lib/frrscript.c \ @@ -47,6 +48,7 @@ lib_libfrr_la_SOURCES = \ lib/if_rmap.c \ lib/imsg-buffer.c \ lib/imsg.c \ + lib/iso.c \ lib/jhash.c \ lib/json.c \ lib/keychain.c \ @@ -98,6 +100,7 @@ lib_libfrr_la_SOURCES = \ lib/sockopt.c \ lib/sockunion.c \ lib/spf_backoff.c \ + lib/segment_routing.c \ lib/srcdest_table.c \ lib/stream.c \ lib/strformat.c \ @@ -173,6 +176,7 @@ clippy_scan += \ lib/affinitymap_cli.c \ lib/if.c \ lib/filter_cli.c \ + lib/if_rmap.c \ lib/log_vty.c \ lib/nexthop_group.c \ lib/northbound_cli.c \ @@ -208,6 +212,7 @@ pkginclude_HEADERS += \ lib/distribute.h \ lib/ferr.h \ lib/filter.h \ + lib/flex_algo.h \ lib/freebsd-queue.h \ lib/frrlua.h \ lib/frrscript.h \ @@ -224,6 +229,7 @@ pkginclude_HEADERS += \ lib/if_rmap.h \ lib/imsg.h \ lib/ipaddr.h \ + lib/iso.h \ lib/jhash.h \ lib/json.h \ lib/keychain.h \ @@ -279,6 +285,7 @@ pkginclude_HEADERS += \ lib/sockopt.h \ lib/sockunion.h \ lib/spf_backoff.h \ + lib/segment_routing.h \ lib/srcdest_table.h \ lib/srte.h \ lib/stream.h \ diff --git a/lib/termtable.c b/lib/termtable.c index 30f1ab74c6..9b36d5ebfa 100644 --- a/lib/termtable.c +++ b/lib/termtable.c @@ -7,6 +7,7 @@ #include <zebra.h> #include <stdio.h> +#include "lib/json.h" #include "printfrr.h" #include "memory.h" #include "termtable.h" @@ -485,3 +486,45 @@ char *ttable_dump(struct ttable *tt, const char *newline) return buf; } + +/* Crude conversion from ttable to json array. + * Assume that the first row has column headings. + * + * Formats are: + * d int32 + * f double + * l int64 + * s string (default) + */ +json_object *ttable_json(struct ttable *tt, const char *const formats) +{ + struct ttable_cell *row; /* iteration pointers */ + json_object *json = NULL; + + json = json_object_new_array(); + + for (int i = 1; i < tt->nrows; i++) { + json_object *jobj; + json_object *val; + + row = tt->table[i]; + jobj = json_object_new_object(); + json_object_array_add(json, jobj); + for (int j = 0; j < tt->ncols; j++) { + switch (formats[j]) { + case 'd': + case 'l': + val = json_object_new_int64(atol(row[j].text)); + break; + case 'f': + val = json_object_new_double(atof(row[j].text)); + break; + default: + val = json_object_new_string(row[j].text); + } + json_object_object_add(jobj, tt->table[0][j].text, val); + } + } + + return json; +} diff --git a/lib/termtable.h b/lib/termtable.h index 3aa8caee89..7258682bd8 100644 --- a/lib/termtable.h +++ b/lib/termtable.h @@ -8,6 +8,7 @@ #define _TERMTABLE_H_ #include <zebra.h> +#include "lib/json.h" #ifdef __cplusplus extern "C" { @@ -277,6 +278,17 @@ void ttable_rowseps(struct ttable *tt, unsigned int row, */ char *ttable_dump(struct ttable *tt, const char *newline); +/** + * Convert a table to a JSON array of objects. + * + * Caller must free the returned json_object structure. + * + * @param tt the table to convert + * @param formats an array of characters indicating what JSON type should be + * used. + */ +json_object *ttable_json(struct ttable *tt, const char *const formats); + #ifdef __cplusplus } #endif diff --git a/lib/typesafe.h b/lib/typesafe.h index 1e3f932565..3292b6ec8b 100644 --- a/lib/typesafe.h +++ b/lib/typesafe.h @@ -20,6 +20,9 @@ extern "C" { #define frr_each(prefix, head, item) \ for (item = prefix##_first(head); item; \ item = prefix##_next(head, item)) +#define frr_each_const(prefix, head, item) \ + for (item = prefix##_const_first(head); item; \ + item = prefix##_const_next(head, item)) #define frr_each_safe(prefix, head, item) \ for (typeof(prefix##_next_safe(head, NULL)) prefix##_safe = \ prefix##_next_safe(head, \ @@ -2400,7 +2400,7 @@ static void vty_timeout(struct event *thread) } /* Read up configuration file from file_name. */ -static void vty_read_file(struct nb_config *config, FILE *confp) +void vty_read_file(struct nb_config *config, FILE *confp) { int ret; struct vty *vty; @@ -369,6 +369,7 @@ extern void vty_pass_fd(struct vty *vty, int fd); extern bool vty_read_config(struct nb_config *config, const char *config_file, char *config_default_dir); +extern void vty_read_file(struct nb_config *config, FILE *confp); extern void vty_time_print(struct vty *, int); extern void vty_serv_sock(const char *, unsigned short, const char *); extern void vty_close(struct vty *); diff --git a/lib/yang.c b/lib/yang.c index 70a3251ab3..4dd8654217 100644 --- a/lib/yang.c +++ b/lib/yang.c @@ -250,6 +250,23 @@ void yang_snode_get_path(const struct lysc_node *snode, } } +struct lysc_node *yang_find_snode(struct ly_ctx *ly_ctx, const char *xpath, + uint32_t options) +{ + struct lysc_node *snode; + struct ly_set *set; + LY_ERR err; + + err = lys_find_xpath(ly_native_ctx, NULL, xpath, options, &set); + if (err || !set->count) + return NULL; + + snode = set->snodes[0]; + ly_set_free(set, NULL); + + return snode; +} + struct lysc_node *yang_snode_real_parent(const struct lysc_node *snode) { struct lysc_node *parent = snode->parent; diff --git a/lib/yang.h b/lib/yang.h index 654c246f0d..37369c09bf 100644 --- a/lib/yang.h +++ b/lib/yang.h @@ -210,6 +210,27 @@ extern void yang_snode_get_path(const struct lysc_node *snode, enum yang_path_type type, char *xpath, size_t xpath_len); + +/* + * Find libyang schema node for the given xpath. Uses `lys_find_xpath`, + * returning only the first of a set of nodes -- normally there should only + * be one. + * + * ly_ctx + * libyang context to operate on. + * + * xpath + * XPath expression (absolute or relative) to find the schema node for. + * + * options + * Libyang findxpathoptions value (see lys_find_xpath). + * + * Returns: + * The libyang schema node if found, or NULL if not found. + */ +extern struct lysc_node *yang_find_snode(struct ly_ctx *ly_ctx, + const char *xpath, uint32_t options); + /* * Find first parent schema node which is a presence-container or a list * (non-presence containers are ignored). diff --git a/lib/yang_translator.c b/lib/yang_translator.c index de668230ab..eae7577a0d 100644 --- a/lib/yang_translator.c +++ b/lib/yang_translator.c @@ -235,8 +235,8 @@ struct yang_translator *yang_translator_load(const char *path) xpath_custom = yang_dnode_get_string(set->dnodes[i], "./custom"); - snode_custom = lys_find_path(translator->ly_ctx, NULL, - xpath_custom, 0); + snode_custom = + yang_find_snode(translator->ly_ctx, xpath_custom, 0); if (!snode_custom) { flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD, "%s: unknown data path: %s", __func__, @@ -247,8 +247,7 @@ struct yang_translator *yang_translator_load(const char *path) xpath_native = yang_dnode_get_string(set->dnodes[i], "./native"); - snode_native = - lys_find_path(ly_native_ctx, NULL, xpath_native, 0); + snode_native = yang_find_snode(ly_native_ctx, xpath_native, 0); if (!snode_native) { flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD, "%s: unknown data path: %s", __func__, @@ -315,7 +314,7 @@ yang_translate_xpath(const struct yang_translator *translator, int dir, else ly_ctx = ly_native_ctx; - snode = lys_find_path(ly_ctx, NULL, xpath, 0); + snode = yang_find_snode(ly_ctx, xpath, 0); if (!snode) { flog_warn(EC_LIB_YANG_TRANSLATION_ERROR, "%s: unknown data path: %s", __func__, xpath); diff --git a/lib/yang_wrappers.c b/lib/yang_wrappers.c index 509c4dd856..dc049a374a 100644 --- a/lib/yang_wrappers.c +++ b/lib/yang_wrappers.c @@ -89,7 +89,7 @@ static const char *yang_get_default_value(const char *xpath) const struct lysc_node *snode; const char *value; - snode = lys_find_path(ly_native_ctx, NULL, xpath, 0); + snode = yang_find_snode(ly_native_ctx, xpath, 0); if (snode == NULL) { flog_err(EC_LIB_YANG_UNKNOWN_DATA_PATH, "%s: unknown data path: %s", __func__, xpath); @@ -206,7 +206,7 @@ int yang_str2enum(const char *xpath, const char *value) const struct lysc_type_enum *type; const struct lysc_type_bitenum_item *enums; - snode = lys_find_path(ly_native_ctx, NULL, xpath, 0); + snode = yang_find_snode(ly_native_ctx, xpath, 0); if (snode == NULL) { flog_err(EC_LIB_YANG_UNKNOWN_DATA_PATH, "%s: unknown data path: %s", __func__, xpath); @@ -241,7 +241,7 @@ struct yang_data *yang_data_new_enum(const char *xpath, int value) const struct lysc_type_enum *type; const struct lysc_type_bitenum_item *enums; - snode = lys_find_path(ly_native_ctx, NULL, xpath, 0); + snode = yang_find_snode(ly_native_ctx, xpath, 0); if (snode == NULL) { flog_err(EC_LIB_YANG_UNKNOWN_DATA_PATH, "%s: unknown data path: %s", __func__, xpath); diff --git a/lib/zclient.c b/lib/zclient.c index 95093a56f5..8526cbfaa1 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -427,13 +427,18 @@ enum zclient_send_status zclient_send_vrf_label(struct zclient *zclient, } enum zclient_send_status zclient_send_localsid(struct zclient *zclient, - const struct in6_addr *sid, ifindex_t oif, + const struct in6_addr *sid, vrf_id_t vrf_id, enum seg6local_action_t action, const struct seg6local_context *context) { struct prefix_ipv6 p = {}; struct zapi_route api = {}; struct zapi_nexthop *znh; + struct interface *ifp; + + ifp = if_get_vrf_loopback(vrf_id); + if (ifp == NULL) + return ZCLIENT_SEND_FAILURE; p.family = AF_INET6; p.prefixlen = IPV6_MAX_BITLEN; @@ -456,7 +461,7 @@ enum zclient_send_status zclient_send_localsid(struct zclient *zclient, memset(znh, 0, sizeof(*znh)); znh->type = NEXTHOP_TYPE_IFINDEX; - znh->ifindex = oif; + znh->ifindex = ifp->ifindex; SET_FLAG(znh->flags, ZAPI_NEXTHOP_FLAG_SEG6LOCAL); znh->seg6local_action = action; memcpy(&znh->seg6local_ctx, context, sizeof(struct seg6local_context)); @@ -4294,6 +4299,8 @@ int32_t zapi_capabilities_decode(struct stream *s, struct zapi_cap *api) memset(api, 0, sizeof(*api)); + api->safi = SAFI_UNICAST; + STREAM_GETL(s, api->cap); switch (api->cap) { case ZEBRA_CLIENT_GR_CAPABILITIES: diff --git a/lib/zclient.h b/lib/zclient.h index 367e5b1474..e43393fd70 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -901,7 +901,7 @@ zclient_send_vrf_label(struct zclient *zclient, vrf_id_t vrf_id, afi_t afi, extern enum zclient_send_status zclient_send_localsid(struct zclient *zclient, const struct in6_addr *sid, - ifindex_t oif, enum seg6local_action_t action, + vrf_id_t vrf_id, enum seg6local_action_t action, const struct seg6local_context *context); extern void zclient_send_reg_requests(struct zclient *, vrf_id_t); diff --git a/mgmtd/mgmt.h b/mgmtd/mgmt.h index 4d186c176b..9579b02230 100644 --- a/mgmtd/mgmt.h +++ b/mgmtd/mgmt.h @@ -62,6 +62,8 @@ struct mgmt_master { }; extern struct mgmt_master *mm; +extern char const *const mgmt_daemons[]; +extern uint mgmt_daemons_count; /* Inline functions */ static inline unsigned long timeval_elapsed(struct timeval a, struct timeval b) diff --git a/mgmtd/mgmt_be_adapter.c b/mgmtd/mgmt_be_adapter.c index af43cf3eae..cf7d705943 100644 --- a/mgmtd/mgmt_be_adapter.c +++ b/mgmtd/mgmt_be_adapter.c @@ -80,23 +80,23 @@ static const struct mgmt_be_xpath_map_reg xpath_static_map_reg[] = { .be_clients = (enum mgmt_be_client_id[]){ #if HAVE_STATICD - MGMTD_BE_CLIENT_ID_STATICD, + MGMTD_BE_CLIENT_ID_STATICD, #endif MGMTD_BE_CLIENT_ID_MAX}}, {.xpath_regexp = "/frr-interface:lib/*", .be_clients = (enum mgmt_be_client_id[]){ #if HAVE_STATICD - MGMTD_BE_CLIENT_ID_STATICD, + MGMTD_BE_CLIENT_ID_STATICD, #endif MGMTD_BE_CLIENT_ID_MAX}}, {.xpath_regexp = - "/frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/*", + "/frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/*", .be_clients = (enum mgmt_be_client_id[]){ #if HAVE_STATICD - MGMTD_BE_CLIENT_ID_STATICD, + MGMTD_BE_CLIENT_ID_STATICD, #endif MGMTD_BE_CLIENT_ID_MAX}}, }; @@ -347,8 +347,8 @@ mgmt_be_adapter_cleanup_old_conn(struct mgmt_be_client_adapter *adapter) struct mgmt_be_client_adapter *old; FOREACH_ADAPTER_IN_LIST (old) { - if (old != adapter - && !strncmp(adapter->name, old->name, sizeof(adapter->name))) { + if (old != adapter && + !strncmp(adapter->name, old->name, sizeof(adapter->name))) { /* * We have a Zombie lingering around */ diff --git a/mgmtd/mgmt_be_server.c b/mgmtd/mgmt_be_server.c index 0fa7ddd6d6..aa77464524 100644 --- a/mgmtd/mgmt_be_server.c +++ b/mgmtd/mgmt_be_server.c @@ -119,7 +119,7 @@ static void mgmt_be_server_start(const char *hostname) return; mgmt_be_server_start_failed: - if (sock) + if (sock > 0) close(sock); mgmt_be_listen_fd = -1; diff --git a/mgmtd/mgmt_ds.c b/mgmtd/mgmt_ds.c index 1724afb182..58c49b8789 100644 --- a/mgmtd/mgmt_ds.c +++ b/mgmtd/mgmt_ds.c @@ -87,7 +87,6 @@ static int mgmt_ds_replace_dst_with_src_ds(struct mgmt_ds_ctx *src, struct mgmt_ds_ctx *dst) { struct lyd_node *dst_dnode, *src_dnode; - struct ly_out *out; if (!src || !dst) return -1; @@ -117,13 +116,6 @@ static int mgmt_ds_replace_dst_with_src_ds(struct mgmt_ds_ctx *src, nb_config_diff_del_changes(&src->root.cfg_root->cfg_chgs); } - if (dst->ds_id == MGMTD_DS_RUNNING) { - if (ly_out_new_filepath(MGMTD_STARTUP_DS_FILE_PATH, &out) - == LY_SUCCESS) - mgmt_ds_dump_in_memory(dst, "", LYD_JSON, out); - ly_out_free(out, NULL, 0); - } - /* TODO: Update the versions if nb_config present */ return 0; @@ -134,7 +126,6 @@ static int mgmt_ds_merge_src_with_dst_ds(struct mgmt_ds_ctx *src, { int ret; struct lyd_node **dst_dnode, *src_dnode; - struct ly_out *out; if (!src || !dst) return -1; @@ -159,13 +150,6 @@ static int mgmt_ds_merge_src_with_dst_ds(struct mgmt_ds_ctx *src, nb_config_diff_del_changes(&src->root.cfg_root->cfg_chgs); } - if (dst->ds_id == MGMTD_DS_RUNNING) { - if (ly_out_new_filepath(MGMTD_STARTUP_DS_FILE_PATH, &out) - == LY_SUCCESS) - mgmt_ds_dump_in_memory(dst, "", LYD_JSON, out); - ly_out_free(out, NULL, 0); - } - return 0; } @@ -190,6 +174,7 @@ static int mgmt_ds_load_cfg_from_file(const char *filepath, void mgmt_ds_reset_candidate(void) { struct lyd_node *dnode = mm->candidate_ds->root.cfg_root->dnode; + if (dnode) yang_dnode_free(dnode); @@ -200,8 +185,6 @@ void mgmt_ds_reset_candidate(void) int mgmt_ds_init(struct mgmt_master *mm) { - struct lyd_node *root; - if (mgmt_ds_mm || mm->running_ds || mm->candidate_ds || mm->oper_ds) assert(!"MGMTD: Call ds_init only once!"); @@ -209,12 +192,6 @@ int mgmt_ds_init(struct mgmt_master *mm) if (!running_config) assert(!"MGMTD: Call ds_init after frr_init only!"); - if (mgmt_ds_load_cfg_from_file(MGMTD_STARTUP_DS_FILE_PATH, &root) - == 0) { - nb_config_free(running_config); - running_config = nb_config_new(root); - } - running.root.cfg_root = running_config; running.config_ds = true; running.ds_id = MGMTD_DS_RUNNING; @@ -523,12 +500,12 @@ int mgmt_ds_delete_data_nodes(struct mgmt_ds_ctx *ds_ctx, const char *xpath) */ return NB_ERR_NOT_FOUND; /* destroy dependant */ - if (nb_node->dep_cbs.get_dependant_xpath) { + if (nb_node && nb_node->dep_cbs.get_dependant_xpath) { nb_node->dep_cbs.get_dependant_xpath(dnode, dep_xpath); dep_dnode = yang_dnode_get( ds_ctx->config_ds ? ds_ctx->root.cfg_root->dnode - : ds_ctx->root.dnode_root, + : ds_ctx->root.dnode_root, dep_xpath); if (dep_dnode) lyd_free_tree(dep_dnode); diff --git a/mgmtd/mgmt_ds.h b/mgmtd/mgmt_ds.h index 89a2ea9425..8d01f3d5b1 100644 --- a/mgmtd/mgmt_ds.h +++ b/mgmtd/mgmt_ds.h @@ -24,8 +24,6 @@ #define MGMTD_DS_NAME_CANDIDATE "candidate" #define MGMTD_DS_NAME_OPERATIONAL "operational" -#define MGMTD_STARTUP_DS_FILE_PATH DAEMON_DB_DIR "/frr_startup.json" - #define FOREACH_MGMTD_DS_ID(id) \ for ((id) = MGMTD_DS_NONE; (id) < MGMTD_DS_MAX_ID; (id)++) diff --git a/mgmtd/mgmt_fe_adapter.c b/mgmtd/mgmt_fe_adapter.c index 5826b17de7..90e6870fd4 100644 --- a/mgmtd/mgmt_fe_adapter.c +++ b/mgmtd/mgmt_fe_adapter.c @@ -495,7 +495,8 @@ static int mgmt_fe_send_setcfg_reply(struct mgmt_fe_session_ctx *session, if (implicit_commit) { if (mm->perf_stats_en) - gettimeofday(&session->adapter->cmt_stats.last_end, NULL); + gettimeofday(&session->adapter->cmt_stats.last_end, + NULL); mgmt_fe_session_compute_commit_timers( &session->adapter->cmt_stats); } @@ -715,8 +716,8 @@ mgmt_fe_adapter_cleanup_old_conn(struct mgmt_fe_client_adapter *adapter) struct mgmt_fe_client_adapter *old; FOREACH_ADAPTER_IN_LIST (old) { - if (old != adapter - && !strncmp(adapter->name, old->name, sizeof(adapter->name))) { + if (old != adapter && + !strncmp(adapter->name, old->name, sizeof(adapter->name))) { /* * We have a Zombie lingering around */ @@ -1109,8 +1110,8 @@ mgmt_fe_session_handle_getdata_req_msg(struct mgmt_fe_session_ctx *session, MGMTD_TXN_TYPE_SHOW); if (session->txn_id == MGMTD_SESSION_ID_NONE) { mgmt_fe_send_getdata_reply( - session, getdata_req->ds_id, getdata_req->req_id, - false, NULL, + session, getdata_req->ds_id, + getdata_req->req_id, false, NULL, "Failed to create a Show transaction!"); goto mgmt_fe_sess_handle_getdata_req_failed; } @@ -1780,8 +1781,8 @@ mgmt_fe_adapter_cmt_stats_write(struct vty *vty, sizeof(buf))); vty_out(vty, " Apply-Config Start: \t\t%s\n", mgmt_realtime_to_string( - &adapter->cmt_stats.apply_cfg_start, buf, - sizeof(buf))); + &adapter->cmt_stats.apply_cfg_start, + buf, sizeof(buf))); vty_out(vty, " Apply-Config End: \t\t%s\n", mgmt_realtime_to_string( &adapter->cmt_stats.apply_cfg_end, buf, @@ -1818,8 +1819,9 @@ mgmt_fe_adapter_setcfg_stats_write(struct vty *vty, adapter->setcfg_stats.avg_tm); vty_out(vty, " Last-Set-Cfg-Details:\n"); vty_out(vty, " Set-Cfg Start: \t\t\t%s\n", - mgmt_realtime_to_string(&adapter->setcfg_stats.last_start, - buf, sizeof(buf))); + mgmt_realtime_to_string( + &adapter->setcfg_stats.last_start, buf, + sizeof(buf))); vty_out(vty, " Set-Cfg End: \t\t\t%s\n", mgmt_realtime_to_string(&adapter->setcfg_stats.last_end, buf, sizeof(buf))); @@ -1894,9 +1896,11 @@ void mgmt_fe_adapter_reset_perf_stats(struct vty *vty) struct mgmt_fe_session_ctx *session; FOREACH_ADAPTER_IN_LIST (adapter) { - memset(&adapter->setcfg_stats, 0, sizeof(adapter->setcfg_stats)); + memset(&adapter->setcfg_stats, 0, + sizeof(adapter->setcfg_stats)); FOREACH_SESSION_IN_LIST (adapter, session) { - memset(&adapter->cmt_stats, 0, sizeof(adapter->cmt_stats)); + memset(&adapter->cmt_stats, 0, + sizeof(adapter->cmt_stats)); } } } diff --git a/mgmtd/mgmt_fe_server.c b/mgmtd/mgmt_fe_server.c index 6097c23aac..e8bbe139bb 100644 --- a/mgmtd/mgmt_fe_server.c +++ b/mgmtd/mgmt_fe_server.c @@ -119,7 +119,7 @@ static void mgmt_fe_server_start(const char *hostname) return; mgmt_fe_server_start_failed: - if (sock) + if (sock > 0) close(sock); mgmt_fe_listen_fd = -1; diff --git a/mgmtd/mgmt_history.c b/mgmtd/mgmt_history.c index 6f9f2dd63f..2251c49f1c 100644 --- a/mgmtd/mgmt_history.c +++ b/mgmtd/mgmt_history.c @@ -33,7 +33,7 @@ DECLARE_DLIST(mgmt_cmt_infos, struct mgmt_cmt_info_t, cmts); * The only instance of VTY session that has triggered an ongoing * config rollback operation. */ -static struct vty *rollback_vty = NULL; +static struct vty *rollback_vty; static bool mgmt_history_record_exists(char *file_path) { @@ -82,7 +82,7 @@ static struct mgmt_cmt_info_t *mgmt_history_create_cmt_rec(void) mgmt_realtime_to_string(&cmt_recd_tv, new->time_str, sizeof(new->time_str)); mgmt_history_hash(new->time_str, new->cmtid_str); - snprintf(new->cmt_json_file, sizeof(new->cmt_json_file), + snprintf(new->cmt_json_file, sizeof(new->cmt_json_file) - 1, MGMTD_COMMIT_FILE_PATH, new->cmtid_str); if (mgmt_cmt_infos_count(&mm->cmts) == MGMTD_MAX_COMMIT_LIST) { @@ -100,7 +100,8 @@ static struct mgmt_cmt_info_t *mgmt_history_create_cmt_rec(void) return new; } -static struct mgmt_cmt_info_t *mgmt_history_find_cmt_record(const char *cmtid_str) +static struct mgmt_cmt_info_t * +mgmt_history_find_cmt_record(const char *cmtid_str) { struct mgmt_cmt_info_t *cmt_info; @@ -129,7 +130,8 @@ static bool mgmt_history_read_cmt_record_index(void) while ((fread(&cmt_info, sizeof(cmt_info), 1, fp)) > 0) { if (cnt < MGMTD_MAX_COMMIT_LIST) { - if (!mgmt_history_record_exists(cmt_info.cmt_json_file)) { + if (!mgmt_history_record_exists( + cmt_info.cmt_json_file)) { zlog_err( "Commit record present in index_file, but commit file %s missing", cmt_info.cmt_json_file); @@ -282,7 +284,8 @@ int mgmt_history_rollback_by_id(struct vty *vty, const char *cmtid_str) FOREACH_CMT_REC (mm, cmt_info) { if (strncmp(cmt_info->cmtid_str, cmtid_str, MGMTD_MD5_HASH_STR_HEX_LEN) == 0) { - ret = mgmt_history_rollback_to_cmt(vty, cmt_info, false); + ret = mgmt_history_rollback_to_cmt(vty, cmt_info, + false); return ret; } @@ -321,7 +324,8 @@ int mgmt_history_rollback_n(struct vty *vty, int num_cmts) FOREACH_CMT_REC (mm, cmt_info) { if (cnt == num_cmts) { - ret = mgmt_history_rollback_to_cmt(vty, cmt_info, false); + ret = mgmt_history_rollback_to_cmt(vty, cmt_info, + false); return ret; } @@ -356,6 +360,7 @@ void show_mgmt_cmt_history(struct vty *vty) void mgmt_history_new_record(struct mgmt_ds_ctx *ds_ctx) { struct mgmt_cmt_info_t *cmt_info = mgmt_history_create_cmt_rec(); + mgmt_ds_dump_ds_to_file(cmt_info->cmt_json_file, ds_ctx); mgmt_history_dump_cmt_record_index(); } diff --git a/mgmtd/mgmt_history.h b/mgmtd/mgmt_history.h index 29a1d7738e..5f96cf90e1 100644 --- a/mgmtd/mgmt_history.h +++ b/mgmtd/mgmt_history.h @@ -1,10 +1,10 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2021 Vmware, Inc. - * Pushpasis Sarkar <spushpasis@vmware.com> - * Copyright (c) 2023, LabN Consulting, L.L.C. - * - */ + * Copyright (C) 2021 Vmware, Inc. + * Pushpasis Sarkar <spushpasis@vmware.com> + * Copyright (c) 2023, LabN Consulting, L.L.C. + * + */ #ifndef _FRR_MGMTD_HISTORY_H_ #define _FRR_MGMTD_HISTORY_H_ diff --git a/mgmtd/mgmt_main.c b/mgmtd/mgmt_main.c index 7d176059f5..08c999260d 100644 --- a/mgmtd/mgmt_main.c +++ b/mgmtd/mgmt_main.c @@ -17,6 +17,13 @@ #include "routing_nb.h" +char const *const mgmt_daemons[] = { +#ifdef HAVE_STATICD + "staticd", +#endif +}; +uint mgmt_daemons_count = array_size(mgmt_daemons); + /* mgmt options, we use GNU getopt library. */ static const struct option longopts[] = { {"skip_runas", no_argument, NULL, 'S'}, diff --git a/mgmtd/mgmt_txn.c b/mgmtd/mgmt_txn.c index 5fa8aabfd6..2ba0cb413a 100644 --- a/mgmtd/mgmt_txn.c +++ b/mgmtd/mgmt_txn.c @@ -1258,6 +1258,7 @@ static int mgmt_txn_prepare_config(struct mgmt_txn_ctx *txn) char err_buf[1024] = {0}; nb_ctx.client = NB_CLIENT_MGMTD_SERVER; nb_ctx.user = (void *)txn; + ret = nb_candidate_validate_yang(nb_config, false, err_buf, sizeof(err_buf) - 1); if (ret != NB_OK) { @@ -1787,27 +1788,10 @@ static int mgmt_txn_get_config(struct mgmt_txn_ctx *txn, struct mgmt_txn_req *txn_req, struct mgmt_ds_ctx *ds_ctx) { - struct mgmt_txn_reqs_head *req_list = NULL; - struct mgmt_txn_reqs_head *pending_list = NULL; int indx; struct mgmt_get_data_req *get_data; struct mgmt_get_data_reply *get_reply; - switch (txn_req->req_event) { - case MGMTD_TXN_PROC_GETCFG: - req_list = &txn->get_cfg_reqs; - break; - case MGMTD_TXN_PROC_GETDATA: - req_list = &txn->get_data_reqs; - break; - case MGMTD_TXN_PROC_SETCFG: - case MGMTD_TXN_PROC_COMMITCFG: - case MGMTD_TXN_COMMITCFG_TIMEOUT: - case MGMTD_TXN_CLEANUP: - assert(!"Wrong txn request type!"); - break; - } - get_data = txn_req->req.get_data; if (!get_data->reply) { @@ -1852,24 +1836,11 @@ static int mgmt_txn_get_config(struct mgmt_txn_ctx *txn, mgmt_txn_get_config_failed: - if (pending_list) { - /* - * Move the transaction to corresponding pending list. - */ - if (req_list) - mgmt_txn_reqs_del(req_list, txn_req); - txn_req->pending_be_proc = true; - mgmt_txn_reqs_add_tail(pending_list, txn_req); - MGMTD_TXN_DBG( - "Moved Req: %p for Txn: %p from Req-List to Pending-List", - txn_req, txn_req->txn); - } else { - /* - * Delete the txn request. It will also remove it from request - * list. - */ - mgmt_txn_req_free(&txn_req); - } + /* + * Delete the txn request. It will also remove it from request + * list. + */ + mgmt_txn_req_free(&txn_req); return 0; } diff --git a/mgmtd/mgmt_vty.c b/mgmtd/mgmt_vty.c index 898eb43915..fa34d90c75 100644 --- a/mgmtd/mgmt_vty.c +++ b/mgmtd/mgmt_vty.c @@ -10,6 +10,8 @@ #include "command.h" #include "json.h" +#include "northbound_cli.h" + #include "mgmtd/mgmt.h" #include "mgmtd/mgmt_be_server.h" #include "mgmtd/mgmt_be_adapter.h" @@ -389,6 +391,7 @@ static struct cmd_node debug_node = { static int config_write_mgmt_debug_helper(struct vty *vty, bool config) { int n = mgmt_debug_be + mgmt_debug_fe + mgmt_debug_ds + mgmt_debug_txn; + if (!n) return 0; @@ -440,6 +443,7 @@ DEFPY(debug_mgmt, debug_mgmt_cmd, "Transaction debug\n") { bool set = !no; + if (all) be = fe = ds = txn = set ? all : NULL; @@ -455,6 +459,33 @@ DEFPY(debug_mgmt, debug_mgmt_cmd, return CMD_SUCCESS; } +/* + * Analog of `frr_config_read_in()`, instead of our config file though we loop + * over all daemons that have transitioned to mgmtd, loading their configs + */ +static int mgmt_config_pre_hook(struct event_loop *loop) +{ + FILE *confp; + char *p; + + for (uint i = 0; i < mgmt_daemons_count; i++) { + p = asprintfrr(MTYPE_TMP, "%s/%s.conf", frr_sysconfdir, + mgmt_daemons[i]); + confp = fopen(p, "r"); + if (confp == NULL) { + if (errno != ENOENT) + zlog_err("%s: couldn't read config file %s: %s", + __func__, p, safe_strerror(errno)); + } else { + zlog_info("mgmtd: reading daemon config from %s", p); + vty_read_file(vty_shared_candidate_config, confp); + fclose(confp); + } + XFREE(MTYPE_TMP, p); + } + return 0; +} + void mgmt_vty_init(void) { /* @@ -468,6 +499,8 @@ void mgmt_vty_init(void) static_vty_init(); #endif + hook_register(frr_config_pre, mgmt_config_pre_hook); + install_node(&debug_node); install_element(VIEW_NODE, &show_mgmt_be_adapter_cmd); diff --git a/nhrpd/debug.h b/nhrpd/debug.h index e9428fa90a..f2c7022ad4 100644 --- a/nhrpd/debug.h +++ b/nhrpd/debug.h @@ -1,13 +1,5 @@ #include "log.h" -#if defined(__GNUC__) && (__GNUC__ >= 3) -#define likely(_x) __builtin_expect(!!(_x), 1) -#define unlikely(_x) __builtin_expect(!!(_x), 0) -#else -#define likely(_x) !!(_x) -#define unlikely(_x) !!(_x) -#endif - #define NHRP_DEBUG_COMMON (1 << 0) #define NHRP_DEBUG_KERNEL (1 << 1) #define NHRP_DEBUG_IF (1 << 2) diff --git a/ospf6d/ospf6_gr_helper.c b/ospf6d/ospf6_gr_helper.c index d313510cfa..216d78c1cc 100644 --- a/ospf6d/ospf6_gr_helper.c +++ b/ospf6d/ospf6_gr_helper.c @@ -937,12 +937,22 @@ static void show_ospf6_gr_helper_details(struct vty *vty, struct ospf6 *ospf6, (ospf6->ospf6_helper_cfg.strict_lsa_check) ? "Enabled" : "Disabled"); + +#if CONFDATE > 20240401 + CPP_NOTICE("Remove deprecated json key: restartSupoort") +#endif json_object_string_add( json, "restartSupoort", (ospf6->ospf6_helper_cfg.only_planned_restart) ? "Planned Restart only" : "Planned and Unplanned Restarts"); + json_object_string_add( + json, "restartSupport", + (ospf6->ospf6_helper_cfg.only_planned_restart) + ? "Planned Restart only" + : "Planned and Unplanned Restarts"); + json_object_int_add( json, "supportedGracePeriod", ospf6->ospf6_helper_cfg.supported_grace_time); diff --git a/ospf6d/ospf6_spf.c b/ospf6d/ospf6_spf.c index 3cc0d5e963..0990b14307 100644 --- a/ospf6d/ospf6_spf.c +++ b/ospf6d/ospf6_spf.c @@ -310,7 +310,7 @@ static void ospf6_nexthop_calc(struct ospf6_vertex *w, struct ospf6_vertex *v, static int ospf6_spf_install(struct ospf6_vertex *v, struct ospf6_route_table *result_table) { - struct ospf6_route *route, *parent_route; + struct ospf6_route *route; struct ospf6_vertex *prev; if (IS_OSPF6_DEBUG_SPF(PROCESS)) @@ -330,7 +330,12 @@ static int ospf6_spf_install(struct ospf6_vertex *v, zlog_debug( " another path found to route %pFX lsa %s, merge", &route->prefix, v->lsa->name); - ospf6_spf_merge_nexthops_to_route(route, v); + + /* merging the parent's nexthop information to the child's + * if the parent is not the root of the tree. + */ + if (!ospf6_merge_parents_nh_to_child(v, route, result_table)) + ospf6_spf_merge_nexthops_to_route(route, v); prev = (struct ospf6_vertex *)route->route_option; assert(prev->hops <= v->hops); @@ -396,13 +401,7 @@ static int ospf6_spf_install(struct ospf6_vertex *v, * installed, * its parent's route's nexthops have already been installed. */ - if (v->parent && v->parent->hops) { - parent_route = - ospf6_route_lookup(&v->parent->vertex_id, result_table); - if (parent_route) { - ospf6_route_merge_nexthops(route, parent_route); - } - } + ospf6_merge_parents_nh_to_child(v, route, result_table); if (v->parent) listnode_add_sort(v->parent->child_list, v); @@ -1275,3 +1274,20 @@ void ospf6_ase_calculate_timer_add(struct ospf6 *ospf6) event_add_timer(master, ospf6_ase_calculate_timer, ospf6, OSPF6_ASE_CALC_INTERVAL, &ospf6->t_ase_calc); } + +bool ospf6_merge_parents_nh_to_child(struct ospf6_vertex *v, + struct ospf6_route *route, + struct ospf6_route_table *result_table) +{ + struct ospf6_route *parent_route; + + if (v->parent && v->parent->hops) { + parent_route = + ospf6_route_lookup(&v->parent->vertex_id, result_table); + if (parent_route) { + ospf6_route_merge_nexthops(route, parent_route); + return true; + } + } + return false; +} diff --git a/ospf6d/ospf6_spf.h b/ospf6d/ospf6_spf.h index 55ca3ec4fe..c2fd5d9ef1 100644 --- a/ospf6d/ospf6_spf.h +++ b/ospf6d/ospf6_spf.h @@ -152,4 +152,8 @@ extern void ospf6_remove_temp_router_lsa(struct ospf6_area *area); extern void ospf6_ase_calculate_timer_add(struct ospf6 *ospf6); extern int ospf6_ase_calculate_route(struct ospf6 *ospf6, struct ospf6_lsa *lsa, struct ospf6_area *area); +extern bool +ospf6_merge_parents_nh_to_child(struct ospf6_vertex *v, + struct ospf6_route *route, + struct ospf6_route_table *result_table); #endif /* OSPF6_SPF_H */ diff --git a/ospfd/ospf_abr.c b/ospfd/ospf_abr.c index e14586c5ee..ded520889f 100644 --- a/ospfd/ospf_abr.c +++ b/ospfd/ospf_abr.c @@ -54,6 +54,7 @@ static void ospf_area_range_free(struct ospf_area_range *range) } static void ospf_area_range_add(struct ospf_area *area, + struct route_table *ranges, struct ospf_area_range *range) { struct route_node *rn; @@ -64,7 +65,7 @@ static void ospf_area_range_add(struct ospf_area *area, p.prefix = range->addr; apply_mask_ipv4(&p); - rn = route_node_get(area->ranges, (struct prefix *)&p); + rn = route_node_get(ranges, (struct prefix *)&p); if (rn->info) route_unlock_node(rn); else @@ -75,10 +76,12 @@ static void ospf_area_range_delete(struct ospf_area *area, struct route_node *rn) { struct ospf_area_range *range = rn->info; + bool nssa = CHECK_FLAG(range->flags, OSPF_AREA_RANGE_NSSA); - if (range->specifics != 0) + if (ospf_area_range_active(range) && + CHECK_FLAG(range->flags, OSPF_AREA_RANGE_ADVERTISE)) ospf_delete_discard_route(area->ospf, area->ospf->new_table, - (struct prefix_ipv4 *)&rn->p); + (struct prefix_ipv4 *)&rn->p, nssa); ospf_area_range_free(range); rn->info = NULL; @@ -87,11 +90,12 @@ static void ospf_area_range_delete(struct ospf_area *area, } struct ospf_area_range *ospf_area_range_lookup(struct ospf_area *area, + struct route_table *ranges, struct prefix_ipv4 *p) { struct route_node *rn; - rn = route_node_lookup(area->ranges, (struct prefix *)p); + rn = route_node_lookup(ranges, (struct prefix *)p); if (rn) { route_unlock_node(rn); return rn->info; @@ -133,11 +137,12 @@ struct ospf_area_range *ospf_area_range_lookup_next(struct ospf_area *area, } static struct ospf_area_range *ospf_area_range_match(struct ospf_area *area, + struct route_table *ranges, struct prefix_ipv4 *p) { struct route_node *node; - node = route_node_match(area->ranges, (struct prefix *)p); + node = route_node_match(ranges, (struct prefix *)p); if (node) { route_unlock_node(node); return node->info; @@ -153,7 +158,7 @@ struct ospf_area_range *ospf_area_range_match_any(struct ospf *ospf, struct listnode *node; for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) - if ((range = ospf_area_range_match(area, p))) + if ((range = ospf_area_range_match(area, area->ranges, p))) return range; return NULL; @@ -169,17 +174,13 @@ static int ospf_area_actively_attached(struct ospf_area *area) return area->act_ints; } -int ospf_area_range_set(struct ospf *ospf, struct in_addr area_id, - struct prefix_ipv4 *p, int advertise) +int ospf_area_range_set(struct ospf *ospf, struct ospf_area *area, + struct route_table *ranges, struct prefix_ipv4 *p, + int advertise, bool nssa) { - struct ospf_area *area; struct ospf_area_range *range; - area = ospf_area_get(ospf, area_id); - if (area == NULL) - return 0; - - range = ospf_area_range_lookup(area, p); + range = ospf_area_range_lookup(area, ranges, p); if (range != NULL) { if (!CHECK_FLAG(advertise, OSPF_AREA_RANGE_ADVERTISE)) range->cost_config = OSPF_AREA_RANGE_COST_UNSPEC; @@ -190,7 +191,7 @@ int ospf_area_range_set(struct ospf *ospf, struct in_addr area_id, ospf_schedule_abr_task(ospf); } else { range = ospf_area_range_new(p); - ospf_area_range_add(area, range); + ospf_area_range_add(area, ranges, range); ospf_schedule_abr_task(ospf); } @@ -201,20 +202,19 @@ int ospf_area_range_set(struct ospf *ospf, struct in_addr area_id, range->cost_config = OSPF_AREA_RANGE_COST_UNSPEC; } + if (nssa) + SET_FLAG(range->flags, OSPF_AREA_RANGE_NSSA); + return 1; } -int ospf_area_range_cost_set(struct ospf *ospf, struct in_addr area_id, - struct prefix_ipv4 *p, uint32_t cost) +int ospf_area_range_cost_set(struct ospf *ospf, struct ospf_area *area, + struct route_table *ranges, struct prefix_ipv4 *p, + uint32_t cost) { - struct ospf_area *area; struct ospf_area_range *range; - area = ospf_area_get(ospf, area_id); - if (area == NULL) - return 0; - - range = ospf_area_range_lookup(area, p); + range = ospf_area_range_lookup(area, ranges, p); if (range == NULL) return 0; @@ -227,17 +227,12 @@ int ospf_area_range_cost_set(struct ospf *ospf, struct in_addr area_id, return 1; } -int ospf_area_range_unset(struct ospf *ospf, struct in_addr area_id, - struct prefix_ipv4 *p) +int ospf_area_range_unset(struct ospf *ospf, struct ospf_area *area, + struct route_table *ranges, struct prefix_ipv4 *p) { - struct ospf_area *area; struct route_node *rn; - area = ospf_area_lookup_by_area_id(ospf, area_id); - if (area == NULL) - return 0; - - rn = route_node_lookup(area->ranges, (struct prefix *)p); + rn = route_node_lookup(ranges, (struct prefix *)p); if (rn == NULL) return 0; @@ -249,14 +244,12 @@ int ospf_area_range_unset(struct ospf *ospf, struct in_addr area_id, return 1; } -int ospf_area_range_substitute_set(struct ospf *ospf, struct in_addr area_id, +int ospf_area_range_substitute_set(struct ospf *ospf, struct ospf_area *area, struct prefix_ipv4 *p, struct prefix_ipv4 *s) { - struct ospf_area *area; struct ospf_area_range *range; - area = ospf_area_get(ospf, area_id); - range = ospf_area_range_lookup(area, p); + range = ospf_area_range_lookup(area, area->ranges, p); if (range != NULL) { if (!CHECK_FLAG(range->flags, OSPF_AREA_RANGE_ADVERTISE) @@ -264,7 +257,7 @@ int ospf_area_range_substitute_set(struct ospf *ospf, struct in_addr area_id, ospf_schedule_abr_task(ospf); } else { range = ospf_area_range_new(p); - ospf_area_range_add(area, range); + ospf_area_range_add(area, area->ranges, range); ospf_schedule_abr_task(ospf); } @@ -276,17 +269,12 @@ int ospf_area_range_substitute_set(struct ospf *ospf, struct in_addr area_id, return 1; } -int ospf_area_range_substitute_unset(struct ospf *ospf, struct in_addr area_id, +int ospf_area_range_substitute_unset(struct ospf *ospf, struct ospf_area *area, struct prefix_ipv4 *p) { - struct ospf_area *area; struct ospf_area_range *range; - area = ospf_area_lookup_by_area_id(ospf, area_id); - if (area == NULL) - return 0; - - range = ospf_area_range_lookup(area, p); + range = ospf_area_range_lookup(area, area->ranges, p); if (range == NULL) return 0; @@ -538,8 +526,7 @@ void ospf_check_abr_status(struct ospf *ospf) } static void ospf_abr_update_aggregate(struct ospf_area_range *range, - struct ospf_route * or, - struct ospf_area *area) + uint32_t cost, struct ospf_area *area) { if (IS_DEBUG_OSPF_EVENT) zlog_debug("%s: Start", __func__); @@ -557,20 +544,18 @@ static void ospf_abr_update_aggregate(struct ospf_area_range *range, range->cost = range->cost_config; } else { - if (range->specifics == 0) { + if (!ospf_area_range_active(range)) { if (IS_DEBUG_OSPF_EVENT) - zlog_debug("%s: use or->cost %d", __func__, - or->cost); + zlog_debug("%s: use cost %d", __func__, cost); - range->cost = or->cost; /* 1st time get 1st cost */ + range->cost = cost; /* 1st time get 1st cost */ } - if (or->cost > range->cost) { + if (cost > range->cost) { if (IS_DEBUG_OSPF_EVENT) - zlog_debug("%s: update to %d", __func__, - or->cost); + zlog_debug("%s: update to %d", __func__, cost); - range->cost = or->cost; + range->cost = cost; } } @@ -605,6 +590,7 @@ static int ospf_abr_translate_nssa(struct ospf_area *area, struct ospf_lsa *lsa) struct ospf_lsa *old = NULL, *new = NULL; struct as_external_lsa *ext7; struct prefix_ipv4 p; + struct ospf_area_range *range; if (!CHECK_FLAG(lsa->data->options, OSPF_OPTION_NP)) { if (IS_DEBUG_OSPF_NSSA) @@ -646,6 +632,18 @@ static int ospf_abr_translate_nssa(struct ospf_area *area, struct ospf_lsa *lsa) return 1; } + range = ospf_area_range_match(area, area->nssa_ranges, &p); + if (range) { + if (IS_DEBUG_OSPF_NSSA) + zlog_debug("Suppressed by range %pI4/%u of area %pI4", + &range->addr, range->masklen, + &area->area_id); + + ospf_abr_update_aggregate(range, GET_METRIC(ext7->e[0].metric), + area); + return 1; + } + if (old && CHECK_FLAG(old->flags, OSPF_LSA_APPROVED)) { if (IS_DEBUG_OSPF_NSSA) zlog_debug( @@ -675,17 +673,27 @@ static int ospf_abr_translate_nssa(struct ospf_area *area, struct ospf_lsa *lsa) } } - /* Area where Aggregate testing will be inserted, just like summary - advertisements */ - /* ospf_abr_check_nssa_range (p_arg, lsa-> cost, lsa -> area); */ - return 0; } -static void ospf_abr_translate_nssa_range(struct prefix_ipv4 *p, uint32_t cost) +static void ospf_abr_translate_nssa_range(struct ospf *ospf, + struct prefix_ipv4 *p, uint32_t cost) { - /* The Type-7 is created from the aggregated prefix and forwarded - for lsa installation and flooding... to be added... */ + struct external_info ei = {}; + struct ospf_lsa *lsa; + + prefix_copy(&ei.p, p); + ei.type = ZEBRA_ROUTE_OSPF; + ei.route_map_set.metric = cost; + ei.route_map_set.metric_type = -1; + + lsa = ospf_external_info_find_lsa(ospf, p); + if (lsa) + lsa = ospf_external_lsa_refresh(ospf, lsa, &ei, + LSA_REFRESH_FORCE, true); + else + lsa = ospf_external_lsa_originate(ospf, &ei); + SET_FLAG(lsa->flags, OSPF_LSA_LOCAL_XLT); } void ospf_abr_announce_network_to_area(struct prefix_ipv4 *p, uint32_t cost, @@ -892,9 +900,11 @@ static void ospf_abr_announce_network(struct ospf *ospf, struct prefix_ipv4 *p, zlog_debug( "%s: this is intra-area route to %pFX", __func__, p); - if ((range = ospf_area_range_match(or_area, p)) - && !ospf_area_is_transit(area)) - ospf_abr_update_aggregate(range, or, area); + if ((range = ospf_area_range_match( + or_area, or_area->ranges, p)) && + !ospf_area_is_transit(area)) + ospf_abr_update_aggregate(range, or->cost, + area); else ospf_abr_announce_network_to_area(p, or->cost, area); @@ -1345,7 +1355,7 @@ static void ospf_abr_unapprove_summaries(struct ospf *ospf) zlog_debug("%s: Stop", __func__); } -static void ospf_abr_prepare_aggregates(struct ospf *ospf) +static void ospf_abr_prepare_aggregates(struct ospf *ospf, bool nssa) { struct listnode *node; struct route_node *rn; @@ -1356,7 +1366,14 @@ static void ospf_abr_prepare_aggregates(struct ospf *ospf) zlog_debug("%s: Start", __func__); for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) { - for (rn = route_top(area->ranges); rn; rn = route_next(rn)) + struct route_table *ranges; + + if (nssa) + ranges = area->nssa_ranges; + else + ranges = area->ranges; + + for (rn = route_top(ranges); rn; rn = route_next(rn)) if ((range = rn->info) != NULL) { range->cost = 0; range->specifics = 0; @@ -1409,7 +1426,7 @@ static void ospf_abr_announce_aggregates(struct ospf *ospf) p.prefixlen = range->subst_masklen; } - if (range->specifics) { + if (ospf_area_range_active(range)) { if (IS_DEBUG_OSPF_EVENT) zlog_debug("%s: active range", __func__); @@ -1452,13 +1469,11 @@ static void ospf_abr_announce_aggregates(struct ospf *ospf) zlog_debug("%s: Stop", __func__); } -static void -ospf_abr_send_nssa_aggregates(struct ospf *ospf) /* temporarily turned off */ +static void ospf_abr_send_nssa_aggregates(struct ospf *ospf) { - struct listnode *node; /*, n; */ - struct ospf_area *area; /*, *ar; */ + struct listnode *node; + struct ospf_area *area; struct route_node *rn; - struct ospf_area_range *range; struct prefix_ipv4 p; if (IS_DEBUG_OSPF_NSSA) @@ -1472,20 +1487,13 @@ ospf_abr_send_nssa_aggregates(struct ospf *ospf) /* temporarily turned off */ zlog_debug("%s: looking at area %pI4", __func__, &area->area_id); - for (rn = route_top(area->ranges); rn; rn = route_next(rn)) { - if (rn->info == NULL) - continue; + for (rn = route_top(area->nssa_ranges); rn; + rn = route_next(rn)) { + struct ospf_area_range *range; range = rn->info; - - if (!CHECK_FLAG(range->flags, - OSPF_AREA_RANGE_ADVERTISE)) { - if (IS_DEBUG_OSPF_NSSA) - zlog_debug( - "%s: discarding suppress-ranges", - __func__); + if (!range) continue; - } p.family = AF_INET; p.prefix = range->addr; @@ -1495,14 +1503,9 @@ ospf_abr_send_nssa_aggregates(struct ospf *ospf) /* temporarily turned off */ zlog_debug("%s: this is range: %pFX", __func__, &p); - if (CHECK_FLAG(range->flags, - OSPF_AREA_RANGE_SUBSTITUTE)) { - p.family = AF_INET; - p.prefix = range->subst_addr; - p.prefixlen = range->subst_masklen; - } - - if (range->specifics) { + if (ospf_area_range_active(range) + && CHECK_FLAG(range->flags, + OSPF_AREA_RANGE_ADVERTISE)) { if (IS_DEBUG_OSPF_NSSA) zlog_debug("%s: active range", __func__); @@ -1512,7 +1515,8 @@ ospf_abr_send_nssa_aggregates(struct ospf *ospf) /* temporarily turned off */ * translate, Install (as Type-5), Approve, and * Flood */ - ospf_abr_translate_nssa_range(&p, range->cost); + ospf_abr_translate_nssa_range(ospf, &p, + range->cost); } } /* all area ranges*/ } /* all areas */ @@ -1807,6 +1811,82 @@ static void ospf_abr_announce_non_dna_routers(struct event *thread) OSPF_LOG_DEBUG(IS_DEBUG_OSPF_EVENT, "%s(): Stop", __func__); } +static void ospf_abr_nssa_type7_default_create(struct ospf *ospf, + struct ospf_area *area, + struct ospf_lsa *lsa) +{ + struct external_info ei; + + if (IS_DEBUG_OSPF_NSSA) + zlog_debug( + "Announcing Type-7 default route into NSSA area %pI4", + &area->area_id); + + /* Prepare the extrenal_info for aggregator */ + memset(&ei, 0, sizeof(struct external_info)); + ei.p.family = AF_INET; + ei.p.prefixlen = 0; + ei.tag = 0; + ei.type = 0; + ei.instance = ospf->instance; + + /* Compute default route type and metric. */ + if (area->nssa_default_originate.metric_value != -1) + ei.route_map_set.metric = + area->nssa_default_originate.metric_value; + else + ei.route_map_set.metric = DEFAULT_DEFAULT_ALWAYS_METRIC; + if (area->nssa_default_originate.metric_type != -1) + ei.route_map_set.metric_type = + area->nssa_default_originate.metric_type; + else + ei.route_map_set.metric_type = DEFAULT_METRIC_TYPE; + + if (!lsa) + ospf_nssa_lsa_originate(area, &ei); + else + ospf_nssa_lsa_refresh(area, lsa, &ei); +} + +static void ospf_abr_nssa_type7_default_delete(struct ospf *ospf, + struct ospf_area *area, + struct ospf_lsa *lsa) +{ + if (lsa && !CHECK_FLAG(lsa->flags, OSPF_LSA_IN_MAXAGE)) { + if (IS_DEBUG_OSPF_NSSA) + zlog_debug( + "Withdrawing Type-7 default route from area %pI4", + &area->area_id); + + ospf_ls_retransmit_delete_nbr_area(area, lsa); + ospf_refresher_unregister_lsa(ospf, lsa); + ospf_lsa_flush_area(lsa, area); + } +} + +/* NSSA Type-7 default route. */ +void ospf_abr_nssa_type7_defaults(struct ospf *ospf) +{ + struct ospf_area *area; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) { + struct in_addr id = {}; + struct ospf_lsa *lsa; + + lsa = ospf_lsdb_lookup_by_id(area->lsdb, OSPF_AS_NSSA_LSA, id, + area->ospf->router_id); + if (area->external_routing == OSPF_AREA_NSSA + && area->nssa_default_originate.enabled + && (IS_OSPF_ABR(ospf) + || (IS_OSPF_ASBR(ospf) + && ospf->nssa_default_import_check.status))) + ospf_abr_nssa_type7_default_create(ospf, area, lsa); + else + ospf_abr_nssa_type7_default_delete(ospf, area, lsa); + } +} + static int ospf_abr_remove_unapproved_translates_apply(struct ospf *ospf, struct ospf_lsa *lsa) { @@ -1874,30 +1954,39 @@ static void ospf_abr_remove_unapproved_summaries(struct ospf *ospf) zlog_debug("%s: Stop", __func__); } -static void ospf_abr_manage_discard_routes(struct ospf *ospf) +static void ospf_abr_manage_discard_routes(struct ospf *ospf, bool nssa) { struct listnode *node, *nnode; struct route_node *rn; struct ospf_area *area; - struct ospf_area_range *range; - for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area)) - for (rn = route_top(area->ranges); rn; rn = route_next(rn)) - if ((range = rn->info) != NULL) - if (CHECK_FLAG(range->flags, - OSPF_AREA_RANGE_ADVERTISE)) { - if (range->specifics) - ospf_add_discard_route( - ospf, ospf->new_table, - area, - (struct prefix_ipv4 - *)&rn->p); - else - ospf_delete_discard_route( - ospf, ospf->new_table, - (struct prefix_ipv4 - *)&rn->p); - } + for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area)) { + struct route_table *ranges; + + if (nssa) + ranges = area->nssa_ranges; + else + ranges = area->ranges; + + for (rn = route_top(ranges); rn; rn = route_next(rn)) { + struct ospf_area_range *range; + + range = rn->info; + if (!range) + continue; + + if (ospf_area_range_active(range) + && CHECK_FLAG(range->flags, + OSPF_AREA_RANGE_ADVERTISE)) + ospf_add_discard_route( + ospf, ospf->new_table, area, + (struct prefix_ipv4 *)&rn->p, nssa); + else + ospf_delete_discard_route( + ospf, ospf->new_table, + (struct prefix_ipv4 *)&rn->p, nssa); + } + } } /* This is the function taking care about ABR NSSA, i.e. NSSA @@ -1925,7 +2014,7 @@ static void ospf_abr_manage_discard_routes(struct ospf *ospf) For External Calculations, any NSSA areas use the Type-7 AREA-LSDB, any ABR-non-NSSA areas use the Type-5 GLOBAL-LSDB. */ -static void ospf_abr_nssa_task(struct ospf *ospf) /* called only if any_nssa */ +void ospf_abr_nssa_task(struct ospf *ospf) /* called only if any_nssa */ { if (ospf->gr_info.restart_in_progress) return; @@ -1952,7 +2041,7 @@ static void ospf_abr_nssa_task(struct ospf *ospf) /* called only if any_nssa */ /* RESET all Ranges in every Area, same as summaries */ if (IS_DEBUG_OSPF_NSSA) zlog_debug("%s: NSSA initialize aggregates", __func__); - ospf_abr_prepare_aggregates(ospf); /*TURNED OFF just for now */ + ospf_abr_prepare_aggregates(ospf, true); /* For all NSSAs, Type-7s, translate to 5's, INSTALL/FLOOD, or * Aggregate as Type-7 @@ -1983,7 +2072,7 @@ static void ospf_abr_nssa_task(struct ospf *ospf) /* called only if any_nssa */ zlog_debug("%s: remove unapproved translates", __func__); ospf_abr_remove_unapproved_translates(ospf); - ospf_abr_manage_discard_routes(ospf); /* same as normal...discard */ + ospf_abr_manage_discard_routes(ospf, true); if (IS_DEBUG_OSPF_NSSA) zlog_debug("%s: Stop", __func__); @@ -2012,7 +2101,7 @@ void ospf_abr_task(struct ospf *ospf) if (IS_DEBUG_OSPF_EVENT) zlog_debug("%s: prepare aggregates", __func__); - ospf_abr_prepare_aggregates(ospf); + ospf_abr_prepare_aggregates(ospf, false); if (IS_OSPF_ABR(ospf)) { if (IS_DEBUG_OSPF_EVENT) @@ -2031,6 +2120,11 @@ void ospf_abr_task(struct ospf *ospf) zlog_debug("%s: announce stub defaults", __func__); ospf_abr_announce_stub_defaults(ospf); + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: announce NSSA Type-7 defaults", + __func__); + ospf_abr_nssa_type7_defaults(ospf); + if (ospf->fr_configured) { OSPF_LOG_DEBUG(IS_DEBUG_OSPF_EVENT, "%s(): announce non-DNArouters", @@ -2050,7 +2144,7 @@ void ospf_abr_task(struct ospf *ospf) zlog_debug("%s: remove unapproved summaries", __func__); ospf_abr_remove_unapproved_summaries(ospf); - ospf_abr_manage_discard_routes(ospf); + ospf_abr_manage_discard_routes(ospf, false); if (IS_DEBUG_OSPF_EVENT) zlog_debug("%s: Stop", __func__); diff --git a/ospfd/ospf_abr.h b/ospfd/ospf_abr.h index 19d444b125..cc2b2b0548 100644 --- a/ospfd/ospf_abr.h +++ b/ospfd/ospf_abr.h @@ -16,6 +16,7 @@ #define OSPF_AREA_RANGE_ADVERTISE (1 << 0) #define OSPF_AREA_RANGE_SUBSTITUTE (1 << 1) +#define OSPF_AREA_RANGE_NSSA (1 << 2) /* Area range. */ struct ospf_area_range { @@ -44,23 +45,23 @@ struct ospf_area_range { /* Prototypes. */ extern struct ospf_area_range *ospf_area_range_lookup(struct ospf_area *, + struct route_table *, struct prefix_ipv4 *); - -extern struct ospf_area_range *ospf_some_area_range_match(struct prefix_ipv4 *); - extern struct ospf_area_range * ospf_area_range_lookup_next(struct ospf_area *, struct in_addr *, int); -extern int ospf_area_range_set(struct ospf *, struct in_addr, - struct prefix_ipv4 *, int); -extern int ospf_area_range_cost_set(struct ospf *, struct in_addr, - struct prefix_ipv4 *, uint32_t); -extern int ospf_area_range_unset(struct ospf *, struct in_addr, - struct prefix_ipv4 *); -extern int ospf_area_range_substitute_set(struct ospf *, struct in_addr, +extern int ospf_area_range_set(struct ospf *, struct ospf_area *, + struct route_table *, struct prefix_ipv4 *, int, + bool); +extern int ospf_area_range_cost_set(struct ospf *, struct ospf_area *, + struct route_table *, struct prefix_ipv4 *, + uint32_t); +extern int ospf_area_range_unset(struct ospf *, struct ospf_area *, + struct route_table *, struct prefix_ipv4 *); +extern int ospf_area_range_substitute_set(struct ospf *, struct ospf_area *, struct prefix_ipv4 *, struct prefix_ipv4 *); -extern int ospf_area_range_substitute_unset(struct ospf *, struct in_addr, +extern int ospf_area_range_substitute_unset(struct ospf *, struct ospf_area *, struct prefix_ipv4 *); extern struct ospf_area_range *ospf_area_range_match_any(struct ospf *, struct prefix_ipv4 *); @@ -69,10 +70,12 @@ extern int ospf_act_bb_connection(struct ospf *); extern void ospf_check_abr_status(struct ospf *); extern void ospf_abr_task(struct ospf *); +extern void ospf_abr_nssa_task(struct ospf *ospf); extern void ospf_schedule_abr_task(struct ospf *); extern void ospf_abr_announce_network_to_area(struct prefix_ipv4 *, uint32_t, struct ospf_area *); +extern void ospf_abr_nssa_type7_defaults(struct ospf *ospf); extern void ospf_abr_nssa_check_status(struct ospf *ospf); extern void ospf_abr_generate_indication_lsa(struct ospf *ospf, const struct ospf_area *area); diff --git a/ospfd/ospf_asbr.c b/ospfd/ospf_asbr.c index 7befcc1086..1b68f6e022 100644 --- a/ospfd/ospf_asbr.c +++ b/ospfd/ospf_asbr.c @@ -94,7 +94,7 @@ int ospf_route_map_set_compare(struct route_map_set_values *values1, struct external_info * ospf_external_info_add(struct ospf *ospf, uint8_t type, unsigned short instance, struct prefix_ipv4 p, ifindex_t ifindex, - struct in_addr nexthop, route_tag_t tag) + struct in_addr nexthop, route_tag_t tag, uint32_t metric) { struct external_info *new; struct route_node *rn; @@ -131,6 +131,9 @@ ospf_external_info_add(struct ospf *ospf, uint8_t type, unsigned short instance, new->tag = tag; new->orig_tag = tag; new->aggr_route = NULL; + new->metric = metric; + new->min_metric = 0; + new->max_metric = OSPF_LS_INFINITY; /* we don't unlock rn from the get() because we're attaching the info */ if (rn) @@ -138,9 +141,9 @@ ospf_external_info_add(struct ospf *ospf, uint8_t type, unsigned short instance, if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) { zlog_debug( - "Redistribute[%s][%u]: %pFX external info created, with NH %pI4", + "Redistribute[%s][%u]: %pFX external info created, with NH %pI4, metric:%u", ospf_redist_string(type), ospf->vrf_id, &p, - &nexthop.s_addr); + &nexthop.s_addr, metric); } return new; } diff --git a/ospfd/ospf_asbr.h b/ospfd/ospf_asbr.h index 6b427c5fd2..dfb9d965c5 100644 --- a/ospfd/ospf_asbr.h +++ b/ospfd/ospf_asbr.h @@ -36,6 +36,10 @@ struct external_info { /* Actual tag received from zebra*/ route_tag_t orig_tag; + uint32_t metric; + uint32_t min_metric; + uint32_t max_metric; + struct route_map_set_values route_map_set; #define ROUTEMAP_METRIC(E) (E)->route_map_set.metric #define ROUTEMAP_METRIC_TYPE(E) (E)->route_map_set.metric_type @@ -99,11 +103,10 @@ extern struct external_info *ospf_external_info_new(struct ospf *, uint8_t, extern void ospf_reset_route_map_set_values(struct route_map_set_values *); extern int ospf_route_map_set_compare(struct route_map_set_values *, struct route_map_set_values *); -extern struct external_info *ospf_external_info_add(struct ospf *, uint8_t, - unsigned short, - struct prefix_ipv4, - ifindex_t, struct in_addr, - route_tag_t); +extern struct external_info * +ospf_external_info_add(struct ospf *, uint8_t, unsigned short, + struct prefix_ipv4, ifindex_t, struct in_addr, + route_tag_t, uint32_t metric); extern void ospf_external_info_delete(struct ospf *, uint8_t, unsigned short, struct prefix_ipv4); extern struct external_info *ospf_external_info_lookup(struct ospf *, uint8_t, diff --git a/ospfd/ospf_ase.c b/ospfd/ospf_ase.c index 80390af505..cc2110d433 100644 --- a/ospfd/ospf_ase.c +++ b/ospfd/ospf_ase.c @@ -159,7 +159,9 @@ ospf_ase_calculate_new_route(struct ospf_lsa *lsa, if (!IS_EXTERNAL_METRIC(al->e[0].tos)) { if (IS_DEBUG_OSPF(lsa, LSA)) - zlog_debug("Route[External]: type-1 created."); + zlog_debug( + "Route[External]: type-1 created, asbr cost:%d metric:%d.", + asbr_route->cost, metric); new->path_type = OSPF_PATH_TYPE1_EXTERNAL; new->cost = asbr_route->cost + metric; /* X + Y */ } else { diff --git a/ospfd/ospf_interface.c b/ospfd/ospf_interface.c index 649ba70e02..5742ece1f7 100644 --- a/ospfd/ospf_interface.c +++ b/ospfd/ospf_interface.c @@ -651,6 +651,8 @@ int ospf_if_new_hook(struct interface *ifp) ifp->info = XCALLOC(MTYPE_OSPF_IF_INFO, sizeof(struct ospf_if_info)); + IF_OSPF_IF_INFO(ifp)->oii_fd = -1; + IF_OIFS(ifp) = route_table_init(); IF_OIFS_PARAMS(ifp) = route_table_init(); @@ -691,6 +693,8 @@ static int ospf_if_delete_hook(struct interface *ifp) { int rc = 0; struct route_node *rn; + struct ospf_if_info *oii; + rc = ospf_opaque_del_if(ifp); /* @@ -707,6 +711,13 @@ static int ospf_if_delete_hook(struct interface *ifp) route_table_finish(IF_OIFS(ifp)); route_table_finish(IF_OIFS_PARAMS(ifp)); + /* Close per-interface socket */ + oii = ifp->info; + if (oii && oii->oii_fd > 0) { + close(oii->oii_fd); + oii->oii_fd = -1; + } + XFREE(MTYPE_OSPF_IF_INFO, ifp->info); return rc; @@ -1367,6 +1378,16 @@ static int ospf_ifp_up(struct interface *ifp) struct ospf_interface *oi; struct route_node *rn; struct ospf_if_info *oii = ifp->info; + struct ospf *ospf; + + if (IS_DEBUG_OSPF(zebra, ZEBRA_INTERFACE)) + zlog_debug("Zebra: Interface[%s] state change to up.", + ifp->name); + + /* Open per-intf write socket if configured */ + ospf = ifp->vrf->info; + if (ospf && ospf->intf_socket_enabled) + ospf_ifp_sock_init(ifp); ospf_if_recalculate_output_cost(ifp); @@ -1384,10 +1405,6 @@ static int ospf_ifp_up(struct interface *ifp) return 0; } - if (IS_DEBUG_OSPF(zebra, ZEBRA_INTERFACE)) - zlog_debug("Zebra: Interface[%s] state change to up.", - ifp->name); - for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) { if ((oi = rn->info) == NULL) continue; @@ -1416,6 +1433,9 @@ static int ospf_ifp_down(struct interface *ifp) ospf_if_down(oi); } + /* Close per-interface write socket if configured */ + ospf_ifp_sock_close(ifp); + return 0; } diff --git a/ospfd/ospf_interface.h b/ospfd/ospf_interface.h index 8625a72ac1..649df437a4 100644 --- a/ospfd/ospf_interface.h +++ b/ospfd/ospf_interface.h @@ -121,6 +121,9 @@ struct ospf_if_info { membership_counts[MEMBER_MAX]; /* multicast group refcnts */ uint32_t curr_mtu; + + /* Per-interface write socket, configured via 'ospf' object */ + int oii_fd; }; struct ospf_interface; diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c index 82f7b96fd5..452a3ba374 100644 --- a/ospfd/ospf_lsa.c +++ b/ospfd/ospf_lsa.c @@ -1654,9 +1654,6 @@ struct in_addr ospf_get_nssa_ip(struct ospf_area *area) if (best_default.s_addr != INADDR_ANY) return best_default; - if (best_default.s_addr != INADDR_ANY) - return best_default; - return fwd; } @@ -1868,8 +1865,7 @@ static struct ospf_lsa *ospf_external_lsa_new(struct ospf *ospf, } /* As Type-7 */ -static void ospf_install_flood_nssa(struct ospf *ospf, struct ospf_lsa *lsa, - struct external_info *ei) +static void ospf_install_flood_nssa(struct ospf *ospf, struct ospf_lsa *lsa) { struct ospf_lsa *new; struct as_external_lsa *extlsa; @@ -1979,6 +1975,9 @@ static struct ospf_lsa *ospf_lsa_translated_nssa_new(struct ospf *ospf, ei.nexthop = ext->header.adv_router; ei.route_map_set.metric = -1; ei.route_map_set.metric_type = -1; + ei.metric = DEFAULT_DEFAULT_METRIC; + ei.max_metric = OSPF_LS_INFINITY; + ei.min_metric = 0; ei.tag = 0; ei.instance = 0; @@ -2017,7 +2016,7 @@ struct ospf_lsa *ospf_translated_nssa_originate(struct ospf *ospf, struct ospf_lsa *type7, struct ospf_lsa *type5) { - struct ospf_lsa *new; + struct ospf_lsa *new, *translated_lsa; struct as_external_lsa *extnew; if (ospf->gr_info.restart_in_progress) { @@ -2031,7 +2030,8 @@ struct ospf_lsa *ospf_translated_nssa_originate(struct ospf *ospf, * the OSPF_LSA_LOCAL_XLT flag, must originate by hand */ - if ((new = ospf_lsa_translated_nssa_new(ospf, type7)) == NULL) { + if ((translated_lsa = ospf_lsa_translated_nssa_new(ospf, type7)) == + NULL) { if (IS_DEBUG_OSPF_NSSA) zlog_debug( "%s: Could not translate Type-7, Id %pI4, to Type-5", @@ -2039,16 +2039,17 @@ struct ospf_lsa *ospf_translated_nssa_originate(struct ospf *ospf, return NULL; } - extnew = (struct as_external_lsa *)new->data; + extnew = (struct as_external_lsa *)translated_lsa->data; /* Update LSA sequence number from translated Type-5 LSA */ if (type5) - new->data->ls_seqnum = lsa_seqnum_increment(type5); + translated_lsa->data->ls_seqnum = lsa_seqnum_increment(type5); - if ((new = ospf_lsa_install(ospf, NULL, new)) == NULL) { + if ((new = ospf_lsa_install(ospf, NULL, translated_lsa)) == NULL) { flog_warn(EC_OSPF_LSA_INSTALL_FAILURE, "%s: Could not install LSA id %pI4", __func__, &type7->data->id); + ospf_lsa_free(translated_lsa); return NULL; } @@ -2071,7 +2072,7 @@ struct ospf_lsa *ospf_translated_nssa_refresh(struct ospf *ospf, struct ospf_lsa *type7, struct ospf_lsa *type5) { - struct ospf_lsa *new = NULL; + struct ospf_lsa *new = NULL, *translated_lsa = NULL; struct as_external_lsa *extold = NULL; uint32_t ls_seqnum = 0; @@ -2147,7 +2148,8 @@ struct ospf_lsa *ospf_translated_nssa_refresh(struct ospf *ospf, ospf_ls_retransmit_delete_nbr_as(ospf, type5); /* create new translated LSA */ - if ((new = ospf_lsa_translated_nssa_new(ospf, type7)) == NULL) { + if ((translated_lsa = ospf_lsa_translated_nssa_new(ospf, type7)) == + NULL) { if (IS_DEBUG_OSPF_NSSA) zlog_debug( "%s: Could not translate Type-7 for %pI4 to Type-5", @@ -2157,13 +2159,14 @@ struct ospf_lsa *ospf_translated_nssa_refresh(struct ospf *ospf, if (type7->area->suppress_fa == 1) { if (extold->e[0].fwd_addr.s_addr == 0) - new->data->ls_seqnum = htonl(ls_seqnum + 1); + translated_lsa->data->ls_seqnum = htonl(ls_seqnum + 1); } - if (!(new = ospf_lsa_install(ospf, NULL, new))) { + if (!(new = ospf_lsa_install(ospf, NULL, translated_lsa))) { flog_warn(EC_OSPF_LSA_INSTALL_FAILURE, "%s: Could not install translated LSA, Id %pI4", __func__, &type7->data->id); + ospf_lsa_free(translated_lsa); return NULL; } @@ -2253,7 +2256,7 @@ struct ospf_lsa *ospf_external_lsa_originate(struct ospf *ospf, /* stay away from translated LSAs! */ !(CHECK_FLAG(new->flags, OSPF_LSA_LOCAL_XLT))) ospf_install_flood_nssa( - ospf, new, ei); /* Install/Flood Type-7 to all NSSAs */ + ospf, new); /* Install/Flood Type-7 to all NSSAs */ /* Debug logging. */ if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) { @@ -2266,6 +2269,100 @@ struct ospf_lsa *ospf_external_lsa_originate(struct ospf *ospf, return new; } +/* Originate an NSSA-LSA, install and flood. */ +struct ospf_lsa *ospf_nssa_lsa_originate(struct ospf_area *area, + struct external_info *ei) +{ + struct ospf *ospf = area->ospf; + struct ospf_lsa *new; + + if (ospf->gr_info.restart_in_progress) { + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + zlog_debug( + "LSA[Type7]: Graceful Restart in progress, don't originate"); + return NULL; + } + + if (ospf->router_id.s_addr == INADDR_ANY) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "LSA[Type7:%pI4]: deferring NSSA-LSA origination, router ID is zero", + &ei->p.prefix); + return NULL; + } + + /* Create new NSSA-LSA instance. */ + if ((new = ospf_external_lsa_new(ospf, ei, NULL)) == NULL) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "LSA[Type7:%pI4]: Could not originate NSSA-LSA", + &ei->p.prefix); + return NULL; + } + new->data->type = OSPF_AS_NSSA_LSA; + new->area = area; + + /* Install newly created LSA into Type-7 LSDB. */ + ospf_lsa_install(ospf, NULL, new); + + /* Update LSA origination count. */ + ospf->lsa_originate_count++; + + /* Flooding new LSA */ + ospf_flood_through_area(area, NULL, new); + + /* Debug logging. */ + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) { + zlog_debug("LSA[Type%d:%pI4]: Originate NSSA-LSA %p", + new->data->type, &new->data->id, (void *)new); + ospf_lsa_header_dump(new->data); + } + + return new; +} + +/* Refresh NSSA-LSA. */ +struct ospf_lsa *ospf_nssa_lsa_refresh(struct ospf_area *area, + struct ospf_lsa *lsa, + struct external_info *ei) +{ + struct ospf *ospf = area->ospf; + struct ospf_lsa *new; + + /* Delete LSA from neighbor retransmit-list. */ + ospf_ls_retransmit_delete_nbr_as(ospf, lsa); + + /* Unregister AS-external-LSA from refresh-list. */ + ospf_refresher_unregister_lsa(ospf, lsa); + + /* Create new NSSA-LSA instance. */ + if ((new = ospf_external_lsa_new(ospf, ei, NULL)) == NULL) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "LSA[Type7:%pI4]: Could not originate NSSA-LSA", + &ei->p.prefix); + return NULL; + } + new->data->type = OSPF_AS_NSSA_LSA; + new->data->ls_seqnum = lsa_seqnum_increment(lsa); + new->area = area; + + /* Install newly created LSA into Type-7 LSDB. */ + ospf_lsa_install(ospf, NULL, new); + + /* Flooding new LSA */ + ospf_flood_through_area(area, NULL, new); + + /* Debug logging. */ + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) { + zlog_debug("LSA[Type%d:%pI4]: NSSA-LSA refresh", + new->data->type, &new->data->id); + ospf_lsa_header_dump(new->data); + } + + return new; +} + static struct external_info *ospf_default_external_info(struct ospf *ospf) { int type; @@ -2611,8 +2708,8 @@ struct ospf_lsa *ospf_external_lsa_refresh(struct ospf *ospf, /* If any attached NSSA, install as Type-7, flood to all NSSA Areas */ if (ospf->anyNSSA && !(CHECK_FLAG(new->flags, OSPF_LSA_LOCAL_XLT))) - ospf_install_flood_nssa(ospf, new, - ei); /* Install/Flood per new rules */ + ospf_install_flood_nssa(ospf, + new); /* Install/Flood per new rules */ /* Register self-originated LSA to refresh queue. * Translated LSAs should not be registered, but refreshed upon diff --git a/ospfd/ospf_lsa.h b/ospfd/ospf_lsa.h index 8ab293f4db..d5ca0694cc 100644 --- a/ospfd/ospf_lsa.h +++ b/ospfd/ospf_lsa.h @@ -278,6 +278,11 @@ extern struct in_addr ospf_get_ip_from_ifp(struct ospf_interface *); extern struct ospf_lsa *ospf_external_lsa_originate(struct ospf *, struct external_info *); +extern struct ospf_lsa *ospf_nssa_lsa_originate(struct ospf_area *area, + struct external_info *ei); +extern struct ospf_lsa *ospf_nssa_lsa_refresh(struct ospf_area *area, + struct ospf_lsa *lsa, + struct external_info *ei); extern void ospf_external_lsa_rid_change(struct ospf *ospf); extern struct ospf_lsa *ospf_lsa_lookup(struct ospf *ospf, struct ospf_area *, uint32_t, struct in_addr, diff --git a/ospfd/ospf_network.c b/ospfd/ospf_network.c index d3f30ce1ee..aff8ed05c7 100644 --- a/ospfd/ospf_network.c +++ b/ospfd/ospf_network.c @@ -15,6 +15,7 @@ #include "sockopt.h" #include "privs.h" #include "lib_errors.h" +#include "lib/table.h" #include "ospfd/ospfd.h" #include "ospfd/ospf_network.h" @@ -111,7 +112,7 @@ int ospf_if_drop_alldrouters(struct ospf *top, struct prefix *p, "can't setsockopt IP_DROP_MEMBERSHIP (fd %d, addr %pI4, ifindex %u, AllDRouters): %s", top->fd, &p->u.prefix4, ifindex, safe_strerror(errno)); - else + else if (IS_DEBUG_OSPF_EVENT) zlog_debug( "interface %pI4 [%u] leave AllDRouters Multicast group.", &p->u.prefix4, ifindex); @@ -119,62 +120,60 @@ int ospf_if_drop_alldrouters(struct ospf *top, struct prefix *p, return ret; } -int ospf_if_ipmulticast(struct ospf *top, struct prefix *p, ifindex_t ifindex) +int ospf_if_ipmulticast(int fd, struct prefix *p, ifindex_t ifindex) { uint8_t val; int ret, len; /* Prevent receiving self-origined multicast packets. */ - ret = setsockopt_ipv4_multicast_loop(top->fd, 0); + ret = setsockopt_ipv4_multicast_loop(fd, 0); if (ret < 0) flog_err(EC_LIB_SOCKET, "can't setsockopt IP_MULTICAST_LOOP(0) for fd %d: %s", - top->fd, safe_strerror(errno)); + fd, safe_strerror(errno)); /* Explicitly set multicast ttl to 1 -- endo. */ val = 1; len = sizeof(val); - ret = setsockopt(top->fd, IPPROTO_IP, IP_MULTICAST_TTL, (void *)&val, - len); + ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, (void *)&val, len); if (ret < 0) flog_err(EC_LIB_SOCKET, "can't setsockopt IP_MULTICAST_TTL(1) for fd %d: %s", - top->fd, safe_strerror(errno)); + fd, safe_strerror(errno)); #ifndef GNU_LINUX /* For GNU LINUX ospf_write uses IP_PKTINFO, in_pktinfo to send * packet out of ifindex. Below would be used Non Linux system. */ - ret = setsockopt_ipv4_multicast_if(top->fd, p->u.prefix4, ifindex); + ret = setsockopt_ipv4_multicast_if(fd, p->u.prefix4, ifindex); if (ret < 0) flog_err(EC_LIB_SOCKET, "can't setsockopt IP_MULTICAST_IF(fd %d, addr %pI4, ifindex %u): %s", - top->fd, &p->u.prefix4, ifindex, + fd, &p->u.prefix4, ifindex, safe_strerror(errno)); #endif return ret; } -int ospf_sock_init(struct ospf *ospf) +/* + * Helper to open and set up a socket; returns the new fd on success, + * -1 on error. + */ +static int sock_init_common(vrf_id_t vrf_id, const char *name, int *pfd) { int ospf_sock; int ret, hincl = 1; - int bufsize = (8 * 1024 * 1024); - - /* silently ignore. already done */ - if (ospf->fd > 0) - return -1; - if (ospf->vrf_id == VRF_UNKNOWN) { + if (vrf_id == VRF_UNKNOWN) { /* silently return since VRF is not ready */ return -1; } + frr_with_privs(&ospfd_privs) { ospf_sock = vrf_socket(AF_INET, SOCK_RAW, IPPROTO_OSPFIGP, - ospf->vrf_id, ospf->name); + vrf_id, name); if (ospf_sock < 0) { - flog_err(EC_LIB_SOCKET, - "ospf_read_sock_init: socket: %s", + flog_err(EC_LIB_SOCKET, "%s: socket: %s", __func__, safe_strerror(errno)); return -1; } @@ -213,9 +212,102 @@ int ospf_sock_init(struct ospf *ospf) ospf_sock); } - setsockopt_so_sendbuf(ospf_sock, bufsize); - setsockopt_so_recvbuf(ospf_sock, bufsize); + *pfd = ospf_sock; - ospf->fd = ospf_sock; return ret; } + +/* + * Update a socket bufsize(s), based on its ospf instance + */ +void ospf_sock_bufsize_update(const struct ospf *ospf, int sock, + enum ospf_sock_type_e type) +{ + int bufsize; + + if (type == OSPF_SOCK_BOTH || type == OSPF_SOCK_RECV) { + bufsize = ospf->recv_sock_bufsize; + setsockopt_so_recvbuf(sock, bufsize); + } + + if (type == OSPF_SOCK_BOTH || type == OSPF_SOCK_SEND) { + bufsize = ospf->send_sock_bufsize; + setsockopt_so_sendbuf(sock, bufsize); + } +} + +int ospf_sock_init(struct ospf *ospf) +{ + int ret; + + /* silently ignore. already done */ + if (ospf->fd > 0) + return -1; + + ret = sock_init_common(ospf->vrf_id, ospf->name, &(ospf->fd)); + + if (ret >= 0) /* Update socket buffer sizes */ + ospf_sock_bufsize_update(ospf, ospf->fd, OSPF_SOCK_BOTH); + + return ret; +} + +/* + * Open per-interface write socket + */ +int ospf_ifp_sock_init(struct interface *ifp) +{ + struct ospf_if_info *oii; + struct ospf_interface *oi; + struct ospf *ospf; + struct route_node *rn; + int ret; + + oii = IF_OSPF_IF_INFO(ifp); + if (oii == NULL) + return -1; + + if (oii->oii_fd > 0) + return 0; + + rn = route_top(IF_OIFS(ifp)); + if (rn && rn->info) { + oi = rn->info; + ospf = oi->ospf; + } else + return -1; + + ret = sock_init_common(ifp->vrf->vrf_id, ifp->name, &oii->oii_fd); + + if (ret >= 0) /* Update socket buffer sizes */ + ospf_sock_bufsize_update(ospf, oii->oii_fd, OSPF_SOCK_BOTH); + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: ifp %s, oii %p, fd %d", __func__, ifp->name, + oii, oii->oii_fd); + + return ret; +} + +/* + * Close per-interface write socket + */ +int ospf_ifp_sock_close(struct interface *ifp) +{ + struct ospf_if_info *oii; + + oii = IF_OSPF_IF_INFO(ifp); + if (oii == NULL) + return 0; + + if (oii->oii_fd > 0) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: ifp %s, oii %p, fd %d", __func__, + ifp->name, oii, oii->oii_fd); + + close(oii->oii_fd); + oii->oii_fd = -1; + } + + return 0; +} diff --git a/ospfd/ospf_network.h b/ospfd/ospf_network.h index 33fd8980bf..b810bad50b 100644 --- a/ospfd/ospf_network.h +++ b/ospfd/ospf_network.h @@ -13,7 +13,20 @@ extern int ospf_if_drop_allspfrouters(struct ospf *, struct prefix *, ifindex_t); extern int ospf_if_add_alldrouters(struct ospf *, struct prefix *, ifindex_t); extern int ospf_if_drop_alldrouters(struct ospf *, struct prefix *, ifindex_t); -extern int ospf_if_ipmulticast(struct ospf *, struct prefix *, ifindex_t); +extern int ospf_if_ipmulticast(int fd, struct prefix *, ifindex_t); extern int ospf_sock_init(struct ospf *ospf); +/* Open, close per-interface write socket */ +int ospf_ifp_sock_init(struct interface *ifp); +int ospf_ifp_sock_close(struct interface *ifp); + +enum ospf_sock_type_e { + OSPF_SOCK_NONE = 0, + OSPF_SOCK_RECV, + OSPF_SOCK_SEND, + OSPF_SOCK_BOTH +}; + +void ospf_sock_bufsize_update(const struct ospf *ospf, int sock, + enum ospf_sock_type_e type); #endif /* _ZEBRA_OSPF_NETWORK_H */ diff --git a/ospfd/ospf_opaque.c b/ospfd/ospf_opaque.c index 6f66ee10a1..c2b40af1c4 100644 --- a/ospfd/ospf_opaque.c +++ b/ospfd/ospf_opaque.c @@ -544,7 +544,7 @@ register_opaque_info_per_type(struct ospf_opaque_functab *functab, listnode_add(new->area->opaque_lsa_self, oipt); break; case OSPF_OPAQUE_AS_LSA: - top = ospf_lookup_by_vrf_id(new->vrf_id); + top = ospf_lookup_by_vrf_id(VRF_DEFAULT); if (new->area != NULL && (top = new->area->ospf) == NULL) { free_opaque_info_per_type(oipt, true); oipt = NULL; @@ -652,7 +652,7 @@ lookup_opaque_info_by_type(struct ospf_lsa *lsa) "Type-10 Opaque-LSA: Reference to AREA is missing?"); break; case OSPF_OPAQUE_AS_LSA: - top = ospf_lookup_by_vrf_id(lsa->vrf_id); + top = ospf_lookup_by_vrf_id(VRF_DEFAULT); if ((area = lsa->area) != NULL && (top = area->ospf) == NULL) { flog_warn( EC_OSPF_LSA, @@ -758,6 +758,13 @@ DEFUN (capability_opaque, { VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + /* Check that OSPF is using default VRF */ + if (ospf->vrf_id != VRF_DEFAULT) { + vty_out(vty, + "OSPF Opaque LSA is only supported in default VRF\n"); + return CMD_WARNING_CONFIG_FAILED; + } + /* Turn on the "master switch" of opaque-lsa capability. */ if (!CHECK_FLAG(ospf->config, OSPF_OPAQUE_CAPABLE)) { if (IS_DEBUG_OSPF_EVENT) @@ -1588,7 +1595,7 @@ struct ospf_lsa *ospf_opaque_lsa_install(struct ospf_lsa *lsa, int rt_recalc) } break; case OSPF_OPAQUE_AS_LSA: - top = ospf_lookup_by_vrf_id(lsa->vrf_id); + top = ospf_lookup_by_vrf_id(VRF_DEFAULT); if (lsa->area != NULL && (top = lsa->area->ospf) == NULL) { /* Above conditions must have passed. */ flog_warn(EC_OSPF_LSA, "%s: Something wrong?", @@ -1615,7 +1622,7 @@ struct ospf_lsa *ospf_opaque_lsa_refresh(struct ospf_lsa *lsa) struct ospf_opaque_functab *functab; struct ospf_lsa *new = NULL; - ospf = ospf_lookup_by_vrf_id(lsa->vrf_id); + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); if ((functab = ospf_opaque_functab_lookup(lsa)) == NULL || functab->lsa_refresher == NULL) { @@ -1752,7 +1759,7 @@ void ospf_opaque_lsa_reoriginate_schedule(void *lsa_type_dependent, /* Generate a dummy lsa to be passed for a lookup function. */ lsa = pseudo_lsa(oi, area, lsa_type, opaque_type); - lsa->vrf_id = top->vrf_id; + lsa->vrf_id = VRF_DEFAULT; if ((oipt = lookup_opaque_info_by_type(lsa)) == NULL) { struct ospf_opaque_functab *functab; @@ -1987,7 +1994,7 @@ void ospf_opaque_lsa_refresh_schedule(struct ospf_lsa *lsa0) ospf_ls_retransmit_delete_nbr_area(lsa->area, lsa); break; case OSPF_OPAQUE_AS_LSA: - top = ospf_lookup_by_vrf_id(lsa0->vrf_id); + top = ospf_lookup_by_vrf_id(VRF_DEFAULT); if ((lsa0->area != NULL) && (lsa0->area->ospf != NULL)) top = lsa0->area->ospf; ospf_ls_retransmit_delete_nbr_as(top, lsa); @@ -2037,7 +2044,7 @@ void ospf_opaque_lsa_flush_schedule(struct ospf_lsa *lsa0) struct ospf_lsa *lsa; struct ospf *top; - top = ospf_lookup_by_vrf_id(lsa0->vrf_id); + top = ospf_lookup_by_vrf_id(VRF_DEFAULT); if ((oipt = lookup_opaque_info_by_type(lsa0)) == NULL || (oipi = lookup_opaque_info_by_id(oipt, lsa0)) == NULL) { diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c index 5f7d49e0bb..552acfd6d3 100644 --- a/ospfd/ospf_packet.c +++ b/ospfd/ospf_packet.c @@ -618,7 +618,7 @@ static void ospf_write(struct event *thread) struct msghdr msg; struct iovec iov[2]; uint8_t type; - int ret; + int ret, fd; int flags = 0; struct listnode *node; #ifdef WANT_OSPF_WRITE_FRAGMENT @@ -633,11 +633,12 @@ static void ospf_write(struct event *thread) struct cmsghdr *cm = (struct cmsghdr *)cmsgbuf; struct in_pktinfo *pi; #endif + fd = ospf->fd; - if (ospf->fd < 0 || ospf->oi_running == 0) { + if (fd < 0 || ospf->oi_running == 0) { if (IS_DEBUG_OSPF_EVENT) zlog_debug("%s failed to send, fd %d, instance %u", - __func__, ospf->fd, ospf->oi_running); + __func__, fd, ospf->oi_running); return; } @@ -657,6 +658,15 @@ static void ospf_write(struct event *thread) /* convenience - max OSPF data per packet */ maxdatasize = oi->ifp->mtu - sizeof(struct ip); #endif /* WANT_OSPF_WRITE_FRAGMENT */ + + /* Reset socket fd to use. */ + fd = ospf->fd; + + /* Check for per-interface socket */ + if (ospf->intf_socket_enabled && + (IF_OSPF_IF_INFO(oi->ifp))->oii_fd > 0) + fd = (IF_OSPF_IF_INFO(oi->ifp))->oii_fd; + /* Get one packet from queue. */ op = ospf_fifo_head(oi->obuf); assert(op); @@ -664,8 +674,7 @@ static void ospf_write(struct event *thread) if (op->dst.s_addr == htonl(OSPF_ALLSPFROUTERS) || op->dst.s_addr == htonl(OSPF_ALLDROUTERS)) - ospf_if_ipmulticast(ospf, oi->address, - oi->ifp->ifindex); + ospf_if_ipmulticast(fd, oi->address, oi->ifp->ifindex); /* Rewrite the md5 signature & update the seq */ ospf_make_md5_digest(oi, op); @@ -760,13 +769,13 @@ static void ospf_write(struct event *thread) #ifdef WANT_OSPF_WRITE_FRAGMENT if (op->length > maxdatasize) - ospf_write_frags(ospf->fd, op, &iph, &msg, maxdatasize, + ospf_write_frags(fd, op, &iph, &msg, maxdatasize, oi->ifp->mtu, flags, type); #endif /* WANT_OSPF_WRITE_FRAGMENT */ /* send final fragment (could be first) */ sockopt_iphdrincl_swab_htosys(&iph); - ret = sendmsg(ospf->fd, &msg, flags); + ret = sendmsg(fd, &msg, flags); sockopt_iphdrincl_swab_systoh(&iph); if (IS_DEBUG_OSPF_EVENT) zlog_debug( diff --git a/ospfd/ospf_ri.c b/ospfd/ospf_ri.c index 0179f9ee0b..725443f490 100644 --- a/ospfd/ospf_ri.c +++ b/ospfd/ospf_ri.c @@ -798,13 +798,10 @@ static struct ospf_lsa *ospf_router_info_lsa_new(struct ospf_area *area) /* Now, create an OSPF LSA instance. */ new = ospf_lsa_new_and_data(length); + /* Routing Information is only supported for default VRF */ + new->vrf_id = VRF_DEFAULT; new->area = area; - if (new->area && new->area->ospf) - new->vrf_id = new->area->ospf->vrf_id; - else - new->vrf_id = VRF_DEFAULT; - SET_FLAG(new->flags, OSPF_LSA_SELF); memcpy(new->data, lsah, length); stream_free(s); @@ -817,7 +814,6 @@ static int ospf_router_info_lsa_originate_as(void *arg) struct ospf_lsa *new; struct ospf *top; int rc = -1; - vrf_id_t vrf_id = VRF_DEFAULT; /* Sanity Check */ if (OspfRI.scope == OSPF_OPAQUE_AREA_LSA) { @@ -830,13 +826,12 @@ static int ospf_router_info_lsa_originate_as(void *arg) /* Create new Opaque-LSA/ROUTER INFORMATION instance. */ new = ospf_router_info_lsa_new(NULL); - new->vrf_id = VRF_DEFAULT; top = (struct ospf *)arg; /* Check ospf info */ if (top == NULL) { zlog_debug("RI (%s): ospf instance not found for vrf id %u", - __func__, vrf_id); + __func__, VRF_DEFAULT); ospf_lsa_unlock(&new); return rc; } @@ -874,7 +869,6 @@ static int ospf_router_info_lsa_originate_area(void *arg) struct ospf *top; struct ospf_ri_area_info *ai = NULL; int rc = -1; - vrf_id_t vrf_id = VRF_DEFAULT; /* Sanity Check */ if (OspfRI.scope == OSPF_OPAQUE_AS_LSA) { @@ -893,19 +887,18 @@ static int ospf_router_info_lsa_originate_area(void *arg) __func__); return rc; } - if (ai->area->ospf) { - vrf_id = ai->area->ospf->vrf_id; + + if (ai->area->ospf) top = ai->area->ospf; - } else { - top = ospf_lookup_by_vrf_id(vrf_id); - } + else + top = ospf_lookup_by_vrf_id(VRF_DEFAULT); + new = ospf_router_info_lsa_new(ai->area); - new->vrf_id = vrf_id; /* Check ospf info */ if (top == NULL) { zlog_debug("RI (%s): ospf instance not found for vrf id %u", - __func__, vrf_id); + __func__, VRF_DEFAULT); ospf_lsa_unlock(&new); return rc; } @@ -1039,10 +1032,9 @@ static struct ospf_lsa *ospf_router_info_lsa_refresh(struct ospf_lsa *lsa) /* Create new Opaque-LSA/ROUTER INFORMATION instance. */ new = ospf_router_info_lsa_new(ai->area); new->data->ls_seqnum = lsa_seqnum_increment(lsa); - new->vrf_id = lsa->vrf_id; /* Install this LSA into LSDB. */ /* Given "lsa" will be freed in the next function. */ - top = ospf_lookup_by_vrf_id(lsa->vrf_id); + top = ospf_lookup_by_vrf_id(VRF_DEFAULT); if (ospf_lsa_install(top, NULL /*oi */, new) == NULL) { flog_warn(EC_OSPF_LSA_INSTALL_FAILURE, "RI (%s): ospf_lsa_install() ?", __func__); @@ -1062,10 +1054,9 @@ static struct ospf_lsa *ospf_router_info_lsa_refresh(struct ospf_lsa *lsa) /* Create new Opaque-LSA/ROUTER INFORMATION instance. */ new = ospf_router_info_lsa_new(NULL); new->data->ls_seqnum = lsa_seqnum_increment(lsa); - new->vrf_id = lsa->vrf_id; /* Install this LSA into LSDB. */ /* Given "lsa" will be freed in the next function. */ - top = ospf_lookup_by_vrf_id(lsa->vrf_id); + top = ospf_lookup_by_vrf_id(VRF_DEFAULT); if (ospf_lsa_install(top, NULL /*oi */, new) == NULL) { flog_warn(EC_OSPF_LSA_INSTALL_FAILURE, "RI (%s): ospf_lsa_install() ?", __func__); @@ -1676,10 +1667,18 @@ DEFUN (router_info, { int idx_mode = 1; uint8_t scope; + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); if (OspfRI.enabled) return CMD_SUCCESS; + /* Check that the OSPF is using default VRF */ + if (ospf->vrf_id != VRF_DEFAULT) { + vty_out(vty, + "Router Information is only supported in default VRF\n"); + return CMD_WARNING_CONFIG_FAILED; + } + /* Check and get Area value if present */ if (strncmp(argv[idx_mode]->arg, "as", 2) == 0) scope = OSPF_OPAQUE_AS_LSA; diff --git a/ospfd/ospf_route.c b/ospfd/ospf_route.c index 5f18bff1cf..75868056ad 100644 --- a/ospfd/ospf_route.c +++ b/ospfd/ospf_route.c @@ -1008,7 +1008,8 @@ void ospf_prune_unreachable_routers(struct route_table *rtrs) } int ospf_add_discard_route(struct ospf *ospf, struct route_table *rt, - struct ospf_area *area, struct prefix_ipv4 *p) + struct ospf_area *area, struct prefix_ipv4 *p, + bool nssa) { struct route_node *rn; struct ospf_route * or, *new_or; @@ -1027,7 +1028,7 @@ int ospf_add_discard_route(struct ospf *ospf, struct route_table *rt, or = rn->info; - if (or->path_type == OSPF_PATH_INTRA_AREA) { + if (!nssa && or->path_type == OSPF_PATH_INTRA_AREA) { if (IS_DEBUG_OSPF_EVENT) zlog_debug("%s: an intra-area route exists", __func__); @@ -1054,7 +1055,10 @@ int ospf_add_discard_route(struct ospf *ospf, struct route_table *rt, new_or->cost = 0; new_or->u.std.area_id = area->area_id; new_or->u.std.external_routing = area->external_routing; - new_or->path_type = OSPF_PATH_INTER_AREA; + if (nssa) + new_or->path_type = OSPF_PATH_TYPE2_EXTERNAL; + else + new_or->path_type = OSPF_PATH_INTER_AREA; rn->info = new_or; ospf_zebra_add_discard(ospf, p); @@ -1063,7 +1067,7 @@ int ospf_add_discard_route(struct ospf *ospf, struct route_table *rt, } void ospf_delete_discard_route(struct ospf *ospf, struct route_table *rt, - struct prefix_ipv4 *p) + struct prefix_ipv4 *p, bool nssa) { struct route_node *rn; struct ospf_route * or ; @@ -1081,7 +1085,7 @@ void ospf_delete_discard_route(struct ospf *ospf, struct route_table *rt, or = rn->info; - if (or->path_type == OSPF_PATH_INTRA_AREA) { + if (!nssa && or->path_type == OSPF_PATH_INTRA_AREA) { if (IS_DEBUG_OSPF_EVENT) zlog_debug("%s: an intra-area route exists", __func__); return; diff --git a/ospfd/ospf_route.h b/ospfd/ospf_route.h index 2582067aec..7639a0049e 100644 --- a/ospfd/ospf_route.h +++ b/ospfd/ospf_route.h @@ -152,9 +152,10 @@ extern void ospf_route_subst_nexthops(struct ospf_route *, struct list *); extern void ospf_prune_unreachable_networks(struct route_table *); extern void ospf_prune_unreachable_routers(struct route_table *); extern int ospf_add_discard_route(struct ospf *, struct route_table *, - struct ospf_area *, struct prefix_ipv4 *); + struct ospf_area *, struct prefix_ipv4 *, + bool); extern void ospf_delete_discard_route(struct ospf *, struct route_table *, - struct prefix_ipv4 *); + struct prefix_ipv4 *, bool); extern int ospf_route_match_same(struct route_table *, struct prefix_ipv4 *, struct ospf_route *); diff --git a/ospfd/ospf_routemap.c b/ospfd/ospf_routemap.c index 3087008819..d2f639031b 100644 --- a/ospfd/ospf_routemap.c +++ b/ospfd/ospf_routemap.c @@ -120,7 +120,7 @@ route_match_ip_nexthop(void *rule, const struct prefix *prefix, void *object) alist = access_list_lookup(AFI_IP, (char *)rule); if (alist == NULL) { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))) zlog_debug( "%s: Access-List Specified: %s does not exist defaulting to NO_MATCH", __func__, (char *)rule); @@ -168,7 +168,7 @@ route_match_ip_next_hop_prefix_list(void *rule, const struct prefix *prefix, plist = prefix_list_lookup(AFI_IP, (char *)rule); if (plist == NULL) { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))) zlog_debug( "%s: Prefix List %s specified does not exist defaulting to NO_MATCH", __func__, (char *)rule); @@ -245,7 +245,7 @@ route_match_ip_address(void *rule, const struct prefix *prefix, void *object) alist = access_list_lookup(AFI_IP, (char *)rule); if (alist == NULL) { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))) zlog_debug( "%s: Access-List Specified: %s does not exist defaulting to NO_MATCH", __func__, (char *)rule); @@ -286,7 +286,7 @@ route_match_ip_address_prefix_list(void *rule, const struct prefix *prefix, plist = prefix_list_lookup(AFI_IP, (char *)rule); if (plist == NULL) { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))) zlog_debug( "%s: Prefix List %s specified does not exist defaulting to NO_MATCH", __func__, (char *)rule); @@ -398,17 +398,19 @@ route_set_metric(void *rule, const struct prefix *prefix, void *object) if (!metric->used) return RMAP_OKAY; - ei->route_map_set.metric = DEFAULT_DEFAULT_METRIC; + ROUTEMAP_METRIC(ei) = ei->metric; if (metric->type == metric_increment) - ei->route_map_set.metric += metric->metric; + ROUTEMAP_METRIC(ei) += metric->metric; else if (metric->type == metric_decrement) - ei->route_map_set.metric -= metric->metric; + ROUTEMAP_METRIC(ei) -= metric->metric; else if (metric->type == metric_absolute) - ei->route_map_set.metric = metric->metric; + ROUTEMAP_METRIC(ei) = metric->metric; - if (ei->route_map_set.metric > OSPF_LS_INFINITY) - ei->route_map_set.metric = OSPF_LS_INFINITY; + if ((uint32_t)ROUTEMAP_METRIC(ei) < ei->min_metric) + ROUTEMAP_METRIC(ei) = ei->min_metric; + if ((uint32_t)ROUTEMAP_METRIC(ei) > ei->max_metric) + ROUTEMAP_METRIC(ei) = ei->max_metric; return RMAP_OKAY; } @@ -462,6 +464,115 @@ static const struct route_map_rule_cmd route_set_metric_cmd = { route_set_metric_free, }; +/* `set min-metric METRIC' */ +/* Set min-metric to attribute. */ +static enum route_map_cmd_result_t +route_set_min_metric(void *rule, const struct prefix *prefix, void *object) +{ + uint32_t *min_metric; + struct external_info *ei; + + /* Fetch routemap's rule information. */ + min_metric = rule; + ei = object; + + ei->min_metric = *min_metric; + + if (ei->min_metric > OSPF_LS_INFINITY) + ei->min_metric = OSPF_LS_INFINITY; + + if ((uint32_t)ROUTEMAP_METRIC(ei) < ei->min_metric) + ROUTEMAP_METRIC(ei) = ei->min_metric; + + return RMAP_OKAY; +} + +/* set min-metric compilation. */ +static void *route_set_min_metric_compile(const char *arg) +{ + + uint32_t *min_metric; + + min_metric = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(uint32_t)); + + *min_metric = strtoul(arg, NULL, 10); + + if (*min_metric) + return min_metric; + + XFREE(MTYPE_ROUTE_MAP_COMPILED, min_metric); + return NULL; +} + +/* Free route map's compiled `set min-metric' value. */ +static void route_set_min_metric_free(void *rule) +{ + XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Set metric rule structure. */ +static const struct route_map_rule_cmd route_set_min_metric_cmd = { + "min-metric", + route_set_min_metric, + route_set_min_metric_compile, + route_set_min_metric_free, +}; + + +/* `set max-metric METRIC' */ +/* Set max-metric to attribute. */ +static enum route_map_cmd_result_t +route_set_max_metric(void *rule, const struct prefix *prefix, void *object) +{ + uint32_t *max_metric; + struct external_info *ei; + + /* Fetch routemap's rule information. */ + max_metric = rule; + ei = object; + + ei->max_metric = *max_metric; + + if (ei->max_metric > OSPF_LS_INFINITY) + ei->max_metric = OSPF_LS_INFINITY; + + if ((uint32_t)ROUTEMAP_METRIC(ei) > ei->max_metric) + ROUTEMAP_METRIC(ei) = ei->max_metric; + + return RMAP_OKAY; +} + +/* set max-metric compilation. */ +static void *route_set_max_metric_compile(const char *arg) +{ + + uint32_t *max_metric; + + max_metric = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(uint32_t)); + + *max_metric = strtoul(arg, NULL, 10); + + if (*max_metric) + return max_metric; + + XFREE(MTYPE_ROUTE_MAP_COMPILED, max_metric); + return NULL; +} + +/* Free route map's compiled `set max-metric' value. */ +static void route_set_max_metric_free(void *rule) +{ + XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Set metric rule structure. */ +static const struct route_map_rule_cmd route_set_max_metric_cmd = { + "max-metric", + route_set_max_metric, + route_set_max_metric_compile, + route_set_max_metric_free, +}; + /* `set metric-type TYPE' */ /* Set metric-type to attribute. */ static enum route_map_cmd_result_t @@ -475,7 +586,7 @@ route_set_metric_type(void *rule, const struct prefix *prefix, void *object) ei = object; /* Set metric out value. */ - ei->route_map_set.metric_type = *metric_type; + ROUTEMAP_METRIC_TYPE(ei) = *metric_type; return RMAP_OKAY; } @@ -582,9 +693,6 @@ void ospf_route_map_init(void) route_map_delete_hook(ospf_route_map_update); route_map_event_hook(ospf_route_map_event); - route_map_set_metric_hook(generic_set_add); - route_map_no_set_metric_hook(generic_set_delete); - route_map_match_ip_next_hop_hook(generic_match_add); route_map_no_match_ip_next_hop_hook(generic_match_delete); @@ -609,6 +717,12 @@ void ospf_route_map_init(void) route_map_set_metric_hook(generic_set_add); route_map_no_set_metric_hook(generic_set_delete); + route_map_set_min_metric_hook(generic_set_add); + route_map_no_set_min_metric_hook(generic_set_delete); + + route_map_set_max_metric_hook(generic_set_add); + route_map_no_set_max_metric_hook(generic_set_delete); + route_map_set_tag_hook(generic_set_add); route_map_no_set_tag_hook(generic_set_delete); @@ -621,6 +735,8 @@ void ospf_route_map_init(void) route_map_install_match(&route_match_tag_cmd); route_map_install_set(&route_set_metric_cmd); + route_map_install_set(&route_set_min_metric_cmd); + route_map_install_set(&route_set_max_metric_cmd); route_map_install_set(&route_set_metric_type_cmd); route_map_install_set(&route_set_tag_cmd); diff --git a/ospfd/ospf_snmp.c b/ospfd/ospf_snmp.c index 2982cc7d6f..fcc43e7311 100644 --- a/ospfd/ospf_snmp.c +++ b/ospfd/ospf_snmp.c @@ -1112,7 +1112,7 @@ static struct ospf_area_range *ospfAreaRangeLookup(struct variable *v, oid2in_addr(offset, IN_ADDR_SIZE, range_net); p.prefix = *range_net; - return ospf_area_range_lookup(area, &p); + return ospf_area_range_lookup(area, area->ranges, &p); } else { /* Set OID offset for Area ID. */ offset = name + v->namelen; diff --git a/ospfd/ospf_te.c b/ospfd/ospf_te.c index 28f6ab0f85..3cf39e5fb5 100644 --- a/ospfd/ospf_te.c +++ b/ospfd/ospf_te.c @@ -1207,10 +1207,9 @@ static struct ospf_lsa *ospf_mpls_te_lsa_new(struct ospf *ospf, /* Now, create an OSPF LSA instance. */ new = ospf_lsa_new_and_data(length); - new->vrf_id = ospf->vrf_id; - if (area && area->ospf) - new->vrf_id = area->ospf->vrf_id; new->area = area; + new->vrf_id = VRF_DEFAULT; + SET_FLAG(new->flags, OSPF_LSA_SELF); memcpy(new->data, lsah, length); stream_free(s); @@ -1329,7 +1328,6 @@ static int ospf_mpls_te_lsa_originate2(struct ospf *top, __func__); return rc; } - new->vrf_id = top->vrf_id; /* Install this LSA into LSDB. */ if (ospf_lsa_install(top, NULL /*oi */, new) == NULL) { @@ -1482,7 +1480,7 @@ static struct ospf_lsa *ospf_mpls_te_lsa_refresh(struct ospf_lsa *lsa) ospf_opaque_lsa_flush_schedule(lsa); return NULL; } - top = ospf_lookup_by_vrf_id(lsa->vrf_id); + top = ospf_lookup_by_vrf_id(VRF_DEFAULT); /* Create new Opaque-LSA/MPLS-TE instance. */ new = ospf_mpls_te_lsa_new(top, area, lp); if (new == NULL) { @@ -1782,7 +1780,7 @@ static void ospf_te_update_link(struct ls_ted *ted, struct ls_vertex *vertex, * @param metric Standard metric attached to this Edge */ static void ospf_te_update_subnet(struct ls_ted *ted, struct ls_vertex *vertex, - struct prefix p, uint8_t metric) + struct prefix *p, uint8_t metric) { struct ls_subnet *subnet; struct ls_prefix *ls_pref; @@ -1841,7 +1839,7 @@ static void ospf_te_delete_subnet(struct ls_ted *ted, struct in_addr addr) p.family = AF_INET; p.prefixlen = IPV4_MAX_BITLEN; p.u.prefix4 = addr; - subnet = ls_find_subnet(ted, p); + subnet = ls_find_subnet(ted, &p); /* Remove subnet if found */ if (subnet) { @@ -1934,7 +1932,7 @@ static int ospf_te_parse_router_lsa(struct ls_ted *ted, struct ospf_lsa *lsa) p.prefixlen = IPV4_MAX_BITLEN; p.u.prefix4 = rl->link[i].link_data; metric = ntohs(rl->link[i].metric); - ospf_te_update_subnet(ted, vertex, p, metric); + ospf_te_update_subnet(ted, vertex, &p, metric); break; case LSA_LINK_TYPE_STUB: /* Keep only /32 prefix */ @@ -1943,7 +1941,7 @@ static int ospf_te_parse_router_lsa(struct ls_ted *ted, struct ospf_lsa *lsa) p.family = AF_INET; p.u.prefix4 = rl->link[i].link_id; metric = ntohs(rl->link[i].metric); - ospf_te_update_subnet(ted, vertex, p, metric); + ospf_te_update_subnet(ted, vertex, &p, metric); } break; default: @@ -2075,12 +2073,12 @@ static void ospf_te_update_remote_asbr(struct ls_ted *ted, struct ls_edge *edge) p.family = AF_INET; p.prefixlen = IPV4_MAX_BITLEN; p.u.prefix4 = attr->standard.local; - ospf_te_update_subnet(ted, edge->source, p, attr->standard.te_metric); + ospf_te_update_subnet(ted, edge->source, &p, attr->standard.te_metric); p.family = AF_INET; p.prefixlen = IPV4_MAX_BITLEN; p.u.prefix4 = attr->standard.remote_addr; - ospf_te_update_subnet(ted, vertex, p, attr->standard.te_metric); + ospf_te_update_subnet(ted, vertex, &p, attr->standard.te_metric); /* Connect Edge to the remote Vertex */ if (edge->destination == NULL) { @@ -2627,14 +2625,14 @@ static int ospf_te_parse_ext_pref(struct ls_ted *ted, struct ospf_lsa *lsa) pref.family = AF_INET; pref.prefixlen = ext->pref_length; pref.u.prefix4 = ext->address; - subnet = ls_find_subnet(ted, pref); + subnet = ls_find_subnet(ted, &pref); /* Create new Link State Prefix if not found */ if (!subnet) { lnid.origin = OSPFv2; lnid.id.ip.addr = lsa->data->adv_router; lnid.id.ip.area_id = lsa->area->area_id; - ls_pref = ls_prefix_new(lnid, pref); + ls_pref = ls_prefix_new(lnid, &pref); /* and add it to the TED */ subnet = ls_subnet_add(ted, ls_pref); } @@ -2700,7 +2698,7 @@ static int ospf_te_delete_ext_pref(struct ls_ted *ted, struct ospf_lsa *lsa) pref.family = AF_INET; pref.prefixlen = ext->pref_length; pref.u.prefix4 = ext->address; - subnet = ls_find_subnet(ted, pref); + subnet = ls_find_subnet(ted, &pref); /* Check if there is a corresponding subnet */ if (!subnet) @@ -3850,6 +3848,12 @@ DEFUN (ospf_mpls_te_on, if (OspfMplsTE.enabled) return CMD_SUCCESS; + /* Check that the OSPF is using default VRF */ + if (ospf->vrf_id != VRF_DEFAULT) { + vty_out(vty, "MPLS TE is only supported in default VRF\n"); + return CMD_WARNING_CONFIG_FAILED; + } + ote_debug("MPLS-TE: OFF -> ON"); OspfMplsTE.enabled = true; @@ -4232,12 +4236,10 @@ static void show_mpls_te_link_sub(struct vty *vty, struct interface *ifp) DEFUN (show_ip_ospf_mpls_te_link, show_ip_ospf_mpls_te_link_cmd, - "show ip ospf [vrf <NAME|all>] mpls-te interface [INTERFACE]", + "show ip ospf mpls-te interface [INTERFACE]", SHOW_STR IP_STR OSPF_STR - VRF_CMD_HELP_STR - "All VRFs\n" "MPLS-TE information\n" "Interface information\n" "Interface name\n") @@ -4245,43 +4247,18 @@ DEFUN (show_ip_ospf_mpls_te_link, struct vrf *vrf; int idx_interface = 0; struct interface *ifp = NULL; - struct listnode *node; - char *vrf_name = NULL; - bool all_vrf = false; - int inst = 0; - int idx_vrf = 0; struct ospf *ospf = NULL; - if (argv_find(argv, argc, "vrf", &idx_vrf)) { - vrf_name = argv[idx_vrf + 1]->arg; - all_vrf = strmatch(vrf_name, "all"); - } argv_find(argv, argc, "INTERFACE", &idx_interface); - /* vrf input is provided could be all or specific vrf*/ - if (vrf_name) { - if (all_vrf) { - for (ALL_LIST_ELEMENTS_RO(om->ospf, node, ospf)) { - if (!ospf->oi_running) - continue; - vrf = vrf_lookup_by_id(ospf->vrf_id); - FOR_ALL_INTERFACES (vrf, ifp) - show_mpls_te_link_sub(vty, ifp); - } - return CMD_SUCCESS; - } - ospf = ospf_lookup_by_inst_name(inst, vrf_name); - } else - ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); if (ospf == NULL || !ospf->oi_running) return CMD_SUCCESS; - vrf = vrf_lookup_by_id(ospf->vrf_id); + vrf = vrf_lookup_by_id(VRF_DEFAULT); if (!vrf) return CMD_SUCCESS; if (idx_interface) { - ifp = if_lookup_by_name( - argv[idx_interface]->arg, - ospf->vrf_id); + ifp = if_lookup_by_name(argv[idx_interface]->arg, VRF_DEFAULT); if (ifp == NULL) { vty_out(vty, "No such interface name in vrf %s\n", vrf->name); @@ -4403,7 +4380,7 @@ DEFUN (show_ip_ospf_mpls_te_db, return CMD_WARNING_CONFIG_FAILED; } /* Get the Subnet from the Link State Database */ - subnet = ls_find_subnet(OspfMplsTE.ted, pref); + subnet = ls_find_subnet(OspfMplsTE.ted, &pref); if (!subnet) { vty_out(vty, "No subnet found for ID %pFX\n", &pref); diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index 8c0afd8527..09dd0081eb 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -35,12 +35,11 @@ #include "ospfd/ospf_spf.h" #include "ospfd/ospf_route.h" #include "ospfd/ospf_zebra.h" -/*#include "ospfd/ospf_routemap.h" */ #include "ospfd/ospf_vty.h" #include "ospfd/ospf_dump.h" #include "ospfd/ospf_bfd.h" #include "ospfd/ospf_ldp_sync.h" - +#include "ospfd/ospf_network.h" FRR_CFG_DEFAULT_BOOL(OSPF_LOG_ADJACENCY_CHANGES, { .val_bool = true, .match_profile = "datacenter", }, @@ -611,6 +610,7 @@ DEFUN (ospf_area_range, "Advertised metric for this range\n") { VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + struct ospf_area *area; int idx_ipv4_number = 1; int idx_ipv4_prefixlen = 3; int idx_cost = 6; @@ -622,12 +622,14 @@ DEFUN (ospf_area_range, VTY_GET_OSPF_AREA_ID(area_id, format, argv[idx_ipv4_number]->arg); str2prefix_ipv4(argv[idx_ipv4_prefixlen]->arg, &p); - ospf_area_range_set(ospf, area_id, &p, OSPF_AREA_RANGE_ADVERTISE); - ospf_area_display_format_set(ospf, ospf_area_get(ospf, area_id), - format); + area = ospf_area_get(ospf, area_id); + ospf_area_display_format_set(ospf, area, format); + + ospf_area_range_set(ospf, area, area->ranges, &p, + OSPF_AREA_RANGE_ADVERTISE, false); if (argc > 5) { cost = strtoul(argv[idx_cost]->arg, NULL, 10); - ospf_area_range_cost_set(ospf, area_id, &p, cost); + ospf_area_range_cost_set(ospf, area, area->ranges, &p, cost); } return CMD_SUCCESS; @@ -647,6 +649,7 @@ DEFUN (ospf_area_range_cost, "Network prefix to be announced instead of range\n") { VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + struct ospf_area *area; int idx_ipv4_number = 1; int idx_ipv4_prefixlen = 3; int idx = 4; @@ -658,19 +661,20 @@ DEFUN (ospf_area_range_cost, VTY_GET_OSPF_AREA_ID(area_id, format, argv[idx_ipv4_number]->arg); str2prefix_ipv4(argv[idx_ipv4_prefixlen]->arg, &p); - ospf_area_range_set(ospf, area_id, &p, OSPF_AREA_RANGE_ADVERTISE); - ospf_area_display_format_set(ospf, ospf_area_get(ospf, area_id), - format); + area = ospf_area_get(ospf, area_id); + ospf_area_display_format_set(ospf, area, format); + ospf_area_range_set(ospf, area, area->ranges, &p, + OSPF_AREA_RANGE_ADVERTISE, false); if (argv_find(argv, argc, "cost", &idx)) { cost = strtoul(argv[idx + 1]->arg, NULL, 10); - ospf_area_range_cost_set(ospf, area_id, &p, cost); + ospf_area_range_cost_set(ospf, area, area->ranges, &p, cost); } idx = 4; if (argv_find(argv, argc, "substitute", &idx)) { str2prefix_ipv4(argv[idx + 1]->arg, &s); - ospf_area_range_substitute_set(ospf, area_id, &p, &s); + ospf_area_range_substitute_set(ospf, area, &p, &s); } return CMD_SUCCESS; @@ -687,6 +691,7 @@ DEFUN (ospf_area_range_not_advertise, "DoNotAdvertise this range\n") { VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + struct ospf_area *area; int idx_ipv4_number = 1; int idx_ipv4_prefixlen = 3; struct prefix_ipv4 p; @@ -696,10 +701,11 @@ DEFUN (ospf_area_range_not_advertise, VTY_GET_OSPF_AREA_ID(area_id, format, argv[idx_ipv4_number]->arg); str2prefix_ipv4(argv[idx_ipv4_prefixlen]->arg, &p); - ospf_area_range_set(ospf, area_id, &p, 0); - ospf_area_display_format_set(ospf, ospf_area_get(ospf, area_id), - format); - ospf_area_range_substitute_unset(ospf, area_id, &p); + area = ospf_area_get(ospf, area_id); + ospf_area_display_format_set(ospf, area, format); + + ospf_area_range_set(ospf, area, area->ranges, &p, 0, false); + ospf_area_range_substitute_unset(ospf, area, &p); return CMD_SUCCESS; } @@ -721,6 +727,7 @@ DEFUN (no_ospf_area_range, "DoNotAdvertise this range\n") { VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + struct ospf_area *area; int idx_ipv4_number = 2; int idx_ipv4_prefixlen = 4; struct prefix_ipv4 p; @@ -730,7 +737,10 @@ DEFUN (no_ospf_area_range, VTY_GET_OSPF_AREA_ID(area_id, format, argv[idx_ipv4_number]->arg); str2prefix_ipv4(argv[idx_ipv4_prefixlen]->arg, &p); - ospf_area_range_unset(ospf, area_id, &p); + area = ospf_area_get(ospf, area_id); + ospf_area_display_format_set(ospf, area, format); + + ospf_area_range_unset(ospf, area, area->ranges, &p); return CMD_SUCCESS; } @@ -748,6 +758,7 @@ DEFUN (no_ospf_area_range_substitute, "Network prefix to be announced instead of range\n") { VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + struct ospf_area *area; int idx_ipv4_number = 2; int idx_ipv4_prefixlen = 4; int idx_ipv4_prefixlen_2 = 6; @@ -759,7 +770,10 @@ DEFUN (no_ospf_area_range_substitute, str2prefix_ipv4(argv[idx_ipv4_prefixlen]->arg, &p); str2prefix_ipv4(argv[idx_ipv4_prefixlen_2]->arg, &s); - ospf_area_range_substitute_unset(ospf, area_id, &p); + area = ospf_area_get(ospf, area_id); + ospf_area_display_format_set(ospf, area, format); + + ospf_area_range_substitute_unset(ospf, area, &p); return CMD_SUCCESS; } @@ -1433,15 +1447,35 @@ DEFUN (no_ospf_area_stub_no_summary, return CMD_SUCCESS; } -static int ospf_area_nssa_cmd_handler(struct vty *vty, int argc, - struct cmd_token **argv, int cfg_nosum, - int nosum) +DEFPY (ospf_area_nssa, + ospf_area_nssa_cmd, + "area <A.B.C.D|(0-4294967295)>$area_str nssa\ + [{\ + <translate-candidate|translate-never|translate-always>$translator_role\ + |default-information-originate$dflt_originate [{metric (0-16777214)$mval|metric-type (1-2)$mtype}]\ + |no-summary$no_summary\ + |suppress-fa$suppress_fa\ + }]", + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Configure OSPF area as nssa\n" + "Configure NSSA-ABR for translate election (default)\n" + "Configure NSSA-ABR to never translate\n" + "Configure NSSA-ABR to always translate\n" + "Originate Type 7 default into NSSA area\n" + "OSPF default metric\n" + "OSPF metric\n" + "OSPF metric type for default routes\n" + "Set OSPF External Type 1/2 metrics\n" + "Do not inject inter-area routes into nssa\n" + "Suppress forwarding address\n") { VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); struct in_addr area_id; int ret, format; - VTY_GET_OSPF_AREA_ID_NO_BB("NSSA", area_id, format, argv[1]->arg); + VTY_GET_OSPF_AREA_ID_NO_BB("NSSA", area_id, format, area_str); ret = ospf_area_nssa_set(ospf, area_id); ospf_area_display_format_set(ospf, ospf_area_get(ospf, area_id), @@ -1452,14 +1486,14 @@ static int ospf_area_nssa_cmd_handler(struct vty *vty, int argc, return CMD_WARNING_CONFIG_FAILED; } - if (argc > 3) { - if (strncmp(argv[3]->text, "translate-c", 11) == 0) + if (translator_role) { + if (strncmp(translator_role, "translate-c", 11) == 0) ospf_area_nssa_translator_role_set( ospf, area_id, OSPF_NSSA_ROLE_CANDIDATE); - else if (strncmp(argv[3]->text, "translate-n", 11) == 0) + else if (strncmp(translator_role, "translate-n", 11) == 0) ospf_area_nssa_translator_role_set( ospf, area_id, OSPF_NSSA_ROLE_NEVER); - else if (strncmp(argv[3]->text, "translate-a", 11) == 0) + else if (strncmp(translator_role, "translate-a", 11) == 0) ospf_area_nssa_translator_role_set( ospf, area_id, OSPF_NSSA_ROLE_ALWAYS); } else { @@ -1467,12 +1501,27 @@ static int ospf_area_nssa_cmd_handler(struct vty *vty, int argc, OSPF_NSSA_ROLE_CANDIDATE); } - if (cfg_nosum) { - if (nosum) - ospf_area_no_summary_set(ospf, area_id); - else - ospf_area_no_summary_unset(ospf, area_id); - } + if (dflt_originate) { + int metric_type = DEFAULT_METRIC_TYPE; + + if (mval_str == NULL) + mval = -1; + if (mtype_str) + (void)str2metric_type(mtype_str, &metric_type); + ospf_area_nssa_default_originate_set(ospf, area_id, mval, + metric_type); + } else + ospf_area_nssa_default_originate_unset(ospf, area_id); + + if (no_summary) + ospf_area_nssa_no_summary_set(ospf, area_id); + else + ospf_area_no_summary_unset(ospf, area_id); + + if (suppress_fa) + ospf_area_nssa_suppress_fa_set(ospf, area_id); + else + ospf_area_nssa_suppress_fa_unset(ospf, area_id); /* Flush the external LSA for the specified area */ ospf_flush_lsa_from_area(ospf, area_id, OSPF_AS_EXTERNAL_LSA); @@ -1482,168 +1531,125 @@ static int ospf_area_nssa_cmd_handler(struct vty *vty, int argc, return CMD_SUCCESS; } - -DEFUN (ospf_area_nssa_translate, - ospf_area_nssa_translate_cmd, - "area <A.B.C.D|(0-4294967295)> nssa <translate-candidate|translate-never|translate-always>", +DEFPY (no_ospf_area_nssa, + no_ospf_area_nssa_cmd, + "no area <A.B.C.D|(0-4294967295)>$area_str nssa\ + [{\ + <translate-candidate|translate-never|translate-always>\ + |default-information-originate [{metric (0-16777214)|metric-type (1-2)}]\ + |no-summary\ + |suppress-fa\ + }]", + NO_STR "OSPF area parameters\n" "OSPF area ID in IP address format\n" "OSPF area ID as a decimal value\n" "Configure OSPF area as nssa\n" "Configure NSSA-ABR for translate election (default)\n" "Configure NSSA-ABR to never translate\n" - "Configure NSSA-ABR to always translate\n") -{ - return ospf_area_nssa_cmd_handler(vty, argc, argv, 0, 0); -} - -DEFUN (ospf_area_nssa, - ospf_area_nssa_cmd, - "area <A.B.C.D|(0-4294967295)> nssa", - "OSPF area parameters\n" - "OSPF area ID in IP address format\n" - "OSPF area ID as a decimal value\n" - "Configure OSPF area as nssa\n") -{ - return ospf_area_nssa_cmd_handler(vty, argc, argv, 0, 0); -} - -DEFUN(ospf_area_nssa_suppress_fa, ospf_area_nssa_suppress_fa_cmd, - "area <A.B.C.D|(0-4294967295)> nssa suppress-fa", - "OSPF area parameters\n" - "OSPF area ID in IP address format\n" - "OSPF area ID as a decimal value\n" - "Configure OSPF area as nssa\n" - "Suppress forwarding address\n") + "Configure NSSA-ABR to always translate\n" + "Originate Type 7 default into NSSA area\n" + "OSPF default metric\n" + "OSPF metric\n" + "OSPF metric type for default routes\n" + "Set OSPF External Type 1/2 metrics\n" + "Do not inject inter-area routes into nssa\n" + "Suppress forwarding address\n") { - int idx_ipv4_number = 1; - struct in_addr area_id; - int format; - VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); - VTY_GET_OSPF_AREA_ID_NO_BB("NSSA", area_id, format, - argv[idx_ipv4_number]->arg); - - ospf_area_display_format_set(ospf, ospf_area_get(ospf, area_id), - format); - ospf_area_nssa_suppress_fa_set(ospf, area_id); - - ospf_schedule_abr_task(ospf); - - return CMD_SUCCESS; -} - -DEFUN(no_ospf_area_nssa_suppress_fa, no_ospf_area_nssa_suppress_fa_cmd, - "no area <A.B.C.D|(0-4294967295)> nssa suppress-fa", - NO_STR - "OSPF area parameters\n" - "OSPF area ID in IP address format\n" - "OSPF area ID as a decimal value\n" - "Configure OSPF area as nssa\n" - "Suppress forwarding address\n") -{ - int idx_ipv4_number = 2; struct in_addr area_id; int format; - VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + VTY_GET_OSPF_AREA_ID_NO_BB("NSSA", area_id, format, area_str); - VTY_GET_OSPF_AREA_ID_NO_BB("nssa", area_id, format, - argv[idx_ipv4_number]->arg); - - ospf_area_display_format_set(ospf, ospf_area_get(ospf, area_id), - format); + /* Flush the NSSA LSA for the specified area */ + ospf_flush_lsa_from_area(ospf, area_id, OSPF_AS_NSSA_LSA); + ospf_area_no_summary_unset(ospf, area_id); + ospf_area_nssa_default_originate_unset(ospf, area_id); ospf_area_nssa_suppress_fa_unset(ospf, area_id); + ospf_area_nssa_unset(ospf, area_id); ospf_schedule_abr_task(ospf); return CMD_SUCCESS; } -DEFUN (ospf_area_nssa_no_summary, - ospf_area_nssa_no_summary_cmd, - "area <A.B.C.D|(0-4294967295)> nssa no-summary", +DEFPY (ospf_area_nssa_range, + ospf_area_nssa_range_cmd, + "area <A.B.C.D|(0-4294967295)>$area_str nssa range A.B.C.D/M$prefix [<not-advertise$not_adv|cost (0-16777215)$cost>]", "OSPF area parameters\n" "OSPF area ID in IP address format\n" "OSPF area ID as a decimal value\n" "Configure OSPF area as nssa\n" - "Do not inject inter-area routes into nssa\n") + "Configured address range\n" + "Specify IPv4 prefix\n" + "Do not advertise\n" + "User specified metric for this range\n" + "Advertised metric for this range\n") { - int idx_ipv4_number = 1; - struct in_addr area_id; - int format; - VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); - VTY_GET_OSPF_AREA_ID_NO_BB("NSSA", area_id, format, - argv[idx_ipv4_number]->arg); - - ospf_area_display_format_set(ospf, ospf_area_get(ospf, area_id), - format); - ospf_area_nssa_no_summary_set(ospf, area_id); - - ospf_schedule_abr_task(ospf); - - return CMD_SUCCESS; -} - -DEFUN (no_ospf_area_nssa_no_summary, - no_ospf_area_nssa_no_summary_cmd, - "no area <A.B.C.D|(0-4294967295)> nssa no-summary", - NO_STR - "OSPF area parameters\n" - "OSPF area ID in IP address format\n" - "OSPF area ID as a decimal value\n" - "Configure OSPF area as nssa\n" - "Do not inject inter-area routes into nssa\n") -{ - int idx_ipv4_number = 2; + struct ospf_area *area; struct in_addr area_id; int format; + int advertise = 0; - VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + VTY_GET_OSPF_AREA_ID(area_id, format, area_str); + area = ospf_area_get(ospf, area_id); + ospf_area_display_format_set(ospf, area, format); - VTY_GET_OSPF_AREA_ID_NO_BB("nssa", area_id, format, - argv[idx_ipv4_number]->arg); + if (area->external_routing != OSPF_AREA_NSSA) { + vty_out(vty, "%% First configure %s as an NSSA area\n", + area_str); + return CMD_WARNING; + } - ospf_area_display_format_set(ospf, ospf_area_get(ospf, area_id), - format); - ospf_area_no_summary_unset(ospf, area_id); + if (!not_adv) + advertise = OSPF_AREA_RANGE_ADVERTISE; - ospf_schedule_abr_task(ospf); + ospf_area_range_set(ospf, area, area->nssa_ranges, + (struct prefix_ipv4 *)prefix, advertise, true); + if (cost_str) + ospf_area_range_cost_set(ospf, area, area->nssa_ranges, + (struct prefix_ipv4 *)prefix, cost); return CMD_SUCCESS; } -DEFUN (no_ospf_area_nssa, - no_ospf_area_nssa_cmd, - "no area <A.B.C.D|(0-4294967295)> nssa [<translate-candidate|translate-never|translate-always>]", +DEFPY (no_ospf_area_nssa_range, + no_ospf_area_nssa_range_cmd, + "no area <A.B.C.D|(0-4294967295)>$area_str nssa range A.B.C.D/M$prefix [<not-advertise|cost (0-16777215)>]", NO_STR "OSPF area parameters\n" "OSPF area ID in IP address format\n" "OSPF area ID as a decimal value\n" "Configure OSPF area as nssa\n" - "Configure NSSA-ABR for translate election (default)\n" - "Configure NSSA-ABR to never translate\n" - "Configure NSSA-ABR to always translate\n") + "Configured address range\n" + "Specify IPv4 prefix\n" + "Do not advertise\n" + "User specified metric for this range\n" + "Advertised metric for this range\n") { VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); - int idx_ipv4_number = 2; + struct ospf_area *area; struct in_addr area_id; int format; - VTY_GET_OSPF_AREA_ID_NO_BB("NSSA", area_id, format, - argv[idx_ipv4_number]->arg); + VTY_GET_OSPF_AREA_ID(area_id, format, area_str); + area = ospf_area_get(ospf, area_id); + ospf_area_display_format_set(ospf, area, format); - /* Flush the NSSA LSA for the specified area */ - ospf_flush_lsa_from_area(ospf, area_id, OSPF_AS_NSSA_LSA); - ospf_area_nssa_unset(ospf, area_id, argc); + if (area->external_routing != OSPF_AREA_NSSA) { + vty_out(vty, "%% First configure %s as an NSSA area\n", + area_str); + return CMD_WARNING; + } - ospf_schedule_abr_task(ospf); + ospf_area_range_unset(ospf, area, area->nssa_ranges, + (struct prefix_ipv4 *)prefix); return CMD_SUCCESS; } - DEFUN (ospf_area_default_cost, ospf_area_default_cost_cmd, "area <A.B.C.D|(0-4294967295)> default-cost (0-16777215)", @@ -3396,6 +3402,23 @@ static int show_ip_ospf_common(struct vty *vty, struct ospf *ospf, /* show LDP-Sync status */ ospf_ldp_sync_show_info(vty, ospf, json_vrf, json ? 1 : 0); + /* Socket buffer sizes */ + if (json) { + if (ospf->recv_sock_bufsize != OSPF_DEFAULT_SOCK_BUFSIZE) + json_object_int_add(json_vrf, "recvSockBufsize", + ospf->recv_sock_bufsize); + if (ospf->send_sock_bufsize != OSPF_DEFAULT_SOCK_BUFSIZE) + json_object_int_add(json_vrf, "sendSockBufsize", + ospf->send_sock_bufsize); + } else { + if (ospf->recv_sock_bufsize != OSPF_DEFAULT_SOCK_BUFSIZE) + vty_out(vty, " Receive socket bufsize: %u\n", + ospf->recv_sock_bufsize); + if (ospf->send_sock_bufsize != OSPF_DEFAULT_SOCK_BUFSIZE) + vty_out(vty, " Send socket bufsize: %u\n", + ospf->send_sock_bufsize); + } + /* Show each area status. */ for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area)) show_ip_ospf_area(vty, area, json_areas, json ? 1 : 0); @@ -4407,15 +4430,10 @@ static void show_ip_ospf_neighbour_brief(struct vty *vty, json_neighbor = json_object_new_object(); ospf_nbr_ism_state_message(nbr, msgbuf, sizeof(msgbuf)); -#if CONFDATE > 20230321 - CPP_NOTICE( - "Remove show_ip_ospf_neighbor_sub() JSON keys: priority, state, deadTimeMsecs, address, retransmitCounter, requestCounter, dbSummaryCounter") -#endif - json_object_int_add(json_neighbor, "priority", nbr->priority); - json_object_string_add(json_neighbor, "state", msgbuf); + json_object_string_add(json_neighbor, "nbrState", msgbuf); + json_object_int_add(json_neighbor, "nbrPriority", nbr->priority); - json_object_string_add(json_neighbor, "nbrState", msgbuf); json_object_string_add( json_neighbor, "converged", @@ -4432,8 +4450,6 @@ static void show_ip_ospf_neighbour_brief(struct vty *vty, 1000LL; json_object_int_add(json_neighbor, "upTimeInMsec", time_val); - json_object_int_add(json_neighbor, "deadTimeMsecs", - time_store); json_object_int_add(json_neighbor, "routerDeadIntervalTimerDueMsec", time_store); @@ -4452,24 +4468,16 @@ static void show_ip_ospf_neighbour_brief(struct vty *vty, "routerDeadIntervalTimerDueMsec", "inactive"); } - json_object_string_addf(json_neighbor, "address", "%pI4", - &nbr->src); json_object_string_addf(json_neighbor, "ifaceAddress", "%pI4", &nbr->src); json_object_string_add(json_neighbor, "ifaceName", IF_NAME(nbr->oi)); - json_object_int_add(json_neighbor, "retransmitCounter", - ospf_ls_retransmit_count(nbr)); json_object_int_add(json_neighbor, "linkStateRetransmissionListCounter", ospf_ls_retransmit_count(nbr)); - json_object_int_add(json_neighbor, "requestCounter", - ospf_ls_request_count(nbr)); json_object_int_add(json_neighbor, "linkStateRequestListCounter", ospf_ls_request_count(nbr)); - json_object_int_add(json_neighbor, "dbSummaryCounter", - ospf_db_summary_count(nbr)); json_object_int_add(json_neighbor, "databaseSummaryListCounter", ospf_db_summary_count(nbr)); @@ -5065,6 +5073,7 @@ static void show_ip_ospf_neighbor_detail_sub(struct vty *vty, json_object *json_neigh = NULL, *json_neigh_array = NULL; char neigh_str[INET_ADDRSTRLEN] = {0}; char neigh_state[16] = {0}; + struct ospf_neighbor *nbr_dr, *nbr_bdr; if (use_json) { if (prev_nbr && @@ -5192,19 +5201,38 @@ static void show_ip_ospf_neighbor_detail_sub(struct vty *vty, } } - /* Show Designated Rotuer ID. */ - if (use_json) - json_object_string_addf(json_neigh, "routerDesignatedId", - "%pI4", &nbr->d_router); - else - vty_out(vty, " DR is %pI4,", &nbr->d_router); + /* Show Designated Router ID. */ + if (DR(oi).s_addr == INADDR_ANY) { + if (!use_json) + vty_out(vty, + " No designated router on this network\n"); + } else { + nbr_dr = ospf_nbr_lookup_by_addr(oi->nbrs, &DR(oi)); + if (nbr_dr) { + if (use_json) + json_object_string_addf( + json_neigh, "routerDesignatedId", + "%pI4", &nbr_dr->router_id); + else + vty_out(vty, " DR is %pI4,", + &nbr_dr->router_id); + } + } - /* Show Backup Designated Rotuer ID. */ - if (use_json) - json_object_string_addf(json_neigh, "routerDesignatedBackupId", - "%pI4", &nbr->bd_router); - else - vty_out(vty, " BDR is %pI4\n", &nbr->bd_router); + /* Show Backup Designated Router ID. */ + nbr_bdr = ospf_nbr_lookup_by_addr(oi->nbrs, &BDR(oi)); + if (nbr_bdr == NULL) { + if (!use_json) + vty_out(vty, + " No backup designated router on this network\n"); + } else { + if (use_json) + json_object_string_addf(json_neigh, + "routerDesignatedBackupId", + "%pI4", &nbr_bdr->router_id); + else + vty_out(vty, " BDR is %pI4\n", &nbr_bdr->router_id); + } /* Show options. */ if (use_json) { @@ -10053,12 +10081,21 @@ static int ospf_show_gr_helper_details(struct vty *vty, struct ospf *ospf, json_object_string_add(json_vrf, "strictLsaCheck", (ospf->strict_lsa_check) ? "Enabled" : "Disabled"); +#if CONFDATE > 20240401 + CPP_NOTICE("Remove deprecated json key: restartSupoort") +#endif json_object_string_add( json_vrf, "restartSupoort", (ospf->only_planned_restart) ? "Planned Restart only" : "Planned and Unplanned Restarts"); + json_object_string_add( + json_vrf, "restartSupport", + (ospf->only_planned_restart) + ? "Planned Restart only" + : "Planned and Unplanned Restarts"); + json_object_int_add(json_vrf, "supportedGracePeriod", ospf->supported_grace_time); @@ -11981,29 +12018,62 @@ static int config_write_ospf_area(struct vty *vty, struct ospf *ospf) vty_out(vty, " no-summary\n"); vty_out(vty, "\n"); } else if (area->external_routing == OSPF_AREA_NSSA) { + vty_out(vty, " area %s nssa", buf); + switch (area->NSSATranslatorRole) { case OSPF_NSSA_ROLE_NEVER: - vty_out(vty, - " area %s nssa translate-never\n", - buf); + vty_out(vty, " translate-never"); break; case OSPF_NSSA_ROLE_ALWAYS: - vty_out(vty, - " area %s nssa translate-always\n", - buf); + vty_out(vty, " translate-always"); break; case OSPF_NSSA_ROLE_CANDIDATE: - vty_out(vty, " area %s nssa \n", buf); break; } - if (area->no_summary) + + if (area->nssa_default_originate.enabled) { vty_out(vty, - " area %s nssa no-summary\n", - buf); + " default-information-originate"); + if (area->nssa_default_originate + .metric_value != -1) + vty_out(vty, " metric %d", + area->nssa_default_originate + .metric_value); + if (area->nssa_default_originate + .metric_type != + DEFAULT_METRIC_TYPE) + vty_out(vty, " metric-type 1"); + } + + if (area->no_summary) + vty_out(vty, " no-summary"); if (area->suppress_fa) - vty_out(vty, - " area %s nssa suppress-fa\n", - buf); + vty_out(vty, " suppress-fa"); + vty_out(vty, "\n"); + + for (rn1 = route_top(area->nssa_ranges); rn1; + rn1 = route_next(rn1)) { + struct ospf_area_range *range; + + range = rn1->info; + if (!range) + continue; + + vty_out(vty, " area %s nssa range %pFX", + buf, &rn1->p); + + if (range->cost_config != + OSPF_AREA_RANGE_COST_UNSPEC) + vty_out(vty, " cost %u", + range->cost_config); + + if (!CHECK_FLAG( + range->flags, + OSPF_AREA_RANGE_ADVERTISE)) + vty_out(vty, " not-advertise"); + + vty_out(vty, "\n"); + } } if (area->default_cost != 1) @@ -12434,6 +12504,9 @@ static int ospf_config_write_one(struct vty *vty, struct ospf *ospf) if (ospf->fr_configured) vty_out(vty, " flood-reduction\n"); + if (!ospf->intf_socket_enabled) + vty_out(vty, " no socket-per-interface\n"); + /* Redistribute information print. */ config_write_ospf_redistribute(vty, ospf); @@ -12490,6 +12563,22 @@ static int ospf_config_write_one(struct vty *vty, struct ospf *ospf) /* LDP-Sync print */ ospf_ldp_sync_write_config(vty, ospf); + /* Socket buffer sizes */ + if (ospf->recv_sock_bufsize != OSPF_DEFAULT_SOCK_BUFSIZE) { + if (ospf->send_sock_bufsize == ospf->recv_sock_bufsize) + vty_out(vty, " socket buffer all %u\n", + ospf->recv_sock_bufsize); + else + vty_out(vty, " socket buffer recv %u\n", + ospf->recv_sock_bufsize); + } + + if (ospf->send_sock_bufsize != OSPF_DEFAULT_SOCK_BUFSIZE && + ospf->send_sock_bufsize != ospf->recv_sock_bufsize) + vty_out(vty, " socket buffer send %u\n", + ospf->send_sock_bufsize); + + vty_out(vty, "exit\n"); write++; @@ -12946,6 +13035,71 @@ DEFPY(no_flood_reduction_area, no_flood_reduction_area_cmd, return CMD_SUCCESS; } +DEFPY(ospf_socket_bufsizes, + ospf_socket_bufsizes_cmd, + "[no] socket buffer <send$send_val | recv$recv_val | all$all_val> \ + ![(1-4000000000)$bufsize]", + NO_STR + "Socket parameters\n" + "Buffer size configuration\n" + "Send buffer size\n" + "Receive buffer size\n" + "Both send and receive buffer sizes\n" + "Buffer size, in bytes\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + uint32_t recvsz, sendsz; + + if (no) + bufsize = OSPF_DEFAULT_SOCK_BUFSIZE; + + if (all_val) { + recvsz = bufsize; + sendsz = bufsize; + } else if (send_val) { + sendsz = bufsize; + recvsz = ospf->recv_sock_bufsize; + } else if (recv_val) { + recvsz = bufsize; + sendsz = ospf->send_sock_bufsize; + } else + return CMD_SUCCESS; + + /* React to a change by modifying existing sockets */ + ospf_update_bufsize(ospf, recvsz, sendsz); + + return CMD_SUCCESS; +} + +DEFPY (per_intf_socket, + per_intf_socket_cmd, + "[no] socket-per-interface", + NO_STR + "Use write socket per interface\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + struct listnode *node; + struct ospf_interface *oi; + + if (no) { + if (ospf->intf_socket_enabled) { + ospf->intf_socket_enabled = false; + + /* Iterate and close any sockets */ + for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi)) + ospf_ifp_sock_close(oi->ifp); + } + } else if (!ospf->intf_socket_enabled) { + ospf->intf_socket_enabled = true; + + /* Iterate and open sockets */ + for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi)) + ospf_ifp_sock_init(oi->ifp); + } + + return CMD_SUCCESS; +} + void ospf_vty_clear_init(void) { install_element(ENABLE_NODE, &clear_ip_ospf_interface_cmd); @@ -13029,12 +13183,9 @@ void ospf_vty_init(void) /* "area nssa" commands. */ install_element(OSPF_NODE, &ospf_area_nssa_cmd); - install_element(OSPF_NODE, &ospf_area_nssa_translate_cmd); - install_element(OSPF_NODE, &ospf_area_nssa_no_summary_cmd); - install_element(OSPF_NODE, &no_ospf_area_nssa_no_summary_cmd); - install_element(OSPF_NODE, &ospf_area_nssa_suppress_fa_cmd); - install_element(OSPF_NODE, &no_ospf_area_nssa_suppress_fa_cmd); install_element(OSPF_NODE, &no_ospf_area_nssa_cmd); + install_element(OSPF_NODE, &ospf_area_nssa_range_cmd); + install_element(OSPF_NODE, &no_ospf_area_nssa_range_cmd); install_element(OSPF_NODE, &ospf_area_default_cost_cmd); install_element(OSPF_NODE, &no_ospf_area_default_cost_cmd); @@ -13112,6 +13263,9 @@ void ospf_vty_init(void) install_element(OSPF_NODE, &flood_reduction_area_cmd); install_element(OSPF_NODE, &no_flood_reduction_area_cmd); + install_element(OSPF_NODE, &ospf_socket_bufsizes_cmd); + install_element(OSPF_NODE, &per_intf_socket_cmd); + /* Init interface related vty commands. */ ospf_vty_if_init(); diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c index 3e02d3c33e..0b770a8364 100644 --- a/ospfd/ospf_zebra.c +++ b/ospfd/ospf_zebra.c @@ -20,6 +20,7 @@ #include "log.h" #include "route_opaque.h" #include "lib/bfd.h" +#include "lib/lib_errors.h" #include "nexthop.h" #include "ospfd/ospfd.h" @@ -955,8 +956,8 @@ int ospf_redistribute_default_set(struct ospf *ospf, int originate, int mtype, type_str = "always"; ospf->redistribute++; ospf_external_add(ospf, DEFAULT_ROUTE, 0); - ospf_external_info_add(ospf, DEFAULT_ROUTE, 0, p, 0, nexthop, - 0); + ospf_external_info_add(ospf, DEFAULT_ROUTE, 0, p, 0, nexthop, 0, + DEFAULT_DEFAULT_METRIC); break; } @@ -1302,9 +1303,11 @@ static int ospf_zebra_read_route(ZAPI_CALLBACK_ARGS) rt_type = DEFAULT_ROUTE; if (IS_DEBUG_OSPF(zebra, ZEBRA_REDISTRIBUTE)) - zlog_debug("%s: cmd %s from client %s: vrf_id %d, p %pFX", - __func__, zserv_command_string(cmd), - zebra_route_string(api.type), vrf_id, &api.prefix); + zlog_debug( + "%s: cmd %s from client %s: vrf_id %d, p %pFX, metric %d", + __func__, zserv_command_string(cmd), + zebra_route_string(api.type), vrf_id, &api.prefix, + api.metric); if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD) { /* XXX|HACK|TODO|FIXME: @@ -1331,7 +1334,8 @@ static int ospf_zebra_read_route(ZAPI_CALLBACK_ARGS) p); ei = ospf_external_info_add(ospf, rt_type, api.instance, p, - ifindex, nexthop, api.tag); + ifindex, nexthop, api.tag, + api.metric); if (ei == NULL) { /* Nothing has changed, so nothing to do; return */ return 0; @@ -1470,6 +1474,61 @@ static int ospf_zebra_read_route(ZAPI_CALLBACK_ARGS) return 0; } +void ospf_zebra_import_default_route(struct ospf *ospf, bool unreg) +{ + struct prefix prefix = {}; + int command; + + if (zclient->sock < 0) { + if (IS_DEBUG_OSPF(zebra, ZEBRA)) + zlog_debug(" Not connected to Zebra"); + return; + } + + prefix.family = AF_INET; + prefix.prefixlen = 0; + + if (unreg) + command = ZEBRA_NEXTHOP_UNREGISTER; + else + command = ZEBRA_NEXTHOP_REGISTER; + + if (IS_DEBUG_OSPF(zebra, ZEBRA)) + zlog_debug("%s: sending cmd %s for %pFX (vrf %u)", __func__, + zserv_command_string(command), &prefix, + ospf->vrf_id); + + if (zclient_send_rnh(zclient, command, &prefix, SAFI_UNICAST, false, + true, ospf->vrf_id) == ZCLIENT_SEND_FAILURE) + flog_err(EC_LIB_ZAPI_SOCKET, "%s: zclient_send_rnh() failed", + __func__); +} + +static int ospf_zebra_import_check_update(ZAPI_CALLBACK_ARGS) +{ + struct ospf *ospf; + struct zapi_route nhr; + struct prefix matched; + + ospf = ospf_lookup_by_vrf_id(vrf_id); + if (ospf == NULL || !IS_OSPF_ASBR(ospf)) + return 0; + + if (!zapi_nexthop_update_decode(zclient->ibuf, &matched, &nhr)) { + zlog_err("%s[%u]: Failure to decode route", __func__, + ospf->vrf_id); + return -1; + } + + if (matched.family != AF_INET || matched.prefixlen != 0 || + nhr.type == ZEBRA_ROUTE_OSPF) + return 0; + + ospf->nssa_default_import_check.status = !!nhr.nexthop_num; + ospf_abr_nssa_type7_defaults(ospf); + + return 0; +} int ospf_distribute_list_out_set(struct ospf *ospf, int type, const char *name) { @@ -2132,6 +2191,7 @@ static zclient_handler *const ospf_handlers[] = { [ZEBRA_REDISTRIBUTE_ROUTE_ADD] = ospf_zebra_read_route, [ZEBRA_REDISTRIBUTE_ROUTE_DEL] = ospf_zebra_read_route, + [ZEBRA_NEXTHOP_UPDATE] = ospf_zebra_import_check_update, [ZEBRA_OPAQUE_MESSAGE] = ospf_opaque_msg_handler, diff --git a/ospfd/ospf_zebra.h b/ospfd/ospf_zebra.h index 711b1e7a61..86a5678fc4 100644 --- a/ospfd/ospf_zebra.h +++ b/ospfd/ospf_zebra.h @@ -69,6 +69,7 @@ extern int ospf_redistribute_set(struct ospf *, struct ospf_redist *, int, unsigned short, int, int); extern int ospf_redistribute_unset(struct ospf *, int, unsigned short); extern int ospf_redistribute_default_set(struct ospf *, int, int, int); +extern void ospf_zebra_import_default_route(struct ospf *ospf, bool unreg); extern int ospf_distribute_list_out_set(struct ospf *, int, const char *); extern int ospf_distribute_list_out_unset(struct ospf *, int, const char *); extern void ospf_routemap_set(struct ospf_redist *, const char *); diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c index 112ddfedb7..7e83714c0a 100644 --- a/ospfd/ospfd.c +++ b/ospfd/ospfd.c @@ -419,6 +419,10 @@ struct ospf *ospf_new_alloc(unsigned short instance, const char *name) QOBJ_REG(new, ospf); new->fd = -1; + new->intf_socket_enabled = true; + + new->recv_sock_bufsize = OSPF_DEFAULT_SOCK_BUFSIZE; + new->send_sock_bufsize = OSPF_DEFAULT_SOCK_BUFSIZE; return new; } @@ -963,6 +967,7 @@ struct ospf_area *ospf_area_new(struct ospf *ospf, struct in_addr area_id) new->oiflist = list_new(); new->ranges = route_table_init(); + new->nssa_ranges = route_table_init(); if (area_id.s_addr == OSPF_AREA_BACKBONE) ospf->backbone = new; @@ -1006,6 +1011,7 @@ static void ospf_area_free(struct ospf_area *area) ospf_lsa_unlock(&area->router_lsa_self); route_table_finish(area->ranges); + route_table_finish(area->nssa_ranges); list_delete(&area->oiflist); if (EXPORT_NAME(area)) @@ -1029,13 +1035,14 @@ void ospf_area_check_free(struct ospf *ospf, struct in_addr area_id) struct ospf_area *area; area = ospf_area_lookup_by_area_id(ospf, area_id); - if (area && listcount(area->oiflist) == 0 && area->ranges->top == NULL - && !ospf_vl_count(ospf, area) - && area->shortcut_configured == OSPF_SHORTCUT_DEFAULT - && area->external_routing == OSPF_AREA_DEFAULT - && area->no_summary == 0 && area->default_cost == 1 - && EXPORT_NAME(area) == NULL && IMPORT_NAME(area) == NULL - && area->auth_type == OSPF_AUTH_NULL) { + if (area && listcount(area->oiflist) == 0 && + area->ranges->top == NULL && area->nssa_ranges->top == NULL && + !ospf_vl_count(ospf, area) && + area->shortcut_configured == OSPF_SHORTCUT_DEFAULT && + area->external_routing == OSPF_AREA_DEFAULT && + area->no_summary == 0 && area->default_cost == 1 && + EXPORT_NAME(area) == NULL && IMPORT_NAME(area) == NULL && + area->auth_type == OSPF_AUTH_NULL) { listnode_delete(ospf->areas, area); ospf_area_free(area); } @@ -1701,7 +1708,7 @@ int ospf_area_nssa_set(struct ospf *ospf, struct in_addr area_id) return 1; } -int ospf_area_nssa_unset(struct ospf *ospf, struct in_addr area_id, int argc) +int ospf_area_nssa_unset(struct ospf *ospf, struct in_addr area_id) { struct ospf_area *area; @@ -1709,22 +1716,14 @@ int ospf_area_nssa_unset(struct ospf *ospf, struct in_addr area_id, int argc) if (area == NULL) return 0; - /* argc < 5 -> 'no area x nssa' */ - if (argc < 5 && area->external_routing == OSPF_AREA_NSSA) { - ospf->anyNSSA--; - /* set NSSA area defaults */ - area->no_summary = 0; - area->suppress_fa = 0; - area->NSSATranslatorRole = OSPF_NSSA_ROLE_CANDIDATE; - area->NSSATranslatorState = OSPF_NSSA_TRANSLATE_DISABLED; - area->NSSATranslatorStabilityInterval = - OSPF_NSSA_TRANS_STABLE_DEFAULT; - ospf_area_type_set(area, OSPF_AREA_DEFAULT); - } else { - ospf_area_nssa_translator_role_set(ospf, area_id, - OSPF_NSSA_ROLE_CANDIDATE); - } - + ospf->anyNSSA--; + /* set NSSA area defaults */ + area->no_summary = 0; + area->suppress_fa = 0; + area->NSSATranslatorRole = OSPF_NSSA_ROLE_CANDIDATE; + area->NSSATranslatorState = OSPF_NSSA_TRANSLATE_DISABLED; + area->NSSATranslatorStabilityInterval = OSPF_NSSA_TRANS_STABLE_DEFAULT; + ospf_area_type_set(area, OSPF_AREA_DEFAULT); ospf_area_check_free(ospf, area_id); return 1; @@ -1782,6 +1781,51 @@ int ospf_area_nssa_translator_role_set(struct ospf *ospf, return 1; } +void ospf_area_nssa_default_originate_set(struct ospf *ospf, + struct in_addr area_id, int metric, + int metric_type) +{ + struct ospf_area *area; + + area = ospf_area_lookup_by_area_id(ospf, area_id); + if (area == NULL) + return; + + if (!area->nssa_default_originate.enabled) { + area->nssa_default_originate.enabled = true; + if (++ospf->nssa_default_import_check.refcnt == 1) { + ospf->nssa_default_import_check.status = false; + ospf_zebra_import_default_route(ospf, false); + } + } + + area->nssa_default_originate.metric_value = metric; + area->nssa_default_originate.metric_type = metric_type; +} + +void ospf_area_nssa_default_originate_unset(struct ospf *ospf, + struct in_addr area_id) +{ + struct ospf_area *area; + + area = ospf_area_lookup_by_area_id(ospf, area_id); + if (area == NULL) + return; + + if (area->nssa_default_originate.enabled) { + area->nssa_default_originate.enabled = false; + if (--ospf->nssa_default_import_check.refcnt == 0) { + ospf->nssa_default_import_check.status = false; + ospf_zebra_import_default_route(ospf, true); + } + area->nssa_default_originate.metric_value = -1; + area->nssa_default_originate.metric_type = -1; + + if (!IS_OSPF_ABR(ospf)) + ospf_abr_nssa_type7_defaults(ospf); + } +} + int ospf_area_export_list_set(struct ospf *ospf, struct ospf_area *area, const char *list_name) { @@ -2140,6 +2184,32 @@ int ospf_nbr_nbma_poll_interval_unset(struct ospf *ospf, struct in_addr addr) return 1; } +/* + * Update socket bufsize(s), usually after config change + */ +void ospf_update_bufsize(struct ospf *ospf, uint32_t recvsize, + uint32_t sendsize) +{ + enum ospf_sock_type_e type = OSPF_SOCK_NONE; + + /* Figure out whether there's been a change */ + if (recvsize != ospf->recv_sock_bufsize) { + type = OSPF_SOCK_RECV; + ospf->recv_sock_bufsize = recvsize; + + if (sendsize != ospf->send_sock_bufsize) { + type = OSPF_SOCK_BOTH; + ospf->send_sock_bufsize = sendsize; + } + } else if (sendsize != ospf->send_sock_bufsize) { + type = OSPF_SOCK_SEND; + ospf->send_sock_bufsize = sendsize; + } + + if (type != OSPF_SOCK_NONE) + ospf_sock_bufsize_update(ospf, ospf->fd, type); +} + void ospf_master_init(struct event_loop *master) { memset(&ospf_master, 0, sizeof(ospf_master)); diff --git a/ospfd/ospfd.h b/ospfd/ospfd.h index 6727d802a6..1f8d1a32e6 100644 --- a/ospfd/ospfd.h +++ b/ospfd/ospfd.h @@ -67,6 +67,9 @@ #define OSPF_LS_REFRESH_SHIFT (60 * 15) #define OSPF_LS_REFRESH_JITTER 60 +/* Default socket buffer size */ +#define OSPF_DEFAULT_SOCK_BUFSIZE (8 * 1024 * 1024) + struct ospf_external { unsigned short instance; struct route_table *external_info; @@ -302,6 +305,18 @@ struct ospf { int default_metric; /* Default metric for redistribute. */ + /* NSSA default-information-originate */ + struct { + /* # of NSSA areas requesting default information */ + uint16_t refcnt; + + /* + * Whether a default route known through non-OSPF protocol is + * present in the RIB. + */ + bool status; + } nssa_default_import_check; + #define OSPF_LSA_REFRESHER_GRANULARITY 10 #define OSPF_LSA_REFRESHER_SLOTS \ ((OSPF_LS_REFRESH_TIME + OSPF_LS_REFRESH_SHIFT) \ @@ -412,6 +427,13 @@ struct ospf { /* Flood Reduction configuration state */ bool fr_configured; + /* Socket buffer sizes */ + uint32_t recv_sock_bufsize; + uint32_t send_sock_bufsize; + + /* Per-interface write socket */ + bool intf_socket_enabled; + QOBJ_FIELDS; }; DECLARE_QOBJ_TYPE(ospf); @@ -517,6 +539,7 @@ struct ospf_area { #define OSPF_TRANSIT_FALSE 0 #define OSPF_TRANSIT_TRUE 1 struct route_table *ranges; /* Configured Area Ranges. */ + struct route_table *nssa_ranges; /* Configured NSSA Area Ranges. */ /* RFC3137 stub router state flags for area */ uint8_t stub_router_state; @@ -561,6 +584,13 @@ struct ospf_area { #define PREFIX_LIST_OUT(A) (A)->plist_out.list #define PREFIX_NAME_OUT(A) (A)->plist_out.name + /* NSSA default-information-originate */ + struct { + bool enabled; + int metric_type; + int metric_value; + } nssa_default_originate; + /* Shortest Path Tree. */ struct vertex *spf; struct list *spf_vertex_list; @@ -697,14 +727,18 @@ extern int ospf_area_no_summary_set(struct ospf *ospf, struct in_addr area_id); extern int ospf_area_no_summary_unset(struct ospf *ospf, struct in_addr area_id); extern int ospf_area_nssa_set(struct ospf *ospf, struct in_addr area_id); -extern int ospf_area_nssa_unset(struct ospf *ospf, struct in_addr area_id, - int argc); +extern int ospf_area_nssa_unset(struct ospf *ospf, struct in_addr area_id); extern int ospf_area_nssa_suppress_fa_set(struct ospf *ospf, struct in_addr area_id); extern int ospf_area_nssa_suppress_fa_unset(struct ospf *ospf, struct in_addr area_id); extern int ospf_area_nssa_translator_role_set(struct ospf *ospf, struct in_addr area_id, int role); +extern void ospf_area_nssa_default_originate_set(struct ospf *ospf, + struct in_addr area_id, + int metric, int metric_type); +extern void ospf_area_nssa_default_originate_unset(struct ospf *ospf, + struct in_addr area_id); extern int ospf_area_export_list_set(struct ospf *ospf, struct ospf_area *area_id, const char *list_name); @@ -769,6 +803,9 @@ int ospf_area_nssa_no_summary_set(struct ospf *ospf, struct in_addr area_id); const char *ospf_get_name(const struct ospf *ospf); extern struct ospf_interface *add_ospf_interface(struct connected *co, struct ospf_area *area); +/* Update socket bufsize(s), after config change */ +void ospf_update_bufsize(struct ospf *ospf, uint32_t recvsize, + uint32_t sendsize); extern int p_spaces_compare_func(const struct p_space *a, const struct p_space *b); diff --git a/pathd/path_ted.c b/pathd/path_ted.c index 86ce6d5a5f..df23f93127 100644 --- a/pathd/path_ted.c +++ b/pathd/path_ted.c @@ -269,7 +269,7 @@ uint32_t path_ted_query_type_c(struct prefix *prefix, uint8_t algo) switch (prefix->family) { case AF_INET: case AF_INET6: - subnet = ls_find_subnet(ted_state_g.ted, *prefix); + subnet = ls_find_subnet(ted_state_g.ted, prefix); if (subnet) { if ((CHECK_FLAG(subnet->ls_pref->flags, LS_PREF_SR)) && (subnet->ls_pref->sr.algo == algo)) @@ -299,7 +299,7 @@ uint32_t path_ted_query_type_e(struct prefix *prefix, uint32_t iface_id) switch (prefix->family) { case AF_INET: case AF_INET6: - subnet = ls_find_subnet(ted_state_g.ted, *prefix); + subnet = ls_find_subnet(ted_state_g.ted, prefix); if (subnet && subnet->vertex && subnet->vertex->outgoing_edges) { /* from the vertex linked in subnet */ diff --git a/pimd/pim6_cmd.c b/pimd/pim6_cmd.c index 9394e4c15a..94e8b874f7 100644 --- a/pimd/pim6_cmd.c +++ b/pimd/pim6_cmd.c @@ -539,11 +539,8 @@ DEFPY (interface_ipv6_mld_join, "Source address\n") { char xpath[XPATH_MAXLEN]; - struct ipaddr group_addr = {0}; - (void)str2ipaddr(group_str, &group_addr); - - if (!IN6_IS_ADDR_MULTICAST(&group_addr)) { + if (!IN6_IS_ADDR_MULTICAST(&group)) { vty_out(vty, "Invalid Multicast Address\n"); return CMD_WARNING_CONFIG_FAILED; } diff --git a/pimd/pim6_mld.c b/pimd/pim6_mld.c index 1d84a9941b..e74707b522 100644 --- a/pimd/pim6_mld.c +++ b/pimd/pim6_mld.c @@ -99,7 +99,7 @@ static inline uint8_t in6_multicast_scope(const pim_addr *addr) return addr->s6_addr[1] & 0xf; } -static inline bool in6_multicast_nofwd(const pim_addr *addr) +bool in6_multicast_nofwd(const pim_addr *addr) { return in6_multicast_scope(addr) <= IPV6_MULTICAST_SCOPE_LINK; } @@ -182,13 +182,11 @@ DECLARE_HASH(gm_gsq_pends, struct gm_gsq_pending, itm, gm_gsq_pending_cmp, * interface -> (S,G) */ -static int gm_sg_cmp(const struct gm_sg *a, const struct gm_sg *b) +int gm_sg_cmp(const struct gm_sg *a, const struct gm_sg *b) { return pim_sgaddr_cmp(a->sgaddr, b->sgaddr); } -DECLARE_RBTREE_UNIQ(gm_sgs, struct gm_sg, itm, gm_sg_cmp); - static struct gm_sg *gm_sg_find(struct gm_if *gm_ifp, pim_addr grp, pim_addr src) { diff --git a/pimd/pim6_mld.h b/pimd/pim6_mld.h index d3f1d39257..7634fb2ec4 100644 --- a/pimd/pim6_mld.h +++ b/pimd/pim6_mld.h @@ -113,6 +113,8 @@ struct gm_sg { */ struct gm_packet_sg *most_recent; }; +int gm_sg_cmp(const struct gm_sg *a, const struct gm_sg *b); +DECLARE_RBTREE_UNIQ(gm_sgs, struct gm_sg, itm, gm_sg_cmp); /* host tracking entry. addr will be one of: * @@ -352,5 +354,6 @@ static inline void gm_ifp_teardown(struct interface *ifp) #endif extern void gm_cli_init(void); +bool in6_multicast_nofwd(const pim_addr *addr); #endif /* PIM6_MLD_H */ diff --git a/pimd/pim_addr.h b/pimd/pim_addr.h index cc473d1f80..94c63bbccc 100644 --- a/pimd/pim_addr.h +++ b/pimd/pim_addr.h @@ -28,6 +28,7 @@ typedef struct in_addr pim_addr; #define PIM_MROUTE_DBG "mroute" #define PIMREG "pimreg" #define GM "IGMP" +#define IPPROTO_GM IPPROTO_IGMP #define PIM_ADDR_FUNCNAME(name) ipv4_##name @@ -57,6 +58,7 @@ typedef struct in6_addr pim_addr; #define PIM_MROUTE_DBG "mroute6" #define PIMREG "pim6reg" #define GM "MLD" +#define IPPROTO_GM IPPROTO_ICMPV6 #define PIM_ADDR_FUNCNAME(name) ipv6_##name diff --git a/pimd/pim_bsm.c b/pimd/pim_bsm.c index b36e006311..df9161943d 100644 --- a/pimd/pim_bsm.c +++ b/pimd/pim_bsm.c @@ -156,7 +156,6 @@ static void pim_on_bs_timer(struct event *t) pim_nht_bsr_del(scope->pim, scope->current_bsr); /* Reset scope zone data */ - scope->accept_nofwd_bsm = false; scope->state = ACCEPT_ANY; scope->current_bsr = PIMADDR_ANY; scope->current_bsr_prio = 0; @@ -1364,6 +1363,10 @@ int pim_bsm_process(struct interface *ifp, pim_sgaddr *sg, uint8_t *buf, } } + /* BSM packet is seen, so resetting accept_nofwd_bsm to false */ + if (pim->global_scope.accept_nofwd_bsm) + pim->global_scope.accept_nofwd_bsm = false; + if (!pim_addr_cmp(sg->grp, qpim_all_pim_routers_addr)) { /* Multicast BSMs are only accepted if source interface & IP * match RPF towards the BSR's IP address, or they have diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index 8c75b0a5b5..2e90cf9053 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -67,7 +67,7 @@ static struct cmd_node debug_node = { }; static struct vrf *pim_cmd_lookup_vrf(struct vty *vty, struct cmd_token *argv[], - const int argc, int *idx) + const int argc, int *idx, bool uj) { struct vrf *vrf; @@ -76,9 +76,13 @@ static struct vrf *pim_cmd_lookup_vrf(struct vty *vty, struct cmd_token *argv[], else vrf = vrf_lookup_by_id(VRF_DEFAULT); - if (!vrf) - vty_out(vty, "Specified VRF: %s does not exist\n", - argv[*idx]->arg); + if (!vrf) { + if (uj) + vty_json_empty(vty); + else + vty_out(vty, "Specified VRF: %s does not exist\n", + argv[*idx]->arg); + } return vrf; } @@ -822,19 +826,172 @@ static void igmp_show_statistics(struct pim_instance *pim, struct vty *vty, } } -static void igmp_show_groups(struct pim_instance *pim, struct vty *vty, bool uj) +static void igmp_source_json_helper(struct gm_source *src, + json_object *json_sources, char *source_str, + char *mmss, char *uptime) +{ + json_object *json_source = NULL; + + json_source = json_object_new_object(); + if (!json_source) + return; + + json_object_string_add(json_source, "source", source_str); + json_object_string_add(json_source, "timer", mmss); + json_object_boolean_add(json_source, "forwarded", + IGMP_SOURCE_TEST_FORWARDING(src->source_flags)); + json_object_string_add(json_source, "uptime", uptime); + json_object_array_add(json_sources, json_source); +} + +static void igmp_group_print(struct interface *ifp, struct vty *vty, bool uj, + json_object *json, struct gm_group *grp, + time_t now, bool detail) { - struct interface *ifp; - time_t now; - json_object *json = NULL; json_object *json_iface = NULL; json_object *json_group = NULL; json_object *json_groups = NULL; + char group_str[INET_ADDRSTRLEN]; + char hhmmss[PIM_TIME_STRLEN]; + char uptime[PIM_TIME_STRLEN]; + + pim_inet4_dump("<group?>", grp->group_addr, group_str, + sizeof(group_str)); + pim_time_timer_to_hhmmss(hhmmss, sizeof(hhmmss), grp->t_group_timer); + pim_time_uptime(uptime, sizeof(uptime), now - grp->group_creation); + + if (uj) { + json_object_object_get_ex(json, ifp->name, &json_iface); + if (!json_iface) { + json_iface = json_object_new_object(); + if (!json_iface) + return; + json_object_pim_ifp_add(json_iface, ifp); + json_object_object_add(json, ifp->name, json_iface); + json_groups = json_object_new_array(); + if (!json_groups) + return; + json_object_object_add(json_iface, "groups", + json_groups); + } + + json_object_object_get_ex(json_iface, "groups", &json_groups); + if (json_groups) { + json_group = json_object_new_object(); + if (!json_group) + return; + + json_object_string_add(json_group, "group", group_str); + if (grp->igmp_version == IGMP_DEFAULT_VERSION) + json_object_string_add( + json_group, "mode", + grp->group_filtermode_isexcl + ? "EXCLUDE" + : "INCLUDE"); + + json_object_string_add(json_group, "timer", hhmmss); + json_object_int_add( + json_group, "sourcesCount", + grp->group_source_list + ? listcount(grp->group_source_list) + : 0); + json_object_int_add(json_group, "version", + grp->igmp_version); + json_object_string_add(json_group, "uptime", uptime); + json_object_array_add(json_groups, json_group); + + if (detail) { + struct listnode *srcnode; + struct gm_source *src; + json_object *json_sources = NULL; + + json_sources = json_object_new_array(); + if (!json_sources) + return; + + json_object_object_add(json_group, "sources", + json_sources); + + for (ALL_LIST_ELEMENTS_RO( + grp->group_source_list, srcnode, + src)) { + char source_str[INET_ADDRSTRLEN]; + char mmss[PIM_TIME_STRLEN]; + char src_uptime[PIM_TIME_STRLEN]; + + pim_inet4_dump( + "<source?>", src->source_addr, + source_str, sizeof(source_str)); + pim_time_timer_to_mmss( + mmss, sizeof(mmss), + src->t_source_timer); + pim_time_uptime( + src_uptime, sizeof(src_uptime), + now - src->source_creation); + + igmp_source_json_helper( + src, json_sources, source_str, + mmss, src_uptime); + } + } + } + } else { + if (detail) { + struct listnode *srcnode; + struct gm_source *src; + + for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, + srcnode, src)) { + char source_str[INET_ADDRSTRLEN]; + + pim_inet4_dump("<source?>", src->source_addr, + source_str, sizeof(source_str)); + + vty_out(vty, + "%-16s %-15s %4s %8s %-15s %d %8s\n", + ifp->name, group_str, + grp->igmp_version == 3 + ? (grp->group_filtermode_isexcl + ? "EXCL" + : "INCL") + : "----", + hhmmss, source_str, grp->igmp_version, + uptime); + } + return; + } + + vty_out(vty, "%-16s %-15s %4s %8s %4d %d %8s\n", ifp->name, + group_str, + grp->igmp_version == 3 + ? (grp->group_filtermode_isexcl ? "EXCL" + : "INCL") + : "----", + hhmmss, + grp->group_source_list + ? listcount(grp->group_source_list) + : 0, + grp->igmp_version, uptime); + } +} + +static void igmp_show_groups_interface_single(struct pim_instance *pim, + struct vty *vty, bool uj, + const char *ifname, + const char *grp_str, bool detail) +{ + struct interface *ifp; + time_t now; + json_object *json = NULL; + struct pim_interface *pim_ifp = NULL; + struct gm_group *grp; now = pim_time_monotonic_sec(); if (uj) { json = json_object_new_object(); + if (!json) + return; json_object_int_add(json, "totalGroups", pim->gm_group_count); json_object_int_add(json, "watermarkLimit", pim->gm_watermark_limit); @@ -843,8 +1000,87 @@ static void igmp_show_groups(struct pim_instance *pim, struct vty *vty, bool uj) vty_out(vty, "Watermark warn limit(%s): %u\n", pim->gm_watermark_limit ? "Set" : "Not Set", pim->gm_watermark_limit); - vty_out(vty, - "Interface Group Mode Timer Srcs V Uptime \n"); + + if (!detail) + vty_out(vty, + "Interface Group Mode Timer Srcs V Uptime\n"); + else + vty_out(vty, + "Interface Group Mode Timer Source V Uptime\n"); + } + + ifp = if_lookup_by_name(ifname, pim->vrf->vrf_id); + if (!ifp) { + if (uj) + vty_json(vty, json); + return; + } + + pim_ifp = ifp->info; + if (!pim_ifp) { + if (uj) + vty_json(vty, json); + return; + } + + if (grp_str) { + struct in_addr group_addr; + struct gm_sock *igmp; + + if (inet_pton(AF_INET, grp_str, &group_addr) == 1) { + igmp = pim_igmp_sock_lookup_ifaddr( + pim_ifp->gm_socket_list, + pim_ifp->primary_address); + if (igmp) { + grp = find_group_by_addr(igmp, group_addr); + if (grp) + igmp_group_print(ifp, vty, uj, json, + grp, now, detail); + } + } + } else { + struct listnode *grpnode; + + /* scan igmp groups */ + for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_group_list, grpnode, grp)) + igmp_group_print(ifp, vty, uj, json, grp, now, detail); + } + + if (uj) { + if (detail) + vty_json_no_pretty(vty, json); + else + vty_json(vty, json); + } +} + +static void igmp_show_groups(struct pim_instance *pim, struct vty *vty, bool uj, + const char *grp_str, bool detail) +{ + struct interface *ifp; + time_t now; + json_object *json = NULL; + + now = pim_time_monotonic_sec(); + + if (uj) { + json = json_object_new_object(); + if (!json) + return; + json_object_int_add(json, "totalGroups", pim->gm_group_count); + json_object_int_add(json, "watermarkLimit", + pim->gm_watermark_limit); + } else { + vty_out(vty, "Total IGMP groups: %u\n", pim->gm_group_count); + vty_out(vty, "Watermark warn limit(%s): %u\n", + pim->gm_watermark_limit ? "Set" : "Not Set", + pim->gm_watermark_limit); + if (!detail) + vty_out(vty, + "Interface Group Mode Timer Srcs V Uptime\n"); + else + vty_out(vty, + "Interface Group Mode Timer Source V Uptime\n"); } /* scan interfaces */ @@ -856,78 +1092,38 @@ static void igmp_show_groups(struct pim_instance *pim, struct vty *vty, bool uj) if (!pim_ifp) continue; - /* scan igmp groups */ - for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_group_list, grpnode, - grp)) { - char group_str[INET_ADDRSTRLEN]; - char hhmmss[10]; - char uptime[10]; - - pim_inet4_dump("<group?>", grp->group_addr, group_str, - sizeof(group_str)); - pim_time_timer_to_hhmmss(hhmmss, sizeof(hhmmss), - grp->t_group_timer); - pim_time_uptime(uptime, sizeof(uptime), - now - grp->group_creation); - - if (uj) { - json_object_object_get_ex(json, ifp->name, - &json_iface); - - if (!json_iface) { - json_iface = json_object_new_object(); - json_object_pim_ifp_add(json_iface, - ifp); - json_object_object_add(json, ifp->name, - json_iface); - json_groups = json_object_new_array(); - json_object_object_add(json_iface, - "groups", - json_groups); + if (grp_str) { + struct in_addr group_addr; + struct gm_sock *igmp; + + if (inet_pton(AF_INET, grp_str, &group_addr) == 1) { + igmp = pim_igmp_sock_lookup_ifaddr( + pim_ifp->gm_socket_list, + pim_ifp->primary_address); + if (igmp) { + grp = find_group_by_addr(igmp, + group_addr); + if (grp) + igmp_group_print(ifp, vty, uj, + json, grp, now, + detail); } - - json_group = json_object_new_object(); - json_object_string_add(json_group, "group", - group_str); - - if (grp->igmp_version == 3) - json_object_string_add( - json_group, "mode", - grp->group_filtermode_isexcl - ? "EXCLUDE" - : "INCLUDE"); - - json_object_string_add(json_group, "timer", - hhmmss); - json_object_int_add( - json_group, "sourcesCount", - grp->group_source_list ? listcount( - grp->group_source_list) - : 0); - json_object_int_add(json_group, "version", - grp->igmp_version); - json_object_string_add(json_group, "uptime", - uptime); - json_object_array_add(json_groups, json_group); - } else { - vty_out(vty, "%-16s %-15s %4s %8s %4d %d %8s\n", - ifp->name, group_str, - grp->igmp_version == 3 - ? (grp->group_filtermode_isexcl - ? "EXCL" - : "INCL") - : "----", - hhmmss, - grp->group_source_list ? listcount( - grp->group_source_list) - : 0, - grp->igmp_version, uptime); } - } /* scan igmp groups */ - } /* scan interfaces */ + } else { + /* scan igmp groups */ + for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_group_list, + grpnode, grp)) + igmp_group_print(ifp, vty, uj, json, grp, now, + detail); + } + } /* scan interfaces */ - if (uj) - vty_json(vty, json); + if (uj) { + if (detail) + vty_json_no_pretty(vty, json); + else + vty_json(vty, json); + } } static void igmp_show_group_retransmission(struct pim_instance *pim, @@ -981,24 +1177,175 @@ static void igmp_show_group_retransmission(struct pim_instance *pim, } /* scan interfaces */ } +static void igmp_sources_print(struct interface *ifp, char *group_str, + struct gm_source *src, time_t now, + json_object *json, struct vty *vty, bool uj) +{ + json_object *json_iface = NULL; + json_object *json_group = NULL; + json_object *json_sources = NULL; + char source_str[INET_ADDRSTRLEN]; + char mmss[PIM_TIME_STRLEN]; + char uptime[PIM_TIME_STRLEN]; + + pim_inet4_dump("<source?>", src->source_addr, source_str, + sizeof(source_str)); + pim_time_timer_to_mmss(mmss, sizeof(mmss), src->t_source_timer); + pim_time_uptime(uptime, sizeof(uptime), now - src->source_creation); + + if (uj) { + json_object_object_get_ex(json, ifp->name, &json_iface); + if (!json_iface) { + json_iface = json_object_new_object(); + if (!json_iface) + return; + json_object_string_add(json_iface, "name", ifp->name); + json_object_object_add(json, ifp->name, json_iface); + } + + json_object_object_get_ex(json_iface, group_str, &json_group); + if (!json_group) { + json_group = json_object_new_object(); + if (!json_group) + return; + json_object_string_add(json_group, "group", group_str); + json_object_object_add(json_iface, group_str, + json_group); + json_sources = json_object_new_array(); + if (!json_sources) + return; + json_object_object_add(json_group, "sources", + json_sources); + } + + json_object_object_get_ex(json_group, "sources", &json_sources); + if (json_sources) + igmp_source_json_helper(src, json_sources, source_str, + mmss, uptime); + } else { + vty_out(vty, "%-16s %-15s %-15s %5s %3s %8s\n", ifp->name, + group_str, source_str, mmss, + IGMP_SOURCE_TEST_FORWARDING(src->source_flags) ? "Y" + : "N", + uptime); + } +} + +static void igmp_show_sources_interface_single(struct pim_instance *pim, + struct vty *vty, bool uj, + const char *ifname, + const char *grp_str) +{ + struct interface *ifp; + time_t now; + json_object *json = NULL; + struct pim_interface *pim_ifp; + struct gm_group *grp; + + now = pim_time_monotonic_sec(); + + if (uj) { + json = json_object_new_object(); + if (!json) + return; + } else { + vty_out(vty, + "Interface Group Source Timer Fwd Uptime \n"); + } + + ifp = if_lookup_by_name(ifname, pim->vrf->vrf_id); + if (!ifp) { + if (uj) + vty_json(vty, json); + return; + } + + pim_ifp = ifp->info; + if (!pim_ifp) { + if (uj) + vty_json(vty, json); + return; + } + + if (grp_str) { + struct in_addr group_addr; + struct gm_sock *igmp; + struct listnode *srcnode; + struct gm_source *src; + char group_str[INET_ADDRSTRLEN]; + int res; + + res = inet_pton(AF_INET, grp_str, &group_addr); + if (res <= 0) { + if (uj) + vty_json(vty, json); + return; + } + + igmp = pim_igmp_sock_lookup_ifaddr(pim_ifp->gm_socket_list, + pim_ifp->primary_address); + if (!igmp) { + if (uj) + vty_json(vty, json); + return; + } + + grp = find_group_by_addr(igmp, group_addr); + if (!grp) { + if (uj) + vty_json(vty, json); + return; + } + pim_inet4_dump("<group?>", grp->group_addr, group_str, + sizeof(group_str)); + + /* scan group sources */ + for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, srcnode, src)) + igmp_sources_print(ifp, group_str, src, now, json, vty, + uj); + } else { + struct listnode *grpnode; + + /* scan igmp groups */ + for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_group_list, grpnode, + grp)) { + char group_str[INET_ADDRSTRLEN]; + struct listnode *srcnode; + struct gm_source *src; + + pim_inet4_dump("<group?>", grp->group_addr, group_str, + sizeof(group_str)); + + /* scan group sources */ + for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, + srcnode, src)) + igmp_sources_print(ifp, group_str, src, now, + json, vty, uj); + + } /* scan igmp groups */ + } + + if (uj) + vty_json(vty, json); +} + static void igmp_show_sources(struct pim_instance *pim, struct vty *vty, bool uj) { struct interface *ifp; time_t now; json_object *json = NULL; - json_object *json_iface = NULL; - json_object *json_group = NULL; - json_object *json_source = NULL; - json_object *json_sources = NULL; now = pim_time_monotonic_sec(); - if (uj) + if (uj) { json = json_object_new_object(); - else + if (!json) + return; + } else { vty_out(vty, - "Interface Group Source Timer Fwd Uptime \n"); + "Interface Group Source Timer Fwd Uptime\n"); + } /* scan interfaces */ FOR_ALL_INTERFACES (pim->vrf, ifp) { @@ -1021,82 +1368,12 @@ static void igmp_show_sources(struct pim_instance *pim, struct vty *vty, /* scan group sources */ for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, - srcnode, src)) { - char source_str[INET_ADDRSTRLEN]; - char mmss[10]; - char uptime[10]; - - pim_inet4_dump("<source?>", src->source_addr, - source_str, sizeof(source_str)); - - pim_time_timer_to_mmss(mmss, sizeof(mmss), - src->t_source_timer); - - pim_time_uptime(uptime, sizeof(uptime), - now - src->source_creation); - - if (uj) { - json_object_object_get_ex( - json, ifp->name, &json_iface); - if (!json_iface) { - json_iface = - json_object_new_object(); - json_object_string_add( - json_iface, "name", - ifp->name); - json_object_object_add( - json, ifp->name, - json_iface); - } - json_object_object_get_ex(json_iface, - group_str, - &json_group); - - if (!json_group) { - json_group = - json_object_new_object(); - json_object_string_add( - json_group, "group", - group_str); - json_object_object_add( - json_iface, group_str, - json_group); - json_sources = - json_object_new_array(); - json_object_object_add( - json_group, "sources", - json_sources); - } - json_source = json_object_new_object(); - json_object_string_add(json_source, - "source", - source_str); - json_object_string_add(json_source, - "timer", mmss); - json_object_boolean_add( - json_source, "forwarded", - IGMP_SOURCE_TEST_FORWARDING( - src->source_flags)); - json_object_string_add( - json_source, "uptime", uptime); - json_object_array_add(json_sources, - json_source); - - } else { - vty_out(vty, - "%-16s %-15s %-15s %5s %3s %8s\n", - ifp->name, group_str, - source_str, mmss, - IGMP_SOURCE_TEST_FORWARDING( - src->source_flags) - ? "Y" - : "N", - uptime); - } - - } /* scan group sources */ + srcnode, src)) + igmp_sources_print(ifp, group_str, src, now, + json, vty, uj); } /* scan igmp groups */ } /* scan interfaces */ + if (uj) vty_json(vty, json); } @@ -1227,7 +1504,7 @@ DEFUN (clear_ip_interfaces, VRF_CMD_HELP_STR) { int idx = 2; - struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, false); if (!vrf) return CMD_WARNING; @@ -1247,7 +1524,7 @@ DEFUN (clear_ip_igmp_interfaces, "Reset IGMP interfaces\n") { int idx = 2; - struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, false); if (!vrf) return CMD_WARNING; @@ -1355,7 +1632,7 @@ DEFUN (clear_ip_pim_bsr_db, "Reset pim bsr data\n") { int idx = 2; - struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, false); if (!vrf) return CMD_WARNING; @@ -1378,8 +1655,8 @@ DEFUN (show_ip_igmp_interface, JSON_STR) { int idx = 2; - struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); bool uj = use_json(argc, argv); + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, uj); if (!vrf) return CMD_WARNING; @@ -1444,8 +1721,8 @@ DEFUN (show_ip_igmp_join, JSON_STR) { int idx = 2; - struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); bool uj = use_json(argc, argv); + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, uj); if (!vrf) return CMD_WARNING; @@ -1487,39 +1764,47 @@ DEFUN (show_ip_igmp_join_vrf_all, return CMD_SUCCESS; } -DEFUN (show_ip_igmp_groups, - show_ip_igmp_groups_cmd, - "show ip igmp [vrf NAME] groups [json]", - SHOW_STR - IP_STR - IGMP_STR - VRF_CMD_HELP_STR - IGMP_GROUP_STR - JSON_STR) +DEFPY(show_ip_igmp_groups, + show_ip_igmp_groups_cmd, + "show ip igmp [vrf NAME$vrf_name] groups [INTERFACE$ifname [GROUP$grp_str]] [detail$detail] [json$json]", + SHOW_STR + IP_STR + IGMP_STR + VRF_CMD_HELP_STR + IGMP_GROUP_STR + "Interface name\n" + "Group address\n" + "Detailed Information\n" + JSON_STR) { int idx = 2; - struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); - bool uj = use_json(argc, argv); + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, !!json); if (!vrf) return CMD_WARNING; - igmp_show_groups(vrf->info, vty, uj); + if (ifname) + igmp_show_groups_interface_single(vrf->info, vty, !!json, + ifname, grp_str, !!detail); + else + igmp_show_groups(vrf->info, vty, !!json, NULL, !!detail); return CMD_SUCCESS; } -DEFUN (show_ip_igmp_groups_vrf_all, - show_ip_igmp_groups_vrf_all_cmd, - "show ip igmp vrf all groups [json]", - SHOW_STR - IP_STR - IGMP_STR - VRF_CMD_HELP_STR - IGMP_GROUP_STR - JSON_STR) +DEFPY(show_ip_igmp_groups_vrf_all, + show_ip_igmp_groups_vrf_all_cmd, + "show ip igmp vrf all groups [GROUP$grp_str] [detail$detail] [json$json]", + SHOW_STR + IP_STR + IGMP_STR + VRF_CMD_HELP_STR + IGMP_GROUP_STR + "Group address\n" + "Detailed Information\n" + JSON_STR) { - bool uj = use_json(argc, argv); + bool uj = !!json; struct vrf *vrf; bool first = true; @@ -1533,7 +1818,7 @@ DEFUN (show_ip_igmp_groups_vrf_all, first = false; } else vty_out(vty, "VRF: %s\n", vrf->name); - igmp_show_groups(vrf->info, vty, uj); + igmp_show_groups(vrf->info, vty, uj, grp_str, !!detail); } if (uj) vty_out(vty, "}\n"); @@ -1552,7 +1837,7 @@ DEFUN (show_ip_igmp_groups_retransmissions, "IGMP group retransmissions\n") { int idx = 2; - struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, false); if (!vrf) return CMD_WARNING; @@ -1562,23 +1847,29 @@ DEFUN (show_ip_igmp_groups_retransmissions, return CMD_SUCCESS; } -DEFUN (show_ip_igmp_sources, - show_ip_igmp_sources_cmd, - "show ip igmp [vrf NAME] sources [json]", - SHOW_STR - IP_STR - IGMP_STR - VRF_CMD_HELP_STR - IGMP_SOURCE_STR - JSON_STR) +DEFPY(show_ip_igmp_sources, + show_ip_igmp_sources_cmd, + "show ip igmp [vrf NAME$vrf_name] sources [INTERFACE$ifname [GROUP$grp_str]] [json$json]", + SHOW_STR + IP_STR + IGMP_STR + VRF_CMD_HELP_STR + IGMP_SOURCE_STR + "Interface name\n" + "Group address\n" + JSON_STR) { int idx = 2; - struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, !!json); if (!vrf) return CMD_WARNING; - igmp_show_sources(vrf->info, vty, use_json(argc, argv)); + if (ifname) + igmp_show_sources_interface_single(vrf->info, vty, !!json, + ifname, grp_str); + else + igmp_show_sources(vrf->info, vty, !!json); return CMD_SUCCESS; } @@ -1594,7 +1885,7 @@ DEFUN (show_ip_igmp_sources_retransmissions, "IGMP source retransmissions\n") { int idx = 2; - struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, false); if (!vrf) return CMD_WARNING; @@ -1617,8 +1908,8 @@ DEFUN (show_ip_igmp_statistics, JSON_STR) { int idx = 2; - struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); bool uj = use_json(argc, argv); + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, uj); if (!vrf) return CMD_WARNING; @@ -1746,7 +2037,7 @@ DEFUN (show_ip_pim_assert, "PIM interface assert\n") { int idx = 2; - struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, false); if (!vrf) return CMD_WARNING; @@ -1766,7 +2057,7 @@ DEFUN (show_ip_pim_assert_internal, "PIM interface internal assert state\n") { int idx = 2; - struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, false); if (!vrf) return CMD_WARNING; @@ -1786,7 +2077,7 @@ DEFUN (show_ip_pim_assert_metric, "PIM interface assert metric\n") { int idx = 2; - struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, false); if (!vrf) return CMD_WARNING; @@ -1806,7 +2097,7 @@ DEFUN (show_ip_pim_assert_winner_metric, "PIM interface assert winner metric\n") { int idx = 2; - struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, false); if (!vrf) return CMD_WARNING; @@ -2125,8 +2416,8 @@ DEFUN(show_ip_pim_mlag_up, show_ip_pim_mlag_up_cmd, const char *src_or_group = NULL; const char *group = NULL; int idx = 2; - struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); bool uj = use_json(argc, argv); + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, uj); if (!vrf || !vrf->info) { vty_out(vty, "%s: VRF or Info missing\n", __func__); @@ -2598,7 +2889,7 @@ DEFUN (show_ip_rib, "Unicast address\n") { int idx = 2; - struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, false); struct in_addr addr; const char *addr_str; struct pim_nexthop nexthop; @@ -2686,7 +2977,7 @@ DEFUN (show_ip_ssmpingd, VRF_CMD_HELP_STR) { int idx = 2; - struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, false); if (!vrf) return CMD_WARNING; @@ -3141,8 +3432,8 @@ DEFUN (show_ip_pim_ssm_range, JSON_STR) { int idx = 2; - struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); bool uj = use_json(argc, argv); + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, uj); if (!vrf) return CMD_WARNING; @@ -3192,8 +3483,8 @@ DEFUN (show_ip_pim_group_type, JSON_STR) { int idx = 2; - struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); bool uj = use_json(argc, argv); + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, uj); if (!vrf) return CMD_WARNING; @@ -5086,7 +5377,7 @@ 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 vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, uj); struct pim_instance *pim; struct json_object *json = NULL; @@ -5322,7 +5613,7 @@ DEFUN (show_ip_msdp_peer_detail, { bool uj = use_json(argc, argv); int idx = 2; - struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, uj); if (!vrf) return CMD_WARNING; @@ -5562,7 +5853,7 @@ DEFUN (show_ip_msdp_sa_detail, { bool uj = use_json(argc, argv); int idx = 2; - struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, uj); if (!vrf) return CMD_WARNING; @@ -5677,7 +5968,7 @@ DEFUN (show_ip_msdp_sa_sg, struct vrf *vrf; int idx = 2; - vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, uj); if (!vrf) return CMD_WARNING; @@ -5947,7 +6238,7 @@ DEFUN (show_ip_pim_vxlan_sg, struct vrf *vrf; int idx = 2; - vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, uj); if (!vrf) return CMD_WARNING; @@ -6007,7 +6298,7 @@ DEFUN_HIDDEN (show_ip_pim_vxlan_sg_work, struct vrf *vrf; int idx = 2; - vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, uj); if (!vrf) return CMD_WARNING; diff --git a/pimd/pim_cmd.h b/pimd/pim_cmd.h index fb6693491b..d39d77cd2f 100644 --- a/pimd/pim_cmd.h +++ b/pimd/pim_cmd.h @@ -60,4 +60,5 @@ void pim_cmd_init(void); +#define PIM_TIME_STRLEN 10 #endif /* PIM_CMD_H */ diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c index ead02d8bd7..b053e422ec 100644 --- a/pimd/pim_iface.c +++ b/pimd/pim_iface.c @@ -42,10 +42,10 @@ #if PIM_IPV == 4 static void pim_if_igmp_join_del_all(struct interface *ifp); -static int igmp_join_sock(const char *ifname, ifindex_t ifindex, - struct in_addr group_addr, struct in_addr source_addr, - struct pim_interface *pim_ifp); #endif +static int gm_join_sock(const char *ifname, ifindex_t ifindex, + pim_addr group_addr, pim_addr source_addr, + struct pim_interface *pim_ifp); void pim_if_init(struct pim_instance *pim) { @@ -560,7 +560,7 @@ void pim_if_addr_add(struct connected *ifc) /* Close socket and reopen with Source and Group */ close(ij->sock_fd); - join_fd = igmp_join_sock( + join_fd = gm_join_sock( ifp->name, ifp->ifindex, ij->group_addr, ij->source_addr, pim_ifp); if (join_fd < 0) { @@ -573,7 +573,7 @@ void pim_if_addr_add(struct connected *ifc) "<src?>", ij->source_addr, source_str, sizeof(source_str)); zlog_warn( - "%s: igmp_join_sock() failure for IGMP group %s source %s on interface %s", + "%s: gm_join_sock() failure for IGMP group %s source %s on interface %s", __func__, group_str, source_str, ifp->name); /* warning only */ @@ -1214,15 +1214,13 @@ long pim_if_t_suppressed_msec(struct interface *ifp) return t_suppressed_msec; } -#if PIM_IPV == 4 -static void igmp_join_free(struct gm_join *ij) +static void gm_join_free(struct gm_join *ij) { XFREE(MTYPE_PIM_IGMP_JOIN, ij); } -static struct gm_join *igmp_join_find(struct list *join_list, - struct in_addr group_addr, - struct in_addr source_addr) +static struct gm_join *gm_join_find(struct list *join_list, pim_addr group_addr, + pim_addr source_addr) { struct listnode *node; struct gm_join *ij; @@ -1230,38 +1228,33 @@ static struct gm_join *igmp_join_find(struct list *join_list, assert(join_list); for (ALL_LIST_ELEMENTS_RO(join_list, node, ij)) { - if ((group_addr.s_addr == ij->group_addr.s_addr) - && (source_addr.s_addr == ij->source_addr.s_addr)) + if ((!pim_addr_cmp(group_addr, ij->group_addr)) && + (!pim_addr_cmp(source_addr, ij->source_addr))) return ij; } return 0; } -static int igmp_join_sock(const char *ifname, ifindex_t ifindex, - struct in_addr group_addr, struct in_addr source_addr, - struct pim_interface *pim_ifp) +static int gm_join_sock(const char *ifname, ifindex_t ifindex, + pim_addr group_addr, pim_addr source_addr, + struct pim_interface *pim_ifp) { int join_fd; pim_ifp->igmp_ifstat_joins_sent++; - join_fd = pim_socket_raw(IPPROTO_IGMP); + join_fd = pim_socket_raw(IPPROTO_GM); if (join_fd < 0) { pim_ifp->igmp_ifstat_joins_failed++; return -1; } - if (pim_igmp_join_source(join_fd, ifindex, group_addr, source_addr)) { - char group_str[INET_ADDRSTRLEN]; - char source_str[INET_ADDRSTRLEN]; - pim_inet4_dump("<grp?>", group_addr, group_str, - sizeof(group_str)); - pim_inet4_dump("<src?>", source_addr, source_str, - sizeof(source_str)); + if (pim_gm_join_source(join_fd, ifindex, group_addr, source_addr)) { zlog_warn( - "%s: setsockopt(fd=%d) failure for IGMP group %s source %s ifindex %d on interface %s: errno=%d: %s", - __func__, join_fd, group_str, source_str, ifindex, + "%s: setsockopt(fd=%d) failure for " GM + " group %pPAs source %pPAs ifindex %d on interface %s: errno=%d: %s", + __func__, join_fd, &group_addr, &source_addr, ifindex, ifname, errno, safe_strerror(errno)); pim_ifp->igmp_ifstat_joins_failed++; @@ -1273,10 +1266,8 @@ static int igmp_join_sock(const char *ifname, ifindex_t ifindex, return join_fd; } -#if PIM_IPV == 4 -static struct gm_join *igmp_join_new(struct interface *ifp, - struct in_addr group_addr, - struct in_addr source_addr) +static struct gm_join *gm_join_new(struct interface *ifp, pim_addr group_addr, + pim_addr source_addr) { struct pim_interface *pim_ifp; struct gm_join *ij; @@ -1285,19 +1276,12 @@ static struct gm_join *igmp_join_new(struct interface *ifp, pim_ifp = ifp->info; assert(pim_ifp); - join_fd = igmp_join_sock(ifp->name, ifp->ifindex, group_addr, - source_addr, pim_ifp); + join_fd = gm_join_sock(ifp->name, ifp->ifindex, group_addr, source_addr, + pim_ifp); if (join_fd < 0) { - char group_str[INET_ADDRSTRLEN]; - char source_str[INET_ADDRSTRLEN]; - - pim_inet4_dump("<grp?>", group_addr, group_str, - sizeof(group_str)); - pim_inet4_dump("<src?>", source_addr, source_str, - sizeof(source_str)); - zlog_warn( - "%s: igmp_join_sock() failure for IGMP group %s source %s on interface %s", - __func__, group_str, source_str, ifp->name); + zlog_warn("%s: gm_join_sock() failure for " GM + " group %pPAs source %pPAs on interface %s", + __func__, &group_addr, &source_addr, ifp->name); return 0; } @@ -1312,11 +1296,9 @@ static struct gm_join *igmp_join_new(struct interface *ifp, return ij; } -#endif /* PIM_IPV == 4 */ -#if PIM_IPV == 4 -ferr_r pim_if_igmp_join_add(struct interface *ifp, struct in_addr group_addr, - struct in_addr source_addr) +ferr_r pim_if_gm_join_add(struct interface *ifp, pim_addr group_addr, + pim_addr source_addr) { struct pim_interface *pim_ifp; struct gm_join *ij; @@ -1329,37 +1311,32 @@ ferr_r pim_if_igmp_join_add(struct interface *ifp, struct in_addr group_addr, if (!pim_ifp->gm_join_list) { pim_ifp->gm_join_list = list_new(); - pim_ifp->gm_join_list->del = (void (*)(void *))igmp_join_free; + pim_ifp->gm_join_list->del = (void (*)(void *))gm_join_free; } - ij = igmp_join_find(pim_ifp->gm_join_list, group_addr, source_addr); + ij = gm_join_find(pim_ifp->gm_join_list, group_addr, source_addr); - /* This interface has already been configured to join this IGMP group + /* This interface has already been configured to join this IGMP/MLD + * group */ if (ij) { return ferr_ok(); } - (void)igmp_join_new(ifp, group_addr, source_addr); + (void)gm_join_new(ifp, group_addr, source_addr); if (PIM_DEBUG_GM_EVENTS) { - char group_str[INET_ADDRSTRLEN]; - char source_str[INET_ADDRSTRLEN]; - pim_inet4_dump("<grp?>", group_addr, group_str, - sizeof(group_str)); - pim_inet4_dump("<src?>", source_addr, source_str, - sizeof(source_str)); zlog_debug( - "%s: issued static igmp join for channel (S,G)=(%s,%s) on interface %s", - __func__, source_str, group_str, ifp->name); + "%s: issued static " GM + " join for channel (S,G)=(%pPA,%pPA) on interface %s", + __func__, &source_addr, &group_addr, ifp->name); } return ferr_ok(); } -#endif /* PIM_IPV == 4 */ -int pim_if_igmp_join_del(struct interface *ifp, struct in_addr group_addr, - struct in_addr source_addr) +int pim_if_gm_join_del(struct interface *ifp, pim_addr group_addr, + pim_addr source_addr) { struct pim_interface *pim_ifp; struct gm_join *ij; @@ -1372,40 +1349,29 @@ int pim_if_igmp_join_del(struct interface *ifp, struct in_addr group_addr, } if (!pim_ifp->gm_join_list) { - zlog_warn("%s: no IGMP join on interface %s", __func__, + zlog_warn("%s: no " GM " join on interface %s", __func__, ifp->name); return -2; } - ij = igmp_join_find(pim_ifp->gm_join_list, group_addr, source_addr); + ij = gm_join_find(pim_ifp->gm_join_list, group_addr, source_addr); if (!ij) { - char group_str[INET_ADDRSTRLEN]; - char source_str[INET_ADDRSTRLEN]; - pim_inet4_dump("<grp?>", group_addr, group_str, - sizeof(group_str)); - pim_inet4_dump("<src?>", source_addr, source_str, - sizeof(source_str)); - zlog_warn( - "%s: could not find IGMP group %s source %s on interface %s", - __func__, group_str, source_str, ifp->name); + zlog_warn("%s: could not find " GM + " group %pPAs source %pPAs on interface %s", + __func__, &group_addr, &source_addr, ifp->name); return -3; } if (close(ij->sock_fd)) { - char group_str[INET_ADDRSTRLEN]; - char source_str[INET_ADDRSTRLEN]; - pim_inet4_dump("<grp?>", group_addr, group_str, - sizeof(group_str)); - pim_inet4_dump("<src?>", source_addr, source_str, - sizeof(source_str)); zlog_warn( - "%s: failure closing sock_fd=%d for IGMP group %s source %s on interface %s: errno=%d: %s", - __func__, ij->sock_fd, group_str, source_str, ifp->name, - errno, safe_strerror(errno)); + "%s: failure closing sock_fd=%d for " GM + " group %pPAs source %pPAs on interface %s: errno=%d: %s", + __func__, ij->sock_fd, &group_addr, &source_addr, + ifp->name, errno, safe_strerror(errno)); /* warning only */ } listnode_delete(pim_ifp->gm_join_list, ij); - igmp_join_free(ij); + gm_join_free(ij); if (listcount(pim_ifp->gm_join_list) < 1) { list_delete(&pim_ifp->gm_join_list); pim_ifp->gm_join_list = 0; @@ -1414,6 +1380,7 @@ int pim_if_igmp_join_del(struct interface *ifp, struct in_addr group_addr, return 0; } +#if PIM_IPV == 4 __attribute__((unused)) static void pim_if_igmp_join_del_all(struct interface *ifp) { @@ -1433,21 +1400,9 @@ static void pim_if_igmp_join_del_all(struct interface *ifp) return; for (ALL_LIST_ELEMENTS(pim_ifp->gm_join_list, node, nextnode, ij)) - pim_if_igmp_join_del(ifp, ij->group_addr, ij->source_addr); + pim_if_gm_join_del(ifp, ij->group_addr, ij->source_addr); } -#else /* PIM_IPV != 4 */ -ferr_r pim_if_igmp_join_add(struct interface *ifp, struct in_addr group_addr, - struct in_addr source_addr) -{ - return ferr_ok(); -} - -int pim_if_igmp_join_del(struct interface *ifp, struct in_addr group_addr, - struct in_addr source_addr) -{ - return 0; -} -#endif /* PIM_IPV != 4 */ +#endif /* PIM_IPV == 4 */ /* RFC 4601 diff --git a/pimd/pim_iface.h b/pimd/pim_iface.h index 0aff56d558..973840a753 100644 --- a/pimd/pim_iface.h +++ b/pimd/pim_iface.h @@ -217,10 +217,10 @@ int pim_if_t_override_msec(struct interface *ifp); pim_addr pim_find_primary_addr(struct interface *ifp); -ferr_r pim_if_igmp_join_add(struct interface *ifp, struct in_addr group_addr, - struct in_addr source_addr); -int pim_if_igmp_join_del(struct interface *ifp, struct in_addr group_addr, - struct in_addr source_addr); +ferr_r pim_if_gm_join_add(struct interface *ifp, pim_addr group_addr, + pim_addr source_addr); +int pim_if_gm_join_del(struct interface *ifp, pim_addr group_addr, + pim_addr source_addr); void pim_if_update_could_assert(struct interface *ifp); diff --git a/pimd/pim_igmp_join.h b/pimd/pim_igmp_join.h index d6d22be3e4..0e9498c50c 100644 --- a/pimd/pim_igmp_join.h +++ b/pimd/pim_igmp_join.h @@ -7,6 +7,8 @@ #ifndef PIM_IGMP_JOIN_H #define PIM_IGMP_JOIN_H +#include "pim_addr.h" + /* required headers #include'd by caller */ #ifndef SOL_IP @@ -26,35 +28,64 @@ struct group_source_req { }; #endif -static inline int pim_igmp_join_source(int fd, ifindex_t ifindex, - struct in_addr group_addr, - struct in_addr source_addr) +#if PIM_IPV == 4 +static inline int pim_gm_join_source(int fd, ifindex_t ifindex, + pim_addr group_addr, pim_addr source_addr) { struct group_source_req req; - struct sockaddr_in group; - struct sockaddr_in source; + struct sockaddr_in group = {}; + struct sockaddr_in source = {}; memset(&req, 0, sizeof(req)); - memset(&group, 0, sizeof(group)); - group.sin_family = AF_INET; + + group.sin_family = PIM_AF; group.sin_addr = group_addr; group.sin_port = htons(0); - memcpy(&req.gsr_group, &group, sizeof(struct sockaddr_in)); + memcpy(&req.gsr_group, &group, sizeof(group)); - memset(&source, 0, sizeof(source)); - source.sin_family = AF_INET; + source.sin_family = PIM_AF; source.sin_addr = source_addr; source.sin_port = htons(0); - memcpy(&req.gsr_source, &source, sizeof(struct sockaddr_in)); + memcpy(&req.gsr_source, &source, sizeof(source)); req.gsr_interface = ifindex; - if (source_addr.s_addr == INADDR_ANY) + if (pim_addr_is_any(source_addr)) return setsockopt(fd, SOL_IP, MCAST_JOIN_GROUP, &req, sizeof(req)); else return setsockopt(fd, SOL_IP, MCAST_JOIN_SOURCE_GROUP, &req, sizeof(req)); } +#else /* PIM_IPV != 4*/ +static inline int pim_gm_join_source(int fd, ifindex_t ifindex, + pim_addr group_addr, pim_addr source_addr) +{ + struct group_source_req req; + struct sockaddr_in6 group = {}; + struct sockaddr_in6 source = {}; + + memset(&req, 0, sizeof(req)); + + group.sin6_family = PIM_AF; + group.sin6_addr = group_addr; + group.sin6_port = htons(0); + memcpy(&req.gsr_group, &group, sizeof(group)); + + source.sin6_family = PIM_AF; + source.sin6_addr = source_addr; + source.sin6_port = htons(0); + memcpy(&req.gsr_source, &source, sizeof(source)); + + req.gsr_interface = ifindex; + + if (pim_addr_is_any(source_addr)) + return setsockopt(fd, SOL_IPV6, MCAST_JOIN_GROUP, &req, + sizeof(req)); + else + return setsockopt(fd, SOL_IPV6, MCAST_JOIN_SOURCE_GROUP, &req, + sizeof(req)); +} +#endif /* PIM_IPV != 4*/ #endif /* PIM_IGMP_JOIN_H */ diff --git a/pimd/pim_nb_config.c b/pimd/pim_nb_config.c index fa6f664149..3c4ab1d4cc 100644 --- a/pimd/pim_nb_config.c +++ b/pimd/pim_nb_config.c @@ -71,12 +71,19 @@ static void pim_if_membership_clear(struct interface *ifp) static void pim_if_membership_refresh(struct interface *ifp) { struct pim_interface *pim_ifp; +#if PIM_IPV == 4 struct listnode *grpnode; struct gm_group *grp; - +#else + struct gm_if *gm_ifp; + struct gm_sg *sg, *sg_start; +#endif pim_ifp = ifp->info; assert(pim_ifp); +#if PIM_IPV == 6 + gm_ifp = pim_ifp->mld; +#endif if (!pim_ifp->pim_enable) return; @@ -90,6 +97,7 @@ static void pim_if_membership_refresh(struct interface *ifp) pim_ifchannel_membership_clear(ifp); +#if PIM_IPV == 4 /* * Then restore PIM (S,G) membership from all IGMPv3 (S,G) entries on * the interface @@ -116,6 +124,16 @@ static void pim_if_membership_refresh(struct interface *ifp) } /* scan group sources */ } /* scan igmp groups */ +#else + sg_start = gm_sgs_first(gm_ifp->sgs); + + frr_each_from (gm_sgs, gm_ifp->sgs, sg, sg_start) { + if (!in6_multicast_nofwd(&sg->sgaddr.grp)) { + pim_ifchannel_local_membership_add( + ifp, &sg->sgaddr, false /*is_vxlan*/); + } + } +#endif /* * Finally delete every PIM (S,G) entry lacking all state info @@ -2880,10 +2898,9 @@ int lib_interface_gmp_address_family_robustness_variable_modify( int lib_interface_gmp_address_family_static_group_create( struct nb_cb_create_args *args) { -#if PIM_IPV == 4 struct interface *ifp; - struct ipaddr source_addr; - struct ipaddr group_addr; + pim_addr source_addr; + pim_addr group_addr; int result; const char *ifp_name; const struct lyd_node *if_dnode; @@ -2899,33 +2916,40 @@ int lib_interface_gmp_address_family_static_group_create( return NB_ERR_VALIDATION; } - yang_dnode_get_ip(&group_addr, args->dnode, "./group-addr"); - if (pim_is_group_224_0_0_0_24(group_addr.ip._v4_addr)) { + yang_dnode_get_pimaddr(&group_addr, args->dnode, + "./group-addr"); +#if PIM_IPV == 4 + if (pim_is_group_224_0_0_0_24(group_addr)) { snprintf( args->errmsg, args->errmsg_len, "Groups within 224.0.0.0/24 are reserved and cannot be joined"); return NB_ERR_VALIDATION; } +#else + if (ipv6_mcast_reserved(&group_addr)) { + snprintf( + args->errmsg, args->errmsg_len, + "Groups within ffx2::/16 are reserved and cannot be joined"); + return NB_ERR_VALIDATION; + } +#endif break; case NB_EV_PREPARE: case NB_EV_ABORT: break; case NB_EV_APPLY: ifp = nb_running_get_entry(args->dnode, NULL, true); - yang_dnode_get_ip(&source_addr, args->dnode, "./source-addr"); - yang_dnode_get_ip(&group_addr, args->dnode, "./group-addr"); - - result = pim_if_igmp_join_add(ifp, group_addr.ip._v4_addr, - source_addr.ip._v4_addr); + yang_dnode_get_pimaddr(&source_addr, args->dnode, + "./source-addr"); + yang_dnode_get_pimaddr(&group_addr, args->dnode, + "./group-addr"); + result = pim_if_gm_join_add(ifp, group_addr, source_addr); if (result) { snprintf(args->errmsg, args->errmsg_len, - "Failure joining IGMP group"); + "Failure joining " GM " group"); return NB_ERR_INCONSISTENCY; } } -#else - /* TBD Depends on MLD data structure changes */ -#endif /* PIM_IPV == 4 */ return NB_OK; } @@ -2933,8 +2957,8 @@ int lib_interface_gmp_address_family_static_group_destroy( struct nb_cb_destroy_args *args) { struct interface *ifp; - struct ipaddr source_addr; - struct ipaddr group_addr; + pim_addr source_addr; + pim_addr group_addr; int result; switch (args->event) { @@ -2944,22 +2968,17 @@ int lib_interface_gmp_address_family_static_group_destroy( break; case NB_EV_APPLY: ifp = nb_running_get_entry(args->dnode, NULL, true); - yang_dnode_get_ip(&source_addr, args->dnode, "./source-addr"); - yang_dnode_get_ip(&group_addr, args->dnode, "./group-addr"); - - result = pim_if_igmp_join_del(ifp, group_addr.ip._v4_addr, - source_addr.ip._v4_addr); + yang_dnode_get_pimaddr(&source_addr, args->dnode, + "./source-addr"); + yang_dnode_get_pimaddr(&group_addr, args->dnode, + "./group-addr"); + result = pim_if_gm_join_del(ifp, group_addr, source_addr); if (result) { - char src_str[INET_ADDRSTRLEN]; - char grp_str[INET_ADDRSTRLEN]; - - ipaddr2str(&source_addr, src_str, sizeof(src_str)); - ipaddr2str(&group_addr, grp_str, sizeof(grp_str)); - snprintf(args->errmsg, args->errmsg_len, - "%% Failure leaving IGMP group %s %s on interface %s: %d", - src_str, grp_str, ifp->name, result); + "%% Failure leaving " GM + " group %pPAs %pPAs on interface %s: %d", + &source_addr, &group_addr, ifp->name, result); return NB_ERR_INCONSISTENCY; } diff --git a/pimd/pim_nht.c b/pimd/pim_nht.c index d164e7ed81..5f0f2a5933 100644 --- a/pimd/pim_nht.c +++ b/pimd/pim_nht.c @@ -724,27 +724,20 @@ int pim_parse_nexthop_update(ZAPI_CALLBACK_ARGS) return 0; } - if (cmd == ZEBRA_NEXTHOP_UPDATE) { - rpf.rpf_addr = pim_addr_from_prefix(&match); - pnc = pim_nexthop_cache_find(pim, &rpf); - if (!pnc) { - if (PIM_DEBUG_PIM_NHT) - zlog_debug( - "%s: Skipping NHT update, addr %pPA is not in local cached DB.", - __func__, &rpf.rpf_addr); - return 0; - } - } else { - /* - * We do not currently handle ZEBRA_IMPORT_CHECK_UPDATE - */ + rpf.rpf_addr = pim_addr_from_prefix(&match); + pnc = pim_nexthop_cache_find(pim, &rpf); + if (!pnc) { + if (PIM_DEBUG_PIM_NHT) + zlog_debug( + "%s: Skipping NHT update, addr %pPA is not in local cached DB.", + __func__, &rpf.rpf_addr); return 0; } pnc->last_update = pim_time_monotonic_usec(); if (nhr.nexthop_num) { - pnc->nexthop_num = 0; // Only increment for pim enabled rpf. + pnc->nexthop_num = 0; for (i = 0; i < nhr.nexthop_num; i++) { nexthop = nexthop_from_zapi_nexthop(&nhr.nexthops[i]); @@ -862,7 +855,8 @@ int pim_parse_nexthop_update(ZAPI_CALLBACK_ARGS) nhlist_tail = nexthop; nhlist_head = nexthop; } - // Only keep track of nexthops which are PIM enabled. + + // Keep track of all nexthops, even PIM-disabled ones. pnc->nexthop_num++; } /* Reset existing pnc->nexthop before assigning new list */ diff --git a/pimd/pim_vty.c b/pimd/pim_vty.c index 91628930f4..0f6547ee2e 100644 --- a/pimd/pim_vty.c +++ b/pimd/pim_vty.c @@ -335,18 +335,12 @@ static int gm_config_write(struct vty *vty, int writes, struct listnode *node; struct gm_join *ij; for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_join_list, node, ij)) { - char group_str[INET_ADDRSTRLEN]; - char source_str[INET_ADDRSTRLEN]; - pim_inet4_dump("<grp?>", ij->group_addr, group_str, - sizeof(group_str)); - if (ij->source_addr.s_addr == INADDR_ANY) { - vty_out(vty, " ip igmp join %s\n", group_str); - } else { - inet_ntop(AF_INET, &ij->source_addr, source_str, - sizeof(source_str)); - vty_out(vty, " ip igmp join %s %s\n", group_str, - source_str); - } + if (pim_addr_is_any(ij->source_addr)) + vty_out(vty, " ip igmp join %pPAs\n", + &ij->group_addr); + else + vty_out(vty, " ip igmp join %pPAs %pPAs\n", + &ij->group_addr, &ij->source_addr); ++writes; } } @@ -388,6 +382,21 @@ static int gm_config_write(struct vty *vty, int writes, vty_out(vty, " ipv6 mld last-member-query-interval %d\n", pim_ifp->gm_specific_query_max_response_time_dsec); + /* IF ipv6 mld join */ + if (pim_ifp->gm_join_list) { + struct listnode *node; + struct gm_join *ij; + for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_join_list, node, ij)) { + if (pim_addr_is_any(ij->source_addr)) + vty_out(vty, " ipv6 mld join %pPAs\n", + &ij->group_addr); + else + vty_out(vty, " ipv6 mld join %pPAs %pPAs\n", + &ij->group_addr, &ij->source_addr); + ++writes; + } + } + return writes; } #endif diff --git a/pimd/subdir.am b/pimd/subdir.am index fd7255cb87..9a7901ec3f 100644 --- a/pimd/subdir.am +++ b/pimd/subdir.am @@ -169,6 +169,7 @@ pimd_pim6d_CFLAGS = $(AM_CFLAGS) -DPIM_IPV=6 pimd_pim6d_LDADD = lib/libfrr.la $(LIBCAP) endif +pimd_test_igmpv3_join_CFLAGS = $(AM_CFLAGS) -DPIM_IPV=4 pimd_test_igmpv3_join_LDADD = lib/libfrr.la pimd_test_igmpv3_join_SOURCES = pimd/test_igmpv3_join.c diff --git a/pimd/test_igmpv3_join.c b/pimd/test_igmpv3_join.c index 07070900d2..926e453c9d 100644 --- a/pimd/test_igmpv3_join.c +++ b/pimd/test_igmpv3_join.c @@ -54,8 +54,8 @@ static int iface_solve_index(const char *ifname) int main(int argc, const char *argv[]) { - struct in_addr group_addr; - struct in_addr source_addr; + pim_addr group_addr; + pim_addr source_addr; const char *ifname; const char *group; const char *source; @@ -106,7 +106,7 @@ int main(int argc, const char *argv[]) exit(1); } - result = pim_igmp_join_source(fd, ifindex, group_addr, source_addr); + result = pim_gm_join_source(fd, ifindex, group_addr, source_addr); if (result) { fprintf(stderr, "%s: setsockopt(fd=%d) failure for IGMP group %s source %s ifindex %d on interface %s: errno=%d: %s\n", diff --git a/ripd/rip_nb.c b/ripd/rip_nb.c index 9947c01af5..fa6652faf4 100644 --- a/ripd/rip_nb.c +++ b/ripd/rip_nb.c @@ -10,6 +10,7 @@ #include "libfrr.h" #include "ripd/rip_nb.h" +#include "lib/if_rmap.h" /* clang-format off */ const struct frr_yang_module_info frr_ripd_info = { @@ -166,6 +167,28 @@ const struct frr_yang_module_info frr_ripd_info = { }, }, { + .xpath = "/frr-ripd:ripd/instance/if-route-maps/if-route-map", + .cbs = { + .create = ripd_instance_if_route_maps_if_route_map_create, + .destroy = ripd_instance_if_route_maps_if_route_map_destroy, + .cli_show = cli_show_if_route_map, + } + }, + { + .xpath = "/frr-ripd:ripd/instance/if-route-maps/if-route-map/in-route-map", + .cbs = { + .modify = ripd_instance_if_route_maps_if_route_map_in_route_map_modify, + .destroy = ripd_instance_if_route_maps_if_route_map_in_route_map_destroy, + } + }, + { + .xpath = "/frr-ripd:ripd/instance/if-route-maps/if-route-map/out-route-map", + .cbs = { + .modify = ripd_instance_if_route_maps_if_route_map_out_route_map_modify, + .destroy = ripd_instance_if_route_maps_if_route_map_out_route_map_destroy, + } + }, + { .xpath = "/frr-ripd:ripd/instance/static-route", .cbs = { .cli_show = cli_show_rip_route, @@ -338,6 +361,66 @@ const struct frr_yang_module_info frr_ripd_info = { }, }, { + .xpath = "/frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop", + .cbs = { + .get_next = ripd_instance_state_routes_route_nexthops_nexthop_get_next, + } + }, + { + .xpath = "/frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/nh-type", + .cbs = { + .get_elem = ripd_instance_state_routes_route_nexthops_nexthop_nh_type_get_elem, + } + }, + { + .xpath = "/frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/protocol", + .cbs = { + .get_elem = ripd_instance_state_routes_route_nexthops_nexthop_protocol_get_elem, + } + }, + { + .xpath = "/frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/rip-type", + .cbs = { + .get_elem = ripd_instance_state_routes_route_nexthops_nexthop_rip_type_get_elem, + } + }, + { + .xpath = "/frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/gateway", + .cbs = { + .get_elem = ripd_instance_state_routes_route_nexthops_nexthop_gateway_get_elem, + } + }, + { + .xpath = "/frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/interface", + .cbs = { + .get_elem = ripd_instance_state_routes_route_nexthops_nexthop_interface_get_elem, + } + }, + { + .xpath = "/frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/from", + .cbs = { + .get_elem = ripd_instance_state_routes_route_nexthops_nexthop_from_get_elem, + } + }, + { + .xpath = "/frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/tag", + .cbs = { + .get_elem = ripd_instance_state_routes_route_nexthops_nexthop_tag_get_elem, + } + }, + { + .xpath = "/frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/external-metric", + .cbs = { + .get_elem = ripd_instance_state_routes_route_nexthops_nexthop_external_metric_get_elem, + } + }, + { + .xpath = "/frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/expire-time", + .cbs = { + .get_elem = ripd_instance_state_routes_route_nexthops_nexthop_expire_time_get_elem, + } + }, + { .xpath = "/frr-ripd:ripd/instance/state/routes/route/metric", .cbs = { .get_elem = ripd_instance_state_routes_route_metric_get_elem, diff --git a/ripd/rip_nb.h b/ripd/rip_nb.h index 99114c9928..ebc60fefb4 100644 --- a/ripd/rip_nb.h +++ b/ripd/rip_nb.h @@ -52,6 +52,18 @@ int ripd_instance_redistribute_route_map_destroy( struct nb_cb_destroy_args *args); int ripd_instance_redistribute_metric_modify(struct nb_cb_modify_args *args); int ripd_instance_redistribute_metric_destroy(struct nb_cb_destroy_args *args); +int ripd_instance_if_route_maps_if_route_map_create( + struct nb_cb_create_args *args); +int ripd_instance_if_route_maps_if_route_map_destroy( + struct nb_cb_destroy_args *args); +int ripd_instance_if_route_maps_if_route_map_in_route_map_modify( + struct nb_cb_modify_args *args); +int ripd_instance_if_route_maps_if_route_map_in_route_map_destroy( + struct nb_cb_destroy_args *args); +int ripd_instance_if_route_maps_if_route_map_out_route_map_modify( + struct nb_cb_modify_args *args); +int ripd_instance_if_route_maps_if_route_map_out_route_map_destroy( + struct nb_cb_destroy_args *args); int ripd_instance_static_route_create(struct nb_cb_create_args *args); int ripd_instance_static_route_destroy(struct nb_cb_destroy_args *args); int ripd_instance_timers_flush_interval_modify(struct nb_cb_modify_args *args); @@ -89,6 +101,37 @@ struct yang_data *ripd_instance_state_routes_route_interface_get_elem( struct nb_cb_get_elem_args *args); struct yang_data *ripd_instance_state_routes_route_metric_get_elem( struct nb_cb_get_elem_args *args); +const void *ripd_instance_state_routes_route_nexthops_nexthop_get_next( + struct nb_cb_get_next_args *args); +struct yang_data * +ripd_instance_state_routes_route_nexthops_nexthop_nh_type_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +ripd_instance_state_routes_route_nexthops_nexthop_protocol_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +ripd_instance_state_routes_route_nexthops_nexthop_rip_type_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +ripd_instance_state_routes_route_nexthops_nexthop_gateway_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +ripd_instance_state_routes_route_nexthops_nexthop_interface_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +ripd_instance_state_routes_route_nexthops_nexthop_from_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +ripd_instance_state_routes_route_nexthops_nexthop_tag_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +ripd_instance_state_routes_route_nexthops_nexthop_external_metric_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +ripd_instance_state_routes_route_nexthops_nexthop_expire_time_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data *ripd_instance_state_routes_route_metric_get_elem( + struct nb_cb_get_elem_args *args); int clear_rip_route_rpc(struct nb_cb_rpc_args *args); int lib_interface_rip_split_horizon_modify(struct nb_cb_modify_args *args); int lib_interface_rip_v2_broadcast_modify(struct nb_cb_modify_args *args); diff --git a/ripd/rip_nb_config.c b/ripd/rip_nb_config.c index 2277ddc204..343bb9bb57 100644 --- a/ripd/rip_nb_config.c +++ b/ripd/rip_nb_config.c @@ -3,6 +3,7 @@ * Copyright (C) 1997, 1998, 1999 Kunihiro Ishiguro <kunihiro@zebra.org> * Copyright (C) 2018 NetDEF, Inc. * Renato Westphal + * Copyright (C) 2023 LabN Consulting, L.L.C. */ #include <zebra.h> @@ -13,6 +14,7 @@ #include "prefix.h" #include "table.h" #include "command.h" +#include "if_rmap.h" #include "routemap.h" #include "northbound.h" #include "libfrr.h" @@ -681,6 +683,94 @@ int ripd_instance_redistribute_metric_destroy(struct nb_cb_destroy_args *args) } /* + * XPath: /frr-ripd:ripd/instance/if-route-maps/if-route-map + */ +int ripd_instance_if_route_maps_if_route_map_create( + struct nb_cb_create_args *args) +{ + /* if_rmap is created when first routemap is added */ + return NB_OK; +} + +int ripd_instance_if_route_maps_if_route_map_destroy( + struct nb_cb_destroy_args *args) +{ + struct rip *rip; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + /* + * YANG will prune edit deletes up to the most general deleted node so + * we need to handle deleting any existing state underneath and not + * count on those more specific callbacks being called individually. + */ + + rip = nb_running_get_entry(args->dnode, NULL, true); + if_rmap_yang_destroy_cb(rip->if_rmap_ctx, args->dnode); + + return NB_OK; +} + +static void if_route_map_modify(const struct lyd_node *dnode, + enum if_rmap_type type, bool delete) +{ + struct rip *rip = nb_running_get_entry(dnode, NULL, true); + + if_rmap_yang_modify_cb(rip->if_rmap_ctx, dnode, type, delete); +} + +/* + * XPath: /frr-ripd:ripd/instance/if-route-maps/if-route-map/in-route-map + */ +int ripd_instance_if_route_maps_if_route_map_in_route_map_modify( + struct nb_cb_modify_args *args) +{ + if (args->event != NB_EV_APPLY) + return NB_OK; + + if_route_map_modify(args->dnode, IF_RMAP_IN, false); + + return NB_OK; +} + +int ripd_instance_if_route_maps_if_route_map_in_route_map_destroy( + struct nb_cb_destroy_args *args) +{ + if (args->event != NB_EV_APPLY) + return NB_OK; + + if_route_map_modify(args->dnode, IF_RMAP_IN, true); + + return NB_OK; +} + +/* + * XPath: /frr-ripd:ripd/instance/if-route-maps/if-route-map/out-route-map + */ +int ripd_instance_if_route_maps_if_route_map_out_route_map_modify( + struct nb_cb_modify_args *args) +{ + if (args->event != NB_EV_APPLY) + return NB_OK; + + if_route_map_modify(args->dnode, IF_RMAP_OUT, false); + + return NB_OK; +} + +int ripd_instance_if_route_maps_if_route_map_out_route_map_destroy( + struct nb_cb_destroy_args *args) +{ + if (args->event != NB_EV_APPLY) + return NB_OK; + + if_route_map_modify(args->dnode, IF_RMAP_OUT, true); + + return NB_OK; +} + +/* * XPath: /frr-ripd:ripd/instance/static-route */ int ripd_instance_static_route_create(struct nb_cb_create_args *args) diff --git a/ripd/rip_nb_state.c b/ripd/rip_nb_state.c index 0e2931b464..fa0d382a0e 100644 --- a/ripd/rip_nb_state.c +++ b/ripd/rip_nb_state.c @@ -207,10 +207,171 @@ struct yang_data *ripd_instance_state_routes_route_prefix_get_elem( const struct route_node *rn = args->list_entry; const struct rip_info *rinfo = listnode_head(rn->info); + assert(rinfo); return yang_data_new_ipv4p(args->xpath, &rinfo->rp->p); } /* + * XPath: /frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop + */ +const void *ripd_instance_state_routes_route_nexthops_nexthop_get_next( + struct nb_cb_get_next_args *args) +{ + const struct route_node *rn = args->parent_list_entry; + const struct listnode *node = args->list_entry; + + assert(rn); + if (node) + return listnextnode(node); + assert(rn->info); + return listhead((struct list *)rn->info); +} + +static inline const struct rip_info *get_rip_info(const void *info) +{ + return (const struct rip_info *)listgetdata( + (const struct listnode *)info); +} + +/* + * XPath: /frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/nh-type + */ +struct yang_data * +ripd_instance_state_routes_route_nexthops_nexthop_nh_type_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct rip_info *rinfo = get_rip_info(args->list_entry); + + assert(rinfo); + return yang_data_new_enum(args->xpath, rinfo->nh.type); +} + +/* + * XPath: /frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/protocol + */ +struct yang_data * +ripd_instance_state_routes_route_nexthops_nexthop_protocol_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct rip_info *rinfo = get_rip_info(args->list_entry); + + assert(rinfo); + return yang_data_new_enum(args->xpath, rinfo->type); +} + +/* + * XPath: /frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/rip-type + */ +struct yang_data * +ripd_instance_state_routes_route_nexthops_nexthop_rip_type_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct rip_info *rinfo = get_rip_info(args->list_entry); + + assert(rinfo); + return yang_data_new_enum(args->xpath, rinfo->sub_type); +} + +/* + * XPath: /frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/gateway + */ +struct yang_data * +ripd_instance_state_routes_route_nexthops_nexthop_gateway_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct rip_info *rinfo = get_rip_info(args->list_entry); + + if (rinfo->nh.type != NEXTHOP_TYPE_IPV4 && + rinfo->nh.type != NEXTHOP_TYPE_IPV4_IFINDEX) + return NULL; + + return yang_data_new_ipv4(args->xpath, &rinfo->nh.gate.ipv4); +} + +/* + * XPath: /frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/interface + */ +struct yang_data * +ripd_instance_state_routes_route_nexthops_nexthop_interface_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct rip_info *rinfo = get_rip_info(args->list_entry); + const struct rip *rip = rip_info_get_instance(rinfo); + + if (rinfo->nh.type != NEXTHOP_TYPE_IFINDEX && + rinfo->nh.type != NEXTHOP_TYPE_IPV4_IFINDEX) + return NULL; + + return yang_data_new_string( + args->xpath, + ifindex2ifname(rinfo->nh.ifindex, rip->vrf->vrf_id)); +} + +/* + * XPath: /frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/from + */ +struct yang_data * +ripd_instance_state_routes_route_nexthops_nexthop_from_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct rip_info *rinfo = get_rip_info(args->list_entry); + + if (rinfo->type != ZEBRA_ROUTE_RIP || rinfo->sub_type != RIP_ROUTE_RTE) + return NULL; + + return yang_data_new_ipv4(args->xpath, &rinfo->from); +} + +/* + * XPath: /frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/tag + */ +struct yang_data * +ripd_instance_state_routes_route_nexthops_nexthop_tag_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct rip_info *rinfo = get_rip_info(args->list_entry); + + return yang_data_new_uint32(args->xpath, rinfo->tag); +} + +/* + * XPath: + * /frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/external-metric + */ +struct yang_data * +ripd_instance_state_routes_route_nexthops_nexthop_external_metric_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct rip_info *rinfo = get_rip_info(args->list_entry); + + if ((rinfo->type == ZEBRA_ROUTE_RIP && + rinfo->sub_type == RIP_ROUTE_RTE) || + rinfo->metric == RIP_METRIC_INFINITY || rinfo->external_metric == 0) + return NULL; + return yang_data_new_uint32(args->xpath, rinfo->external_metric); +} + +/* + * XPath: + * /frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/expire-time + */ +struct yang_data * +ripd_instance_state_routes_route_nexthops_nexthop_expire_time_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct rip_info *rinfo = get_rip_info(args->list_entry); + struct event *event; + + if ((event = rinfo->t_timeout) == NULL) + event = rinfo->t_garbage_collect; + if (!event) + return NULL; + + return yang_data_new_uint32(args->xpath, + event_timer_remain_second(event)); +} + +/* * XPath: /frr-ripd:ripd/instance/state/routes/route/next-hop */ struct yang_data *ripd_instance_state_routes_route_next_hop_get_elem( diff --git a/ripd/ripd.c b/ripd/ripd.c index ae4d93b4f5..fb3d574aaa 100644 --- a/ripd/ripd.c +++ b/ripd/ripd.c @@ -1500,7 +1500,7 @@ static int rip_send_packet(uint8_t *buf, int size, struct sockaddr_in *to, ret = sendmsg(rip->sock, &msg, 0); if (IS_RIP_DEBUG_EVENT) - zlog_debug("SEND to %pI4%d", &sin.sin_addr, + zlog_debug("SEND to %pI4 port %d", &sin.sin_addr, ntohs(sin.sin_port)); if (ret < 0) @@ -2836,16 +2836,11 @@ uint8_t rip_distance_apply(struct rip *rip, struct rip_info *rinfo) if (access_list_apply(alist, &rinfo->rp->p) == FILTER_DENY) return 0; - - return rdistance->distance; - } else - return rdistance->distance; + } + return rdistance->distance; } - if (rip->distance) - return rip->distance; - - return 0; + return rip->distance; } static void rip_distance_show(struct vty *vty, struct rip *rip) @@ -3209,9 +3204,6 @@ static int config_write_rip(struct vty *vty) /* Distribute configuration. */ config_write_distribute(vty, rip->distribute_ctx); - /* Interface routemap configuration */ - config_write_if_rmap(vty, rip->if_rmap_ctx); - vty_out(vty, "exit\n"); write = 1; diff --git a/ripngd/ripng_nb.c b/ripngd/ripng_nb.c index 63144d866b..1c6d7191a3 100644 --- a/ripngd/ripng_nb.c +++ b/ripngd/ripng_nb.c @@ -10,6 +10,7 @@ #include "libfrr.h" #include "ripngd/ripng_nb.h" +#include "lib/if_rmap.h" /* clang-format off */ const struct frr_yang_module_info frr_ripngd_info = { @@ -115,6 +116,28 @@ const struct frr_yang_module_info frr_ripngd_info = { }, }, { + .xpath = "/frr-ripngd:ripngd/instance/if-route-maps/if-route-map", + .cbs = { + .create = ripngd_instance_if_route_maps_if_route_map_create, + .destroy = ripngd_instance_if_route_maps_if_route_map_destroy, + .cli_show = cli_show_if_route_map, + } + }, + { + .xpath = "/frr-ripngd:ripngd/instance/if-route-maps/if-route-map/in-route-map", + .cbs = { + .modify = ripngd_instance_if_route_maps_if_route_map_in_route_map_modify, + .destroy = ripngd_instance_if_route_maps_if_route_map_in_route_map_destroy, + } + }, + { + .xpath = "/frr-ripngd:ripngd/instance/if-route-maps/if-route-map/out-route-map", + .cbs = { + .modify = ripngd_instance_if_route_maps_if_route_map_out_route_map_modify, + .destroy = ripngd_instance_if_route_maps_if_route_map_out_route_map_destroy, + } + }, + { .xpath = "/frr-ripngd:ripngd/instance/static-route", .cbs = { .cli_show = cli_show_ripng_route, diff --git a/ripngd/ripng_nb.h b/ripngd/ripng_nb.h index 675cef7c92..1c0e63c241 100644 --- a/ripngd/ripng_nb.h +++ b/ripngd/ripng_nb.h @@ -39,6 +39,18 @@ int ripngd_instance_redistribute_route_map_destroy( int ripngd_instance_redistribute_metric_modify(struct nb_cb_modify_args *args); int ripngd_instance_redistribute_metric_destroy( struct nb_cb_destroy_args *args); +int ripngd_instance_if_route_maps_if_route_map_create( + struct nb_cb_create_args *args); +int ripngd_instance_if_route_maps_if_route_map_destroy( + struct nb_cb_destroy_args *args); +int ripngd_instance_if_route_maps_if_route_map_in_route_map_modify( + struct nb_cb_modify_args *args); +int ripngd_instance_if_route_maps_if_route_map_in_route_map_destroy( + struct nb_cb_destroy_args *args); +int ripngd_instance_if_route_maps_if_route_map_out_route_map_modify( + struct nb_cb_modify_args *args); +int ripngd_instance_if_route_maps_if_route_map_out_route_map_destroy( + struct nb_cb_destroy_args *args); int ripngd_instance_static_route_create(struct nb_cb_create_args *args); int ripngd_instance_static_route_destroy(struct nb_cb_destroy_args *args); int ripngd_instance_aggregate_address_create(struct nb_cb_create_args *args); diff --git a/ripngd/ripng_nb_config.c b/ripngd/ripng_nb_config.c index 006bf79ce8..30f707e061 100644 --- a/ripngd/ripng_nb_config.c +++ b/ripngd/ripng_nb_config.c @@ -3,6 +3,7 @@ * Copyright (C) 1998 Kunihiro Ishiguro * Copyright (C) 2018 NetDEF, Inc. * Renato Westphal + * Copyright (C) 2023 LabN Consulting, L.L.C. */ #include <zebra.h> @@ -13,6 +14,7 @@ #include "prefix.h" #include "table.h" #include "command.h" +#include "if_rmap.h" #include "routemap.h" #include "agg_table.h" #include "northbound.h" @@ -503,6 +505,93 @@ int ripngd_instance_redistribute_metric_destroy(struct nb_cb_destroy_args *args) } /* + * XPath: /frr-ripngd:ripngd/instance/if-route-maps/if-route-map + */ +int ripngd_instance_if_route_maps_if_route_map_create( + struct nb_cb_create_args *args) +{ + /* if_rmap is created when first routemap is added */ + return NB_OK; +} + +int ripngd_instance_if_route_maps_if_route_map_destroy( + struct nb_cb_destroy_args *args) +{ + struct ripng *ripng; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + /* + * YANG will prune edit deletes up to the most general deleted node so + * we need to handle deleting any existing state underneath and not + * count on those more specific callbacks being called individually. + */ + + ripng = nb_running_get_entry(args->dnode, NULL, true); + if_rmap_yang_destroy_cb(ripng->if_rmap_ctx, args->dnode); + + return NB_OK; +} + +static void if_route_map_modify(const struct lyd_node *dnode, + enum if_rmap_type type, bool delete) +{ + struct ripng *ripng = nb_running_get_entry(dnode, NULL, true); + + if_rmap_yang_modify_cb(ripng->if_rmap_ctx, dnode, type, delete); +} +/* + * XPath: /frr-ripng:ripng/instance/if-route-maps/if-route-map/in-route-map + */ +int ripngd_instance_if_route_maps_if_route_map_in_route_map_modify( + struct nb_cb_modify_args *args) +{ + if (args->event != NB_EV_APPLY) + return NB_OK; + + if_route_map_modify(args->dnode, IF_RMAP_IN, false); + + return NB_OK; +} + +int ripngd_instance_if_route_maps_if_route_map_in_route_map_destroy( + struct nb_cb_destroy_args *args) +{ + if (args->event != NB_EV_APPLY) + return NB_OK; + + if_route_map_modify(args->dnode, IF_RMAP_IN, true); + + return NB_OK; +} + +/* + * XPath: /frr-ripngd:ripngd/instance/if-route-maps/if-route-map/out-route-map + */ +int ripngd_instance_if_route_maps_if_route_map_out_route_map_modify( + struct nb_cb_modify_args *args) +{ + if (args->event != NB_EV_APPLY) + return NB_OK; + + if_route_map_modify(args->dnode, IF_RMAP_OUT, false); + + return NB_OK; +} + +int ripngd_instance_if_route_maps_if_route_map_out_route_map_destroy( + struct nb_cb_destroy_args *args) +{ + if (args->event != NB_EV_APPLY) + return NB_OK; + + if_route_map_modify(args->dnode, IF_RMAP_OUT, true); + + return NB_OK; +} + +/* * XPath: /frr-ripngd:ripngd/instance/static-route */ int ripngd_instance_static_route_create(struct nb_cb_create_args *args) diff --git a/ripngd/ripngd.c b/ripngd/ripngd.c index 79e8871f6a..2f6409a70d 100644 --- a/ripngd/ripngd.c +++ b/ripngd/ripngd.c @@ -2231,7 +2231,6 @@ static int ripng_config_write(struct vty *vty) nb_cli_show_dnode_cmds(vty, dnode, false); config_write_distribute(vty, ripng->distribute_ctx); - config_write_if_rmap(vty, ripng->if_rmap_ctx); vty_out(vty, "exit\n"); diff --git a/sharpd/sharp_vty.c b/sharpd/sharp_vty.c index 5e70c57443..ca2212cd87 100644 --- a/sharpd/sharp_vty.c +++ b/sharpd/sharp_vty.c @@ -1061,7 +1061,7 @@ DEFUN (show_sharp_ted, return CMD_WARNING_CONFIG_FAILED; } /* Get the Subnet from the Link State Database */ - subnet = ls_find_subnet(sg.ted, pref); + subnet = ls_find_subnet(sg.ted, &pref); if (!subnet) { vty_out(vty, "No subnet found for ID %pFX\n", &pref); @@ -1247,6 +1247,7 @@ DEFPY (show_sharp_cspf, } if (path->status != SUCCESS) { vty_out(vty, "Path computation failed: %d\n", path->status); + cpath_del(path); return CMD_SUCCESS; } @@ -1262,7 +1263,7 @@ DEFPY (show_sharp_cspf, &edge->attributes->standard.remote6); } vty_out(vty, "\n"); - + cpath_del(path); return CMD_SUCCESS; } diff --git a/staticd/static_main.c b/staticd/static_main.c index 09f22318ff..9809d9751a 100644 --- a/staticd/static_main.c +++ b/staticd/static_main.c @@ -161,6 +161,10 @@ static const struct frr_yang_module_info *const staticd_yang_modules[] = { #define STATIC_VTY_PORT 2616 +/* + * NOTE: .flags == FRR_NO_SPLIT_CONFIG to avoid reading split config, mgmtd will + * do this for us now + */ FRR_DAEMON_INFO(staticd, STATIC, .vty_port = STATIC_VTY_PORT, .proghelp = "Implementation of STATIC.", @@ -170,7 +174,8 @@ FRR_DAEMON_INFO(staticd, STATIC, .vty_port = STATIC_VTY_PORT, .privs = &static_privs, .yang_modules = staticd_yang_modules, .n_yang_modules = array_size(staticd_yang_modules), -); + + .flags = FRR_NO_SPLIT_CONFIG); int main(int argc, char **argv, char **envp) { @@ -210,9 +215,12 @@ int main(int argc, char **argv, char **envp) routing_control_plane_protocols_register_vrf_dependency(); - snprintf(backup_config_file, sizeof(backup_config_file), - "%s/zebra.conf", frr_sysconfdir); - staticd_di.backup_config_file = backup_config_file; + /* + * We set FRR_NO_SPLIT_CONFIG flag to avoid reading our config, but we + * still need to write one if vtysh tells us to. Setting the host + * config filename does this. + */ + host_config_set(config_default); frr_config_fork(); frr_run(master); diff --git a/tests/bgpd/test_mp_attr.c b/tests/bgpd/test_mp_attr.c index 54596dbdfb..ae7903e0cc 100644 --- a/tests/bgpd/test_mp_attr.c +++ b/tests/bgpd/test_mp_attr.c @@ -1042,9 +1042,9 @@ static void parse_test(struct peer *peer, struct test_segment *t, int type) if (!parse_ret) { if (type == BGP_ATTR_MP_REACH_NLRI) - nlri_ret = bgp_nlri_parse(peer, &attr, &nlri, 0); + nlri_ret = bgp_nlri_parse(peer, &attr, &nlri, false); else if (type == BGP_ATTR_MP_UNREACH_NLRI) - nlri_ret = bgp_nlri_parse(peer, &attr, &nlri, 1); + nlri_ret = bgp_nlri_parse(peer, &attr, &nlri, true); } handle_result(peer, t, parse_ret, nlri_ret); } diff --git a/tests/isisd/test_common.c b/tests/isisd/test_common.c index d0288f600d..e47456965e 100644 --- a/tests/isisd/test_common.c +++ b/tests/isisd/test_common.c @@ -98,7 +98,7 @@ static void lsp_add_ip_reach(struct isis_lsp *lsp, { struct prefix prefix; struct sr_prefix_cfg pcfg = {}; - struct sr_prefix_cfg *pcfg_p = NULL; + struct sr_prefix_cfg *pcfg_p[SR_ALGORITHM_COUNT] = {NULL}; if (str2prefix(prefix_str, &prefix) != 1) { zlog_debug("%s: invalid network: %s", __func__, prefix_str); @@ -106,7 +106,7 @@ static void lsp_add_ip_reach(struct isis_lsp *lsp, } if (CHECK_FLAG(tnode->flags, F_ISIS_TEST_NODE_SR)) { - pcfg_p = &pcfg; + pcfg_p[SR_ALGORITHM_SPF] = &pcfg; pcfg.sid = *next_sid_index; *next_sid_index = *next_sid_index + 1; @@ -163,31 +163,32 @@ static void lsp_add_reach(struct isis_lsp *lsp, static void lsp_add_router_capability(struct isis_lsp *lsp, const struct isis_test_node *tnode) { - struct isis_router_cap cap = {}; + struct isis_router_cap *cap; if (!tnode->router_id) return; - if (inet_pton(AF_INET, tnode->router_id, &cap.router_id) != 1) { + cap = isis_tlvs_init_router_capability(lsp->tlvs); + + if (inet_pton(AF_INET, tnode->router_id, &cap->router_id) != 1) { zlog_debug("%s: invalid router-id: %s", __func__, tnode->router_id); return; } if (CHECK_FLAG(tnode->flags, F_ISIS_TEST_NODE_SR)) { - cap.srgb.flags = + cap->srgb.flags = ISIS_SUBTLV_SRGB_FLAG_I | ISIS_SUBTLV_SRGB_FLAG_V; - cap.srgb.lower_bound = tnode->srgb.lower_bound - ? tnode->srgb.lower_bound - : SRGB_DFTL_LOWER_BOUND; - cap.srgb.range_size = tnode->srgb.range_size - ? tnode->srgb.range_size - : SRGB_DFTL_RANGE_SIZE; - cap.algo[0] = SR_ALGORITHM_SPF; - cap.algo[1] = SR_ALGORITHM_UNSET; + cap->srgb.lower_bound = tnode->srgb.lower_bound + ? tnode->srgb.lower_bound + : SRGB_DFTL_LOWER_BOUND; + cap->srgb.range_size = tnode->srgb.range_size + ? tnode->srgb.range_size + : SRGB_DFTL_RANGE_SIZE; + cap->algo[0] = SR_ALGORITHM_SPF; + cap->algo[1] = SR_ALGORITHM_UNSET; } - isis_tlvs_set_router_capability(lsp->tlvs, &cap); } static void lsp_add_mt_router_info(struct isis_lsp *lsp, diff --git a/tests/isisd/test_isis_spf.c b/tests/isisd/test_isis_spf.c index 85ddfea5b5..6eb180b501 100644 --- a/tests/isisd/test_isis_spf.c +++ b/tests/isisd/test_isis_spf.c @@ -49,12 +49,13 @@ static void test_run_spf(struct vty *vty, const struct isis_topology *topology, /* Run SPF. */ spf_type = reverse ? SPF_TYPE_REVERSE : SPF_TYPE_FORWARD; spftree = isis_spftree_new(area, lspdb, root->sysid, level, tree, - spf_type, F_SPFTREE_NO_ADJACENCIES); + spf_type, F_SPFTREE_NO_ADJACENCIES, + SR_ALGORITHM_SPF); isis_run_spf(spftree); /* Print the SPT and the corresponding routing table. */ isis_print_spftree(vty, spftree); - isis_print_routes(vty, spftree, false, false); + isis_print_routes(vty, spftree, NULL, false, false); /* Cleanup SPF tree. */ isis_spftree_del(spftree); @@ -71,8 +72,9 @@ static void test_run_lfa(struct vty *vty, const struct isis_topology *topology, /* Run forward SPF in the root node. */ flags = F_SPFTREE_NO_ADJACENCIES; - spftree_self = isis_spftree_new(area, lspdb, root->sysid, level, tree, - SPF_TYPE_FORWARD, flags); + spftree_self = + isis_spftree_new(area, lspdb, root->sysid, level, tree, + SPF_TYPE_FORWARD, flags, SR_ALGORITHM_SPF); isis_run_spf(spftree_self); /* Run forward SPF on all adjacent routers. */ @@ -84,9 +86,9 @@ static void test_run_lfa(struct vty *vty, const struct isis_topology *topology, /* Print the SPT and the corresponding main/backup routing tables. */ isis_print_spftree(vty, spftree_self); vty_out(vty, "Main:\n"); - isis_print_routes(vty, spftree_self, false, false); + isis_print_routes(vty, spftree_self, NULL, false, false); vty_out(vty, "Backup:\n"); - isis_print_routes(vty, spftree_self, false, true); + isis_print_routes(vty, spftree_self, NULL, false, true); /* Cleanup everything. */ isis_spftree_del(spftree_self); @@ -107,8 +109,9 @@ static void test_run_rlfa(struct vty *vty, const struct isis_topology *topology, /* Run forward SPF in the root node. */ flags = F_SPFTREE_NO_ADJACENCIES; - spftree_self = isis_spftree_new(area, lspdb, root->sysid, level, tree, - SPF_TYPE_FORWARD, flags); + spftree_self = + isis_spftree_new(area, lspdb, root->sysid, level, tree, + SPF_TYPE_FORWARD, flags, SR_ALGORITHM_SPF); isis_run_spf(spftree_self); /* Run reverse SPF in the root node. */ @@ -162,9 +165,9 @@ static void test_run_rlfa(struct vty *vty, const struct isis_topology *topology, /* Print the SPT and the corresponding main/backup routing tables. */ isis_print_spftree(vty, spftree_self); vty_out(vty, "Main:\n"); - isis_print_routes(vty, spftree_self, false, false); + isis_print_routes(vty, spftree_self, NULL, false, false); vty_out(vty, "Backup:\n"); - isis_print_routes(vty, spftree_self, false, true); + isis_print_routes(vty, spftree_self, NULL, false, true); /* Cleanup everything. */ isis_spftree_del(spftree_self); @@ -187,8 +190,9 @@ static void test_run_ti_lfa(struct vty *vty, /* Run forward SPF in the root node. */ flags = F_SPFTREE_NO_ADJACENCIES; - spftree_self = isis_spftree_new(area, lspdb, root->sysid, level, tree, - SPF_TYPE_FORWARD, flags); + spftree_self = + isis_spftree_new(area, lspdb, root->sysid, level, tree, + SPF_TYPE_FORWARD, flags, SR_ALGORITHM_SPF); isis_run_spf(spftree_self); /* Run reverse SPF in the root node. */ @@ -224,7 +228,7 @@ static void test_run_ti_lfa(struct vty *vty, * Print the post-convergence SPT and the corresponding routing table. */ isis_print_spftree(vty, spftree_pc); - isis_print_routes(vty, spftree_self, false, true); + isis_print_routes(vty, spftree_self, NULL, false, true); /* Cleanup everything. */ isis_spftree_del(spftree_self); diff --git a/tests/lib/subdir.am b/tests/lib/subdir.am index 1bc092a49e..e950d0120d 100644 --- a/tests/lib/subdir.am +++ b/tests/lib/subdir.am @@ -15,7 +15,7 @@ tests_lib_test_frrscript_CFLAGS = $(TESTS_CFLAGS) tests_lib_test_frrscript_CPPFLAGS = $(TESTS_CPPFLAGS) tests_lib_test_frrscript_LDADD = $(ALL_TESTS_LDADD) tests_lib_test_frrscript_SOURCES = tests/lib/test_frrscript.c -EXTRA_DIST += tests/lib/test_frrscript.py +EXTRA_DIST += tests/lib/test_frrscript.py tests/lib/script1.lua ############################################################################## 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 f7c3a4c19d..92bb99c8f2 100644 --- a/tests/topotests/all_protocol_startup/test_all_protocol_startup.py +++ b/tests/topotests/all_protocol_startup/test_all_protocol_startup.py @@ -288,6 +288,17 @@ def test_converge_protocols(): thisDir = os.path.dirname(os.path.realpath(__file__)) + # We need loopback to have a link local so it always is the + # "selected" router for fe80::/64 when we static compare below. + print("Adding link-local to loopback for stable results") + cmd = ( + "mac=`cat /sys/class/net/lo/address`; echo lo: $mac;" + " [ -z \"$mac\" ] && continue; IFS=':'; set $mac; unset IFS;" + " ip address add dev lo scope link" + " fe80::$(printf %02x $((0x$1 ^ 2)))$2:${3}ff:fe$4:$5$6/64" + ) + net["r1"].cmd_raises(cmd) + print("\n\n** Waiting for protocols convergence") print("******************************************\n") diff --git a/tests/topotests/bgp_evpn_vxlan_svd_topo1/test_bgp_evpn_vxlan_svd.py b/tests/topotests/bgp_evpn_vxlan_svd_topo1/test_bgp_evpn_vxlan_svd.py index 8b9631175a..65c0c3532a 100755 --- a/tests/topotests/bgp_evpn_vxlan_svd_topo1/test_bgp_evpn_vxlan_svd.py +++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/test_bgp_evpn_vxlan_svd.py @@ -83,6 +83,7 @@ def build_topo(tgen): switch.add_link(tgen.gears["PE2"]) switch.add_link(tgen.gears["host2"]) + def setup_pe_router(tgen, pe_name, tunnel_local_ip, svi_ip, intf): pe = tgen.gears[pe_name] @@ -100,7 +101,9 @@ def setup_pe_router(tgen, pe_name, tunnel_local_ip, svi_ip, intf): # setup single vxlan device pe.run( - "ip link add dev vxlan0 type vxlan dstport 4789 local {0} nolearning external".format(tunnel_local_ip) + "ip link add dev vxlan0 type vxlan dstport 4789 local {0} nolearning external".format( + tunnel_local_ip + ) ) pe.run("ip link set dev vxlan0 master bridge") pe.run("bridge link set dev vxlan0 vlan_tunnel on") @@ -136,10 +139,12 @@ def setup_pe_router(tgen, pe_name, tunnel_local_ip, svi_ip, intf): pe.run("bridge vlan add dev vxlan0 vid 300") pe.run("bridge vlan add dev vxlan0 vid 300 tunnel_info id 300") + def setup_p_router(tgen, p_name): p1 = tgen.gears[p_name] p1.run("sysctl -w net.ipv4.ip_forward=1") + def setup_module(mod): "Sets up the pytest environment" @@ -180,7 +185,7 @@ def teardown_module(mod): "Teardown the pytest environment" tgen = get_topogen() - #tgen.mininet_cli() + # tgen.mininet_cli() # This function tears down the whole topology. tgen.stop_topology() @@ -204,17 +209,21 @@ def check_vni_macs_present(tgen, router, vni, maclist): ) return None + def check_flood_entry_present(pe, vni, vtep): if not topotest.iproute2_is_fdb_get_capable(): return None - output = pe.run("bridge fdb get 00:00:00:00:00:00 dev vxlan0 vni {} self".format(vni)) + output = pe.run( + "bridge fdb get 00:00:00:00:00:00 dev vxlan0 vni {} self".format(vni) + ) if str(vtep) not in output: return output return None + def test_pe1_converge_evpn(): "Wait for protocol convergence" @@ -231,6 +240,15 @@ def test_pe1_converge_evpn(): _, result = topotest.run_and_expect(test_func, None, count=45, wait=1) assertmsg = '"{}" JSON output mismatches'.format(pe1.name) + # Let's ensure that the hosts have actually tried talking to + # each other. Otherwise under certain startup conditions + # they may not actually do any l2 arp'ing and as such + # the bridges won't know about the hosts on their networks + host1 = tgen.gears["host1"] + host1.run("ping -c 1 10.10.1.56") + host2 = tgen.gears["host2"] + host2.run("ping -c 1 10.10.1.55") + test_func = partial( check_vni_macs_present, tgen, @@ -249,11 +267,12 @@ def test_pe1_converge_evpn(): assertmsg = '"{}" Flood FDB Entry for VTEP {} not found'.format(pe1.name, vtep) assert result is None, assertmsg + def test_pe2_converge_evpn(): "Wait for protocol convergence" tgen = get_topogen() -#Don't run this test if we have any failure. + # Don't run this test if we have any failure. if tgen.routers_have_failure(): pytest.skip(tgen.errors) @@ -284,6 +303,7 @@ def test_pe2_converge_evpn(): assertmsg = '"{}" Flood FDB Entry for VTEP {} not found'.format(pe2.name, vtep) assert result is None, assertmsg + def mac_learn_test(host, local): "check the host MAC gets learned by the VNI" @@ -389,11 +409,11 @@ def ip_learn_test(tgen, host, local, remote, ip_addr): if "HWaddr" in line_items[0]: mac = line_items[1] break - #print(host_output) + # print(host_output) # check we have a local association between the MAC and IP local_output = local.vtysh_cmd("show evpn mac vni 101 mac {} json".format(mac)) - #print(local_output) + # print(local_output) local_output_json = json.loads(local_output) mac_type = local_output_json[mac]["type"] assertmsg = "Failed to learn local IP address on host {}".format(host.name) @@ -417,7 +437,7 @@ def ip_learn_test(tgen, host, local, remote, ip_addr): remote_output = remote.vtysh_cmd( "show evpn mac vni 101 mac {} json".format(mac) ) - #print(remote_output) + # print(remote_output) remote_output_json = json.loads(remote_output) type = remote_output_json[mac]["type"] if not remote_output_json[mac]["neighbors"] == "none": @@ -431,12 +451,12 @@ def ip_learn_test(tgen, host, local, remote, ip_addr): count += 1 sleep(1) - #print("tries: {}".format(count)) + # print("tries: {}".format(count)) assertmsg = "{} remote learned mac no address: {} ".format(host.name, mac) # some debug for this failure if not converged == True: log_output = remote.run("cat zebra.log") - #print(log_output) + # print(log_output) assert converged == True, assertmsg if remote_output_json[mac]["neighbors"]["active"]: @@ -463,8 +483,8 @@ def test_ip_pe1_learn(): host1 = tgen.gears["host1"] pe1 = tgen.gears["PE1"] pe2 = tgen.gears["PE2"] - #pe2.vtysh_cmd("debug zebra vxlan") - #pe2.vtysh_cmd("debug zebra kernel") + # pe2.vtysh_cmd("debug zebra vxlan") + # pe2.vtysh_cmd("debug zebra kernel") # lets populate that arp cache host1.run("ping -c1 10.10.1.1") ip_learn_test(tgen, host1, pe1, pe2, "10.10.1.55") @@ -482,13 +502,14 @@ def test_ip_pe2_learn(): host2 = tgen.gears["host2"] pe1 = tgen.gears["PE1"] pe2 = tgen.gears["PE2"] - #pe1.vtysh_cmd("debug zebra vxlan") - #pe1.vtysh_cmd("debug zebra kernel") + # pe1.vtysh_cmd("debug zebra vxlan") + # pe1.vtysh_cmd("debug zebra kernel") # lets populate that arp cache host2.run("ping -c1 10.10.1.3") ip_learn_test(tgen, host2, pe2, pe1, "10.10.1.56") # tgen.mininet_cli() + def show_dvni_route(pe, vni, prefix, vrf): output = pe.vtysh_cmd("show ip route vrf {} {}".format(vrf, prefix)) @@ -502,6 +523,7 @@ def show_dvni_route(pe, vni, prefix, vrf): return None + def test_dvni(): "test Downstream VNI works as expected importing into PE1" @@ -517,7 +539,7 @@ def test_dvni(): _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) assertmsg = '"{}" DVNI route {} not found'.format(pe1.name, prefix) assert result is None, assertmsg - #tgen.mininet_cli() + # tgen.mininet_cli() def test_memory_leak(): diff --git a/tests/topotests/bgp_evpn_vxlan_topo1/test_bgp_evpn_vxlan.py b/tests/topotests/bgp_evpn_vxlan_topo1/test_bgp_evpn_vxlan.py index 6561833d6e..2884043012 100755 --- a/tests/topotests/bgp_evpn_vxlan_topo1/test_bgp_evpn_vxlan.py +++ b/tests/topotests/bgp_evpn_vxlan_topo1/test_bgp_evpn_vxlan.py @@ -164,6 +164,15 @@ def test_pe1_converge_evpn(): _, result = topotest.run_and_expect(test_func, None, count=45, wait=1) assertmsg = '"{}" JSON output mismatches'.format(pe1.name) + # Let's ensure that the hosts have actually tried talking to + # each other. Otherwise under certain startup conditions + # they may not actually do any l2 arp'ing and as such + # the bridges won't know about the hosts on their networks + host1 = tgen.gears["host1"] + host1.run("ping -c 1 10.10.1.56") + host2 = tgen.gears["host2"] + host2.run("ping -c 1 10.10.1.55") + test_func = partial( check_vni_macs_present, tgen, @@ -171,6 +180,7 @@ def test_pe1_converge_evpn(): 101, (("host1", "host1-eth0"), ("host2", "host2-eth0")), ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) if result: logger.warning("%s", result) @@ -385,8 +395,8 @@ def test_ip_pe1_learn(): host1 = tgen.gears["host1"] pe1 = tgen.gears["PE1"] pe2 = tgen.gears["PE2"] - #pe2.vtysh_cmd("debug zebra vxlan") - #pe2.vtysh_cmd("debug zebra kernel") + # pe2.vtysh_cmd("debug zebra vxlan") + # pe2.vtysh_cmd("debug zebra kernel") # lets populate that arp cache host1.run("ping -c1 10.10.1.1") ip_learn_test(tgen, host1, pe1, pe2, "10.10.1.55") @@ -404,8 +414,8 @@ def test_ip_pe2_learn(): host2 = tgen.gears["host2"] pe1 = tgen.gears["PE1"] pe2 = tgen.gears["PE2"] - #pe1.vtysh_cmd("debug zebra vxlan") - #pe1.vtysh_cmd("debug zebra kernel") + # pe1.vtysh_cmd("debug zebra vxlan") + # pe1.vtysh_cmd("debug zebra kernel") # lets populate that arp cache host2.run("ping -c1 10.10.1.3") ip_learn_test(tgen, host2, pe2, pe1, "10.10.1.56") diff --git a/tests/topotests/bgp_features/r1/ospf_neighbor.json b/tests/topotests/bgp_features/r1/ospf_neighbor.json index 3b5f46d934..caf700d82a 100644 --- a/tests/topotests/bgp_features/r1/ospf_neighbor.json +++ b/tests/topotests/bgp_features/r1/ospf_neighbor.json @@ -2,13 +2,13 @@ "neighbors":{ "192.168.0.2":[ { - "priority":5, + "nbrPriority":5, "converged":"Full" } ], "192.168.0.3":[ { - "priority":5, + "nbrPriority":5, "converged":"Full" } ] diff --git a/tests/topotests/bgp_features/r2/ospf_neighbor.json b/tests/topotests/bgp_features/r2/ospf_neighbor.json index 47bb57cd00..3a168ba335 100644 --- a/tests/topotests/bgp_features/r2/ospf_neighbor.json +++ b/tests/topotests/bgp_features/r2/ospf_neighbor.json @@ -2,13 +2,13 @@ "neighbors":{ "192.168.0.1":[ { - "priority":10, + "nbrPriority":10, "converged":"Full" } ], "192.168.0.3":[ { - "priority":5, + "nbrPriority":5, "converged":"Full" } ] diff --git a/tests/topotests/bgp_features/r3/ospf_neighbor.json b/tests/topotests/bgp_features/r3/ospf_neighbor.json index b84974ccca..9f8c05949f 100644 --- a/tests/topotests/bgp_features/r3/ospf_neighbor.json +++ b/tests/topotests/bgp_features/r3/ospf_neighbor.json @@ -2,13 +2,13 @@ "neighbors":{ "192.168.0.1":[ { - "priority":10, + "nbrPriority":10, "converged":"Full" } ], "192.168.0.2":[ { - "priority":10, + "nbrPriority":10, "converged":"Full" } ] diff --git a/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-3.py b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-3.py index 6388295c95..f8a01a1c9d 100644 --- a/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-3.py +++ b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-3.py @@ -2142,6 +2142,9 @@ def test_BGP_GR_TC_43_p1(request): tc_name, result ) + # restart the daemon or we get warnings in the follow-on tests + start_router_daemons(tgen, "r1", ["bgpd"]) + write_test_footer(tc_name) @@ -2432,6 +2435,9 @@ def test_BGP_GR_TC_44_p1(request): result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + # restart the daemon or we get warnings in the follow-on tests + start_router_daemons(tgen, "r2", ["bgpd"]) + write_test_footer(tc_name) @@ -2727,6 +2733,9 @@ def test_BGP_GR_TC_45_p1(request): result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + # restart the daemon or we get warnings in the follow-on tests + start_router_daemons(tgen, "r1", ["bgpd"]) + write_test_footer(tc_name) diff --git a/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-4.py b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-4.py index 1166cdc0ef..31aaa0b8a6 100644 --- a/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-4.py +++ b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-4.py @@ -763,6 +763,9 @@ def test_BGP_GR_TC_46_p1(request): tc_name, result ) + # restart the daemon or we get warnings in the follow-on tests + start_router_daemons(tgen, "r1", ["bgpd"]) + write_test_footer(tc_name) @@ -1023,6 +1026,9 @@ def test_BGP_GR_TC_47_p1(request): tc_name, result ) + # restart the daemon or we get warnings in the follow-on tests + start_router_daemons(tgen, "r1", ["bgpd"]) + write_test_footer(tc_name) @@ -1300,6 +1306,9 @@ def test_BGP_GR_TC_48_p1(request): tc_name, result ) + # restart the daemon or we get warnings in the follow-on tests + start_router_daemons(tgen, "r1", ["bgpd"]) + write_test_footer(tc_name) diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_up.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_up.py index eaa6aa4c30..46993c7d9a 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_up.py +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_up.py @@ -62,6 +62,25 @@ else: "pass", "Adding {} routes".format(num), ) + luCommand( + "ce1", + 'vtysh -c "show ip route summ" | grep "sharp" | cut -d " " -f 33', + str(num), + "wait", + "See all sharp routes in rib on ce1", + wait, + wait_time=10, + ) + luCommand( + "ce2", + 'vtysh -c "show ip route summ" | grep "sharp" | cut -d " " -f 33', + str(num), + "wait", + "See all sharp routes in rib on ce2", + wait, + wait_time=10, + ) + rtrs = ["ce1", "ce2", "ce3"] for rtr in rtrs: luCommand( diff --git a/tests/topotests/bgp_lu_explicitnull/r1/bgpd.conf b/tests/topotests/bgp_lu_explicitnull/r1/bgpd.conf new file mode 100644 index 0000000000..a31439c984 --- /dev/null +++ b/tests/topotests/bgp_lu_explicitnull/r1/bgpd.conf @@ -0,0 +1,15 @@ +router bgp 65500 + bgp router-id 192.0.2.1 + no bgp ebgp-requires-policy + bgp labeled-unicast explicit-null + neighbor 192.0.2.2 remote-as 65501 +! + address-family ipv4 unicast + no neighbor 192.0.2.2 activate + network 192.168.2.1/32 + exit-address-family + ! + address-family ipv4 labeled-unicast + neighbor 192.0.2.2 activate + exit-address-family +! diff --git a/tests/topotests/bgp_lu_explicitnull/r1/zebra.conf b/tests/topotests/bgp_lu_explicitnull/r1/zebra.conf new file mode 100644 index 0000000000..b84574891e --- /dev/null +++ b/tests/topotests/bgp_lu_explicitnull/r1/zebra.conf @@ -0,0 +1,6 @@ +interface r1-eth0 + ip address 192.0.2.1/24 +! +interface r1-eth1 + ip address 192.168.2.1/32 +!
\ No newline at end of file diff --git a/tests/topotests/bgp_lu_explicitnull/r2/bgpd.conf b/tests/topotests/bgp_lu_explicitnull/r2/bgpd.conf new file mode 100644 index 0000000000..41c2b9b6fa --- /dev/null +++ b/tests/topotests/bgp_lu_explicitnull/r2/bgpd.conf @@ -0,0 +1,15 @@ +router bgp 65501 + bgp router-id 192.0.2.2 + no bgp ebgp-requires-policy + bgp labeled-unicast explicit-null + neighbor 192.0.2.1 remote-as 65500 +! + address-family ipv4 unicast + no neighbor 192.0.2.1 activate + network 192.168.2.2/32 + exit-address-family + ! + address-family ipv4 labeled-unicast + neighbor 192.0.2.1 activate + exit-address-family +! diff --git a/tests/topotests/bgp_lu_explicitnull/r2/zebra.conf b/tests/topotests/bgp_lu_explicitnull/r2/zebra.conf new file mode 100644 index 0000000000..9a639610c1 --- /dev/null +++ b/tests/topotests/bgp_lu_explicitnull/r2/zebra.conf @@ -0,0 +1,6 @@ +interface r2-eth0 + ip address 192.0.2.2/24 +! +interface r2-eth1 + ip address 192.168.2.2/32 +! diff --git a/tests/topotests/bgp_lu_explicitnull/test_bgp_lu_explicitnull.py b/tests/topotests/bgp_lu_explicitnull/test_bgp_lu_explicitnull.py new file mode 100644 index 0000000000..0656e1ed41 --- /dev/null +++ b/tests/topotests/bgp_lu_explicitnull/test_bgp_lu_explicitnull.py @@ -0,0 +1,196 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC +# +# test_bgp_explicitnull.py +# +# Part of NetDEF Topology Tests +# +# Copyright 2023 by 6WIND S.A. +# + +""" +test_bgp_lu_explicitnull.py: Test BGP LU label allocation +""" + +import os +import sys +import json +import functools +import pytest + +# 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 + + +pytestmark = [pytest.mark.bgpd] + + +# Basic scenario for BGP-LU. Nodes are directly connected. +# The 192.168.2.2/32 prefix is advertised from r2 to r1 +# The explicit-null label should be used +# The 192.168.2.1/32 prefix is advertised from r1 to r2 +# The explicit-null label should be used +# Traffic from 192.168.2.1 to 192.168.2.2 should use explicit-null label +# +# AS65500 BGP-LU AS65501 +# +-----+ +-----+ +# | |.1 .2| | +# | 1 +----------------+ 2 + 192.168.0.2/32 +# | | 192.0.2.0/24 | | +# +-----+ +-----+ + + +def build_topo(tgen): + "Build function" + + # Create routers + tgen.add_router("r1") + tgen.add_router("r2") + + # r1-r2 + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + # r1 + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r1"]) + + # r2 + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + "Sets up the pytest environment" + # This function initiates the topology build with Topogen... + tgen = Topogen(build_topo, mod.__name__) + + # Skip if no mpls support + if not tgen.hasmpls: + logger.info("MPLS is not available, skipping test") + pytest.skip("MPLS is not available, skipping") + return + + # ... and here it calls Mininet initialization functions. + tgen.start_topology() + + # This is a sample of configuration loading. + router_list = tgen.routers() + + # Enable mpls input for routers, so we can ping + sval = "net.mpls.conf.{}.input" + topotest.sysctl_assure(router_list["r2"], sval.format("r2-eth0"), 1) + topotest.sysctl_assure(router_list["r1"], sval.format("r1-eth0"), 1) + + # For all registred routers, load the zebra configuration file + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + # After loading the configurations, this function loads configured daemons. + tgen.start_router() + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +def check_show_ip_label_prefix_found(router, ipversion, prefix, label): + output = json.loads( + router.vtysh_cmd("show {} route {} json".format(ipversion, prefix)) + ) + expected = { + prefix: [ + {"prefix": prefix, "nexthops": [{"fib": True, "labels": [int(label)]}]} + ] + } + return topotest.json_cmp(output, expected) + + +def test_converge_bgplu(): + "Wait for protocol convergence" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # tgen.mininet_cli(); + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + # Check r1 gets prefix 192.168.2.2/32 + test_func = functools.partial( + check_show_ip_label_prefix_found, + tgen.gears["r1"], + "ip", + "192.168.2.2/32", + "0", + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "r1, prefix 192.168.2.2/32 from r2 not present" + + # Check r2 gets prefix 192.168.2.1/32 + test_func = functools.partial( + check_show_ip_label_prefix_found, + tgen.gears["r2"], + "ip", + "192.168.2.1/32", + "0", + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "r2, prefix 192.168.2.1/32 from r1 not present" + + +def test_traffic_connectivity(): + "Wait for protocol convergence" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def _check_ping(name, dest_addr, src_addr): + tgen = get_topogen() + output = tgen.gears[name].run( + "ping {} -c 1 -w 1 -I {}".format(dest_addr, src_addr) + ) + logger.info(output) + if " 0% packet loss" not in output: + return True + + logger.info("r1, check ping 192.168.2.2 from 192.168.2.1 is OK") + tgen = get_topogen() + func = functools.partial(_check_ping, "r1", "192.168.2.2", "192.168.2.1") + # tgen.mininet_cli() + success, result = topotest.run_and_expect(func, None, count=10, wait=0.5) + assert result is None, "r1, ping to 192.168.2.2 from 192.168.2.1 fails" + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_route_origin_parser/pe1/bgpd.conf b/tests/topotests/bgp_route_origin_parser/pe1/bgpd.conf new file mode 100644 index 0000000000..1929dfa69b --- /dev/null +++ b/tests/topotests/bgp_route_origin_parser/pe1/bgpd.conf @@ -0,0 +1,2 @@ +router bgp 65001 + neighbor 192.168.2.1 remote-as external diff --git a/tests/topotests/bgp_route_origin_parser/test_bgp_route_origin_parser.py b/tests/topotests/bgp_route_origin_parser/test_bgp_route_origin_parser.py new file mode 100644 index 0000000000..673efc2c73 --- /dev/null +++ b/tests/topotests/bgp_route_origin_parser/test_bgp_route_origin_parser.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: GPL-2.0-or-later +# +# April 03 2023, Trey Aspelund <taspelund@nvidia.com> +# +# Copyright (C) 2023 NVIDIA Corporation +# +# Test if the CLI parser for RT/SoO ecoms correctly +# constrain user input to valid 4-byte ASN values. +# + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.common_config import step + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + tgen.add_router("pe1") + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + pe1 = tgen.gears["pe1"] + pe1.load_config(TopoRouter.RD_BGP, os.path.join(CWD, "pe1/bgpd.conf")) + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_route_origin_parser(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + pe1 = tgen.gears["pe1"] + + def _invalid_soo_accepted(): + pe1.vtysh_cmd( + """ + configure terminal + router bgp 65001 + address-family ipv4 unicast + neighbor 192.168.2.1 soo 4294967296:65 + """ + ) + run_cfg = pe1.vtysh_cmd("show run") + return "soo" in run_cfg + + def _max_soo_accepted(): + pe1.vtysh_cmd( + """ + configure terminal + router bgp 65001 + address-family ipv4 unicast + neighbor 192.168.2.1 soo 4294967295:65 + """ + ) + run_cfg = pe1.vtysh_cmd("show run") + return "soo 4294967295:65" in run_cfg + + def _invalid_rt_accepted(): + pe1.vtysh_cmd( + """ + configure terminal + router bgp 65001 + address-family ipv4 unicast + rt vpn both 4294967296:65 + """ + ) + run_cfg = pe1.vtysh_cmd("show run") + return "rt vpn" in run_cfg + + def _max_rt_accepted(): + pe1.vtysh_cmd( + """ + configure terminal + router bgp 65001 + address-family ipv4 unicast + rt vpn both 4294967295:65 + """ + ) + run_cfg = pe1.vtysh_cmd("show run") + return "rt vpn both 4294967295:65" in run_cfg + + step( + "Configure invalid 4-byte value SoO (4294967296:65), this should not be accepted" + ) + test_func = functools.partial(_invalid_soo_accepted) + _, result = topotest.run_and_expect(test_func, False, count=30, wait=0.5) + assert result is False, "invalid 4-byte value of SoO accepted" + + step("Configure max 4-byte value SoO (4294967295:65), this should be accepted") + test_func = functools.partial(_max_soo_accepted) + _, result = topotest.run_and_expect(test_func, True, count=30, wait=0.5) + assert result is True, "max 4-byte value of SoO not accepted" + + step( + "Configure invalid 4-byte value RT (4294967296:65), this should not be accepted" + ) + test_func = functools.partial(_invalid_rt_accepted) + _, result = topotest.run_and_expect(test_func, False, count=30, wait=0.5) + assert result is False, "invalid 4-byte value of RT accepted" + + step("Configure max 4-byte value RT (4294967295:65), this should be accepted") + test_func = functools.partial(_max_rt_accepted) + _, result = topotest.run_and_expect(test_func, True, count=30, wait=0.5) + assert result is True, "max 4-byte value of RT not accepted" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_unique_rid/test_bgp_unique_rid.py b/tests/topotests/bgp_unique_rid/test_bgp_unique_rid.py index 47b2452b81..f89f3378fb 100644 --- a/tests/topotests/bgp_unique_rid/test_bgp_unique_rid.py +++ b/tests/topotests/bgp_unique_rid/test_bgp_unique_rid.py @@ -847,8 +847,6 @@ def test_bgp_unique_rid_chaos4_p2(): for intf in topo["routers"][rtr]["links"].keys(): topo1["routers"][rtr]["links"][intf].pop("ipv4") topo1["routers"][rtr]["links"][intf].pop("ipv6") - if intf is "lo": - topo1["routers"][rtr]["links"][intf].pop("ipv4") build_config_from_json(tgen, topo1, save_bkup=False) diff --git a/tests/topotests/config_timing/test_config_timing.py b/tests/topotests/config_timing/test_config_timing.py index 8de6f9bf70..5c1b97262c 100644 --- a/tests/topotests/config_timing/test_config_timing.py +++ b/tests/topotests/config_timing/test_config_timing.py @@ -133,7 +133,7 @@ def test_static_timing(): delta = (datetime.datetime.now() - tstamp).total_seconds() tot_delta += delta - router.logger.info( + router.logger.debug( "\nvtysh command => {}\nvtysh output <= {}\nin {}s".format( load_command, output, delta ) @@ -152,7 +152,7 @@ def test_static_timing(): # Number of static routes router = tgen.gears["r1"] - output = router.run("vtysh -h | grep address-sanitizer") + output = router.net.cmd_legacy("vtysh -h | grep address-sanitizer", warn=False) if output == "": logger.info("No Address Sanitizer, generating 10000 routes") prefix_count = 10000 diff --git a/tests/topotests/conftest.py b/tests/topotests/conftest.py index df5d066023..4224b037b0 100755 --- a/tests/topotests/conftest.py +++ b/tests/topotests/conftest.py @@ -7,21 +7,23 @@ import glob import os import pdb import re +import resource import subprocess import sys import time -import resource import pytest + import lib.fixtures from lib import topolog -from lib.micronet import Commander, proc_error -from lib.micronet_cli import cli -from lib.micronet_compat import Mininet, cleanup_current, cleanup_previous +from lib.micronet_compat import Mininet from lib.topogen import diagnose_env, get_topogen from lib.topolog import logger from lib.topotest import g_extra_config as topotest_extra_config from lib.topotest import json_cmp_result +from munet.base import Commander, proc_error +from munet.cleanup import cleanup_current, cleanup_previous +from munet import cli def pytest_addoption(parser): @@ -501,7 +503,7 @@ def pytest_runtest_makereport(item, call): # Really would like something better than using this global here. # Not all tests use topogen though so get_topogen() won't work. if Mininet.g_mnet_inst: - cli(Mininet.g_mnet_inst, title=title, background=False) + cli.cli(Mininet.g_mnet_inst, title=title, background=False) else: logger.error("Could not launch CLI b/c no mininet exists yet") @@ -515,7 +517,7 @@ def pytest_runtest_makereport(item, call): user = user.strip() if user == "cli": - cli(Mininet.g_mnet_inst) + cli.cli(Mininet.g_mnet_inst) elif user == "pdb": pdb.set_trace() # pylint: disable=forgotten-debug-statement elif user: diff --git a/tests/topotests/isis_advertise_high_metrics/test_isis_advertise_high_metrics.py b/tests/topotests/isis_advertise_high_metrics/test_isis_advertise_high_metrics.py index 5eef879e3f..ada8c0f5fb 100644 --- a/tests/topotests/isis_advertise_high_metrics/test_isis_advertise_high_metrics.py +++ b/tests/topotests/isis_advertise_high_metrics/test_isis_advertise_high_metrics.py @@ -121,14 +121,14 @@ def _check_interface_metrics(router, expected_metrics): tgen = get_topogen() router = tgen.gears[router] logger.info(f"check_interface_metrics {router}") - isis_interface_output = router.vtysh_cmd( - "show isis interface detail json" - ) + isis_interface_output = router.vtysh_cmd("show isis interface detail json") intf_json = json.loads(isis_interface_output) for i in range(len(expected_metrics)): - metric = intf_json["areas"][0]["circuits"][i]["interface"]["levels"][0]["metric"] - if (metric != expected_metrics[i]): + metric = intf_json["areas"][0]["circuits"][i]["interface"]["levels"][0][ + "metric" + ] + if metric != expected_metrics[i]: intf_name = intf_json["areas"][0]["circuits"][i]["interface"]["name"] return "{} with expected metric {} on {} got {}".format( router.name, expected_metrics[i], intf_name, metric @@ -139,9 +139,7 @@ def _check_interface_metrics(router, expected_metrics): def check_interface_metrics(router, expected_metrics): "Verfiy metrics on router's isis interfaces" - assertmsg = _check_interface_metrics( - router, expected_metrics - ) + assertmsg = _check_interface_metrics(router, expected_metrics) assert assertmsg is True, assertmsg @@ -151,9 +149,7 @@ def _check_lsp_metrics(router, lsp, expected_metrics): tgen = get_topogen() router = tgen.gears[router] logger.info(f"check_lsp_metrics {router}") - isis_lsp_output = router.vtysh_cmd( - "show isis database detail {}".format(lsp) - ) + isis_lsp_output = router.vtysh_cmd("show isis database detail {}".format(lsp)) metrics_list = [int(i) for i in re.findall(r"Metric: (\d+)", isis_lsp_output)] if len(metrics_list) == 0: @@ -170,9 +166,7 @@ def _check_lsp_metrics(router, lsp, expected_metrics): def check_lsp_metrics(router, lsp, expected_metrics): "Verfiy metrics on router's lsp" - assertmsg = _check_lsp_metrics( - router, lsp, expected_metrics - ) + assertmsg = _check_lsp_metrics(router, lsp, expected_metrics) assert assertmsg is True, assertmsg @@ -183,14 +177,12 @@ def _check_ip_route(router, destination, expected_interface): tgen = get_topogen() router = tgen.gears[router] logger.info(f"check_ip_route {router}") - route_output = router.vtysh_cmd( - "show ip route {} json".format(destination) - ) + route_output = router.vtysh_cmd("show ip route {} json".format(destination)) route_json = json.loads(route_output) interface = route_json[destination][0]["nexthops"][0]["interfaceName"] - if (interface != expected_interface): + if interface != expected_interface: return "{} with expected route to {} got {} expected {}".format( router.name, destination, interface, expected_interface ) @@ -201,9 +193,7 @@ def _check_ip_route(router, destination, expected_interface): def check_ip_route(router, destination, expected_interface): "Verfiy IS-IS route" - assertmsg = _check_ip_route( - router, destination, expected_interface - ) + assertmsg = _check_ip_route(router, destination, expected_interface) assert assertmsg is True, assertmsg @@ -216,9 +206,7 @@ def test_isis_daemon_up(): for router in ["r1", "r2", "r3", "r4"]: r = tgen.gears[router] - daemons = r.vtysh_cmd( - "show daemons" - ) + daemons = r.vtysh_cmd("show daemons") assert "isisd" in daemons # Verify initial metric values. @@ -420,9 +408,9 @@ def test_isis_advertise_high_metrics_route(): Topology: r2 - / \ + // \\ r1 r4 - \ / + \\ // r3 Devices are configured with preferred route between r1 and r4: diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo1/rt1/isisd.conf new file mode 100644 index 0000000000..5503baa58c --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/isisd.conf @@ -0,0 +1,96 @@ +password 1 +hostname rt1 +log file isisd.log +! +!debug northbound +!debug isis events +!debug isis spf-events +!debug isis route-events +!debug isis sr-events +!debug isis lsp-gen +! +affinity-map red bit-position 0 +affinity-map blue bit-position 1 +affinity-map green bit-position 2 +affinity-map yellow bit-position 3 +affinity-map orange bit-position 4 +! +interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive +! +interface eth-rt2 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 + isis network point-to-point +! +interface eth-rt3 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 + isis network point-to-point +! +router isis 1 + lsp-gen-interval 2 + net 49.0000.0000.0000.0001.00 + is-type level-1 + topology ipv6-unicast + mpls-te on + ! + flex-algo 201 + dataplane sr-mpls + advertise-definition + affinity exclude-any red + ! + flex-algo 202 + dataplane sr-mpls + advertise-definition + affinity exclude-any blue + ! + flex-algo 203 + dataplane sr-mpls + advertise-definition + affinity exclude-any green + ! + flex-algo 204 + dataplane sr-mpls + advertise-definition + affinity include-any blue green + ! + flex-algo 205 + dataplane sr-mpls + advertise-definition + affinity include-any red green + ! + flex-algo 206 + dataplane sr-mpls + advertise-definition + affinity include-any red blue + ! + flex-algo 207 + dataplane sr-mpls + advertise-definition + affinity include-all yellow orange + ! + segment-routing on + segment-routing global-block 20000 23999 + segment-routing node-msd 8 + segment-routing prefix 1.1.1.1/32 index 1 + segment-routing prefix 1.1.1.1/32 algorithm 201 index 101 + segment-routing prefix 1.1.1.1/32 algorithm 202 index 201 + segment-routing prefix 1.1.1.1/32 algorithm 203 index 301 + segment-routing prefix 1.1.1.1/32 algorithm 204 index 401 + segment-routing prefix 1.1.1.1/32 algorithm 205 index 501 + segment-routing prefix 1.1.1.1/32 algorithm 206 index 601 + segment-routing prefix 1.1.1.1/32 algorithm 207 index 701 + segment-routing prefix 2001:db8:1000::1/128 index 1001 + segment-routing prefix 2001:db8:1000::1/128 algorithm 201 index 1101 + segment-routing prefix 2001:db8:1000::1/128 algorithm 202 index 1201 + segment-routing prefix 2001:db8:1000::1/128 algorithm 203 index 1301 + segment-routing prefix 2001:db8:1000::1/128 algorithm 204 index 1401 + segment-routing prefix 2001:db8:1000::1/128 algorithm 205 index 1501 + segment-routing prefix 2001:db8:1000::1/128 algorithm 206 index 1601 + segment-routing prefix 2001:db8:1000::1/128 algorithm 207 index 1701 +! diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step1/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step1/show_isis_flex_algo.ref new file mode 100644 index 0000000000..750abc1fa3 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step1/show_isis_flex_algo.ref @@ -0,0 +1,125 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set
\ No newline at end of file diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step1/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step1/show_mpls_table.ref new file mode 100644 index 0000000000..2b16c53132 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step1/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20102, + "outLabelStack":[ + 20102 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20303, + "outLabelStack":[ + 20303 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20402, + "outLabelStack":[ + 20402 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20603, + "outLabelStack":[ + 20603 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20702, + "outLabelStack":[ + 20702 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21102, + "outLabelStack":[ + 21102 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21302":{ + "inLabel":21302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21303":{ + "inLabel":21303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21303, + "outLabelStack":[ + 21303 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21402, + "outLabelStack":[ + 21402 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21603, + "outLabelStack":[ + 21603 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21702, + "outLabelStack":[ + 21702 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step10/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step10/show_isis_flex_algo.ref new file mode 100644 index 0000000000..9421990316 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step10/show_isis_flex_algo.ref @@ -0,0 +1,126 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set + diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step10/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step10/show_mpls_table.ref new file mode 100644 index 0000000000..2b16c53132 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step10/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20102, + "outLabelStack":[ + 20102 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20303, + "outLabelStack":[ + 20303 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20402, + "outLabelStack":[ + 20402 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20603, + "outLabelStack":[ + 20603 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20702, + "outLabelStack":[ + 20702 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21102, + "outLabelStack":[ + 21102 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21302":{ + "inLabel":21302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21303":{ + "inLabel":21303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21303, + "outLabelStack":[ + 21303 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21402, + "outLabelStack":[ + 21402 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21603, + "outLabelStack":[ + 21603 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21702, + "outLabelStack":[ + 21702 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step11/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step11/show_isis_flex_algo.ref new file mode 100644 index 0000000000..9421990316 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step11/show_isis_flex_algo.ref @@ -0,0 +1,126 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set + diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step11/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step11/show_mpls_table.ref new file mode 100644 index 0000000000..2b16c53132 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step11/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20102, + "outLabelStack":[ + 20102 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20303, + "outLabelStack":[ + 20303 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20402, + "outLabelStack":[ + 20402 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20603, + "outLabelStack":[ + 20603 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20702, + "outLabelStack":[ + 20702 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21102, + "outLabelStack":[ + 21102 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21302":{ + "inLabel":21302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21303":{ + "inLabel":21303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21303, + "outLabelStack":[ + 21303 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21402, + "outLabelStack":[ + 21402 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21603, + "outLabelStack":[ + 21603 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21702, + "outLabelStack":[ + 21702 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step2/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step2/show_isis_flex_algo.ref new file mode 100644 index 0000000000..9421990316 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step2/show_isis_flex_algo.ref @@ -0,0 +1,126 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set + diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step2/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step2/show_mpls_table.ref new file mode 100644 index 0000000000..2b16c53132 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step2/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20102, + "outLabelStack":[ + 20102 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20303, + "outLabelStack":[ + 20303 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20402, + "outLabelStack":[ + 20402 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20603, + "outLabelStack":[ + 20603 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20702, + "outLabelStack":[ + 20702 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21102, + "outLabelStack":[ + 21102 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21302":{ + "inLabel":21302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21303":{ + "inLabel":21303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21303, + "outLabelStack":[ + 21303 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21402, + "outLabelStack":[ + 21402 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21603, + "outLabelStack":[ + 21603 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21702, + "outLabelStack":[ + 21702 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step3/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step3/show_isis_flex_algo.ref new file mode 100644 index 0000000000..13a96161af --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step3/show_isis_flex_algo.ref @@ -0,0 +1,115 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: Not found + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set + diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step3/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step3/show_mpls_table.ref new file mode 100644 index 0000000000..d311e924d6 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step3/show_mpls_table.ref @@ -0,0 +1,450 @@ +{ + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20102, + "outLabelStack":[ + 20102 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20402, + "outLabelStack":[ + 20402 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20603, + "outLabelStack":[ + 20603 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20702, + "outLabelStack":[ + 20702 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21102, + "outLabelStack":[ + 21102 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21402, + "outLabelStack":[ + 21402 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21603, + "outLabelStack":[ + 21603 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21702, + "outLabelStack":[ + 21702 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step4/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step4/show_isis_flex_algo.ref new file mode 100644 index 0000000000..9421990316 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step4/show_isis_flex_algo.ref @@ -0,0 +1,126 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set + diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step4/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step4/show_mpls_table.ref new file mode 100644 index 0000000000..2b16c53132 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step4/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20102, + "outLabelStack":[ + 20102 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20303, + "outLabelStack":[ + 20303 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20402, + "outLabelStack":[ + 20402 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20603, + "outLabelStack":[ + 20603 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20702, + "outLabelStack":[ + 20702 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21102, + "outLabelStack":[ + 21102 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21302":{ + "inLabel":21302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21303":{ + "inLabel":21303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21303, + "outLabelStack":[ + 21303 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21402, + "outLabelStack":[ + 21402 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21603, + "outLabelStack":[ + 21603 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21702, + "outLabelStack":[ + 21702 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step5/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step5/show_isis_flex_algo.ref new file mode 100644 index 0000000000..9421990316 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step5/show_isis_flex_algo.ref @@ -0,0 +1,126 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set + diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step5/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step5/show_mpls_table.ref new file mode 100644 index 0000000000..2b16c53132 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step5/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20102, + "outLabelStack":[ + 20102 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20303, + "outLabelStack":[ + 20303 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20402, + "outLabelStack":[ + 20402 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20603, + "outLabelStack":[ + 20603 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20702, + "outLabelStack":[ + 20702 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21102, + "outLabelStack":[ + 21102 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21302":{ + "inLabel":21302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21303":{ + "inLabel":21303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21303, + "outLabelStack":[ + 21303 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21402, + "outLabelStack":[ + 21402 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21603, + "outLabelStack":[ + 21603 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21702, + "outLabelStack":[ + 21702 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step6/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step6/show_isis_flex_algo.ref new file mode 100644 index 0000000000..ce31766da5 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step6/show_isis_flex_algo.ref @@ -0,0 +1,112 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: None + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set + diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step6/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step6/show_mpls_table.ref new file mode 100644 index 0000000000..d311e924d6 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step6/show_mpls_table.ref @@ -0,0 +1,450 @@ +{ + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20102, + "outLabelStack":[ + 20102 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20402, + "outLabelStack":[ + 20402 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20603, + "outLabelStack":[ + 20603 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20702, + "outLabelStack":[ + 20702 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21102, + "outLabelStack":[ + 21102 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21402, + "outLabelStack":[ + 21402 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21603, + "outLabelStack":[ + 21603 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21702, + "outLabelStack":[ + 21702 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step7/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step7/show_isis_flex_algo.ref new file mode 100644 index 0000000000..e56e5af2f9 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step7/show_isis_flex_algo.ref @@ -0,0 +1,108 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set + diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step7/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step7/show_mpls_table.ref new file mode 100644 index 0000000000..d311e924d6 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step7/show_mpls_table.ref @@ -0,0 +1,450 @@ +{ + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20102, + "outLabelStack":[ + 20102 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20402, + "outLabelStack":[ + 20402 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20603, + "outLabelStack":[ + 20603 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20702, + "outLabelStack":[ + 20702 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21102, + "outLabelStack":[ + 21102 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21402, + "outLabelStack":[ + 21402 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21603, + "outLabelStack":[ + 21603 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21702, + "outLabelStack":[ + 21702 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step8/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step8/show_isis_flex_algo.ref new file mode 100644 index 0000000000..9421990316 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step8/show_isis_flex_algo.ref @@ -0,0 +1,126 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set + diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step8/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step8/show_mpls_table.ref new file mode 100644 index 0000000000..2b16c53132 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step8/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20102, + "outLabelStack":[ + 20102 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20303, + "outLabelStack":[ + 20303 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20402, + "outLabelStack":[ + 20402 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20603, + "outLabelStack":[ + 20603 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20702, + "outLabelStack":[ + 20702 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21102, + "outLabelStack":[ + 21102 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21302":{ + "inLabel":21302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21303":{ + "inLabel":21303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21303, + "outLabelStack":[ + 21303 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21402, + "outLabelStack":[ + 21402 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21603, + "outLabelStack":[ + 21603 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21702, + "outLabelStack":[ + 21702 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step9/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step9/show_isis_flex_algo.ref new file mode 100644 index 0000000000..9421990316 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step9/show_isis_flex_algo.ref @@ -0,0 +1,126 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set + diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step9/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step9/show_mpls_table.ref new file mode 100644 index 0000000000..2b16c53132 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step9/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20102, + "outLabelStack":[ + 20102 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20303, + "outLabelStack":[ + 20303 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20402, + "outLabelStack":[ + 20402 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20603, + "outLabelStack":[ + 20603 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20702, + "outLabelStack":[ + 20702 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21102, + "outLabelStack":[ + 21102 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21302":{ + "inLabel":21302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21303":{ + "inLabel":21303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21303, + "outLabelStack":[ + 21303 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21402, + "outLabelStack":[ + 21402 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21603, + "outLabelStack":[ + 21603 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21702, + "outLabelStack":[ + 21702 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo1/rt1/zebra.conf new file mode 100644 index 0000000000..5140eda73a --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/zebra.conf @@ -0,0 +1,39 @@ +log file zebra.log +! +hostname rt1 +! +log stdout notifications +log monitor notifications +log commands +! +!debug zebra packet +!debug zebra dplane +!debug zebra kernel +! +affinity-map red bit-position 0 +affinity-map blue bit-position 1 +affinity-map green bit-position 2 +affinity-map yellow bit-position 3 +affinity-map orange bit-position 4 +! +interface lo + ip address 1.1.1.1/32 + ipv6 address 2001:db8:1000::1/128 +! +interface eth-rt2 + ip address 10.12.0.1/24 + link-params + affinity red + exit-link-params +! +interface eth-rt3 + ip address 10.13.0.1/24 + link-params + affinity green yellow orange + exit-link-params +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo1/rt2/isisd.conf new file mode 100644 index 0000000000..8655e7434a --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/isisd.conf @@ -0,0 +1,96 @@ +password 1 +hostname rt2 +log file isisd.log +! +!debug northbound +!debug isis events +!debug isis route-events +!debug isis spf-events +!debug isis sr-events +!debug isis lsp-gen +! +affinity-map red bit-position 0 +affinity-map blue bit-position 1 +affinity-map green bit-position 2 +affinity-map yellow bit-position 3 +affinity-map orange bit-position 4 +! +interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive +! +interface eth-rt1 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 + isis network point-to-point +! +interface eth-rt3 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 + isis network point-to-point +! +router isis 1 + lsp-gen-interval 2 + net 49.0000.0000.0000.0002.00 + is-type level-1 + topology ipv6-unicast + mpls-te on + ! + flex-algo 201 + dataplane sr-mpls + advertise-definition + affinity exclude-any red + ! + flex-algo 202 + dataplane sr-mpls + advertise-definition + affinity exclude-any blue + ! + flex-algo 203 + dataplane sr-mpls + advertise-definition + affinity exclude-any green + ! + flex-algo 204 + dataplane sr-mpls + advertise-definition + affinity include-any blue green + ! + flex-algo 205 + dataplane sr-mpls + advertise-definition + affinity include-any red green + ! + flex-algo 206 + dataplane sr-mpls + advertise-definition + affinity include-any red blue + ! + flex-algo 207 + dataplane sr-mpls + advertise-definition + affinity include-all yellow orange + ! + segment-routing on + segment-routing global-block 20000 23999 + segment-routing node-msd 8 + segment-routing prefix 2.2.2.2/32 index 2 + segment-routing prefix 2.2.2.2/32 algorithm 201 index 102 + segment-routing prefix 2.2.2.2/32 algorithm 202 index 202 + segment-routing prefix 2.2.2.2/32 algorithm 203 index 302 + segment-routing prefix 2.2.2.2/32 algorithm 204 index 402 + segment-routing prefix 2.2.2.2/32 algorithm 205 index 502 + segment-routing prefix 2.2.2.2/32 algorithm 206 index 602 + segment-routing prefix 2.2.2.2/32 algorithm 207 index 702 + segment-routing prefix 2001:db8:1000::2/128 index 1002 + segment-routing prefix 2001:db8:1000::2/128 algorithm 201 index 1102 + segment-routing prefix 2001:db8:1000::2/128 algorithm 202 index 1202 + segment-routing prefix 2001:db8:1000::2/128 algorithm 203 index 1302 + segment-routing prefix 2001:db8:1000::2/128 algorithm 204 index 1402 + segment-routing prefix 2001:db8:1000::2/128 algorithm 205 index 1502 + segment-routing prefix 2001:db8:1000::2/128 algorithm 206 index 1602 + segment-routing prefix 2001:db8:1000::2/128 algorithm 207 index 1702 +! diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step1/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step1/show_isis_flex_algo.ref new file mode 100644 index 0000000000..defa3efa6b --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step1/show_isis_flex_algo.ref @@ -0,0 +1,125 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step1/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step1/show_mpls_table.ref new file mode 100644 index 0000000000..099045a14d --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step1/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20101, + "outLabelStack":[ + 20101 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20203, + "outLabelStack":[ + 20203 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20401, + "outLabelStack":[ + 20401 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20503, + "outLabelStack":[ + 20503 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20701, + "outLabelStack":[ + 20701 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21101, + "outLabelStack":[ + 21101 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21203, + "outLabelStack":[ + 21203 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21301":{ + "inLabel":21301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21303":{ + "inLabel":21303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21401, + "outLabelStack":[ + 21401 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21503, + "outLabelStack":[ + 21503 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21701, + "outLabelStack":[ + 21701 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step10/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step10/show_isis_flex_algo.ref new file mode 100644 index 0000000000..defa3efa6b --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step10/show_isis_flex_algo.ref @@ -0,0 +1,125 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step10/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step10/show_mpls_table.ref new file mode 100644 index 0000000000..099045a14d --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step10/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20101, + "outLabelStack":[ + 20101 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20203, + "outLabelStack":[ + 20203 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20401, + "outLabelStack":[ + 20401 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20503, + "outLabelStack":[ + 20503 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20701, + "outLabelStack":[ + 20701 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21101, + "outLabelStack":[ + 21101 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21203, + "outLabelStack":[ + 21203 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21301":{ + "inLabel":21301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21303":{ + "inLabel":21303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21401, + "outLabelStack":[ + 21401 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21503, + "outLabelStack":[ + 21503 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21701, + "outLabelStack":[ + 21701 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step11/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step11/show_isis_flex_algo.ref new file mode 100644 index 0000000000..defa3efa6b --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step11/show_isis_flex_algo.ref @@ -0,0 +1,125 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step11/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step11/show_mpls_table.ref new file mode 100644 index 0000000000..0ed0eb31ee --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step11/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20101, + "outLabelStack":[ + 20101 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20203, + "outLabelStack":[ + 20203 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20311":{ + "inLabel":20311, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20401, + "outLabelStack":[ + 20401 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20503, + "outLabelStack":[ + 20503 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20701, + "outLabelStack":[ + 20701 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21101, + "outLabelStack":[ + 21101 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21203, + "outLabelStack":[ + 21203 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21303":{ + "inLabel":21303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21311":{ + "inLabel":21311, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21401, + "outLabelStack":[ + 21401 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21503, + "outLabelStack":[ + 21503 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21701, + "outLabelStack":[ + 21701 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step2/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step2/show_isis_flex_algo.ref new file mode 100644 index 0000000000..defa3efa6b --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step2/show_isis_flex_algo.ref @@ -0,0 +1,125 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step2/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step2/show_mpls_table.ref new file mode 100644 index 0000000000..099045a14d --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step2/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20101, + "outLabelStack":[ + 20101 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20203, + "outLabelStack":[ + 20203 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20401, + "outLabelStack":[ + 20401 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20503, + "outLabelStack":[ + 20503 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20701, + "outLabelStack":[ + 20701 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21101, + "outLabelStack":[ + 21101 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21203, + "outLabelStack":[ + 21203 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21301":{ + "inLabel":21301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21303":{ + "inLabel":21303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21401, + "outLabelStack":[ + 21401 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21503, + "outLabelStack":[ + 21503 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21701, + "outLabelStack":[ + 21701 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step3/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step3/show_isis_flex_algo.ref new file mode 100644 index 0000000000..b10cb70866 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step3/show_isis_flex_algo.ref @@ -0,0 +1,114 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: Not found + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step3/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step3/show_mpls_table.ref new file mode 100644 index 0000000000..8b407387b4 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step3/show_mpls_table.ref @@ -0,0 +1,450 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20101, + "outLabelStack":[ + 20101 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20203, + "outLabelStack":[ + 20203 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20401, + "outLabelStack":[ + 20401 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20503, + "outLabelStack":[ + 20503 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20701, + "outLabelStack":[ + 20701 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21101, + "outLabelStack":[ + 21101 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21203, + "outLabelStack":[ + 21203 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21401, + "outLabelStack":[ + 21401 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21503, + "outLabelStack":[ + 21503 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21701, + "outLabelStack":[ + 21701 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step4/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step4/show_isis_flex_algo.ref new file mode 100644 index 0000000000..defa3efa6b --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step4/show_isis_flex_algo.ref @@ -0,0 +1,125 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step4/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step4/show_mpls_table.ref new file mode 100644 index 0000000000..099045a14d --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step4/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20101, + "outLabelStack":[ + 20101 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20203, + "outLabelStack":[ + 20203 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20401, + "outLabelStack":[ + 20401 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20503, + "outLabelStack":[ + 20503 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20701, + "outLabelStack":[ + 20701 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21101, + "outLabelStack":[ + 21101 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21203, + "outLabelStack":[ + 21203 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21301":{ + "inLabel":21301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21303":{ + "inLabel":21303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21401, + "outLabelStack":[ + 21401 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21503, + "outLabelStack":[ + 21503 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21701, + "outLabelStack":[ + 21701 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step5/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step5/show_isis_flex_algo.ref new file mode 100644 index 0000000000..defa3efa6b --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step5/show_isis_flex_algo.ref @@ -0,0 +1,125 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step5/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step5/show_mpls_table.ref new file mode 100644 index 0000000000..099045a14d --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step5/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20101, + "outLabelStack":[ + 20101 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20203, + "outLabelStack":[ + 20203 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20401, + "outLabelStack":[ + 20401 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20503, + "outLabelStack":[ + 20503 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20701, + "outLabelStack":[ + 20701 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21101, + "outLabelStack":[ + 21101 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21203, + "outLabelStack":[ + 21203 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21301":{ + "inLabel":21301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21303":{ + "inLabel":21303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21401, + "outLabelStack":[ + 21401 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21503, + "outLabelStack":[ + 21503 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21701, + "outLabelStack":[ + 21701 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step6/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step6/show_isis_flex_algo.ref new file mode 100644 index 0000000000..358c4310ed --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step6/show_isis_flex_algo.ref @@ -0,0 +1,111 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: None + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step6/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step6/show_mpls_table.ref new file mode 100644 index 0000000000..8b407387b4 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step6/show_mpls_table.ref @@ -0,0 +1,450 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20101, + "outLabelStack":[ + 20101 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20203, + "outLabelStack":[ + 20203 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20401, + "outLabelStack":[ + 20401 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20503, + "outLabelStack":[ + 20503 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20701, + "outLabelStack":[ + 20701 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21101, + "outLabelStack":[ + 21101 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21203, + "outLabelStack":[ + 21203 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21401, + "outLabelStack":[ + 21401 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21503, + "outLabelStack":[ + 21503 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21701, + "outLabelStack":[ + 21701 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step7/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step7/show_isis_flex_algo.ref new file mode 100644 index 0000000000..04d07e6414 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step7/show_isis_flex_algo.ref @@ -0,0 +1,107 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step7/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step7/show_mpls_table.ref new file mode 100644 index 0000000000..8b407387b4 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step7/show_mpls_table.ref @@ -0,0 +1,450 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20101, + "outLabelStack":[ + 20101 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20203, + "outLabelStack":[ + 20203 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20401, + "outLabelStack":[ + 20401 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20503, + "outLabelStack":[ + 20503 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20701, + "outLabelStack":[ + 20701 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21101, + "outLabelStack":[ + 21101 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21203, + "outLabelStack":[ + 21203 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21401, + "outLabelStack":[ + 21401 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21503, + "outLabelStack":[ + 21503 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21701, + "outLabelStack":[ + 21701 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step8/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step8/show_isis_flex_algo.ref new file mode 100644 index 0000000000..defa3efa6b --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step8/show_isis_flex_algo.ref @@ -0,0 +1,125 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step8/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step8/show_mpls_table.ref new file mode 100644 index 0000000000..099045a14d --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step8/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20101, + "outLabelStack":[ + 20101 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20203, + "outLabelStack":[ + 20203 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20401, + "outLabelStack":[ + 20401 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20503, + "outLabelStack":[ + 20503 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20701, + "outLabelStack":[ + 20701 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21101, + "outLabelStack":[ + 21101 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21203, + "outLabelStack":[ + 21203 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21301":{ + "inLabel":21301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21303":{ + "inLabel":21303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21401, + "outLabelStack":[ + 21401 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21503, + "outLabelStack":[ + 21503 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21701, + "outLabelStack":[ + 21701 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step9/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step9/show_isis_flex_algo.ref new file mode 100644 index 0000000000..defa3efa6b --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step9/show_isis_flex_algo.ref @@ -0,0 +1,125 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step9/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step9/show_mpls_table.ref new file mode 100644 index 0000000000..3db0ebfbf6 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step9/show_mpls_table.ref @@ -0,0 +1,482 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20101, + "outLabelStack":[ + 20101 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20203, + "outLabelStack":[ + 20203 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20401, + "outLabelStack":[ + 20401 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20503, + "outLabelStack":[ + 20503 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20701, + "outLabelStack":[ + 20701 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21101, + "outLabelStack":[ + 21101 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21203, + "outLabelStack":[ + 21203 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21303":{ + "inLabel":21303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21401, + "outLabelStack":[ + 21401 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21503, + "outLabelStack":[ + 21503 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21701, + "outLabelStack":[ + 21701 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo1/rt2/zebra.conf new file mode 100644 index 0000000000..388348fced --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/zebra.conf @@ -0,0 +1,39 @@ +log file zebra.log +! +hostname rt2 +! +log stdout notifications +log monitor notifications +log commands +! +!debug zebra packet +!debug zebra dplane +!debug zebra kernel +! +affinity-map red bit-position 0 +affinity-map blue bit-position 1 +affinity-map green bit-position 2 +affinity-map yellow bit-position 3 +affinity-map orange bit-position 4 +! +interface lo + ip address 2.2.2.2/32 + ipv6 address 2001:db8:1000::2/128 +! +interface eth-rt1 + ip address 10.12.0.2/24 + link-params + affinity red + exit-link-params +! +interface eth-rt3 + ip address 10.23.0.2/24 + link-params + affinity blue yellow orange + exit-link-params +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo1/rt3/isisd.conf new file mode 100644 index 0000000000..d77af81d7c --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/isisd.conf @@ -0,0 +1,76 @@ +password 1 +hostname rt3 +log file isisd.log +! +!debug northbound +!debug isis events +!debug isis route-events +!debug isis spf-events +!debug isis sr-events +!debug isis lsp-gen +! +affinity-map red bit-position 0 +affinity-map blue bit-position 1 +affinity-map green bit-position 2 +affinity-map yellow bit-position 3 +affinity-map orange bit-position 4 +! +interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive +! +interface eth-rt1 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 + isis network point-to-point +! +interface eth-rt2 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 + isis network point-to-point +! +router isis 1 + lsp-gen-interval 2 + net 49.0000.0000.0000.0003.00 + is-type level-1 + topology ipv6-unicast + mpls-te on + ! + flex-algo 201 + dataplane sr-mpls + flex-algo 202 + dataplane sr-mpls + flex-algo 203 + dataplane sr-mpls + flex-algo 204 + dataplane sr-mpls + flex-algo 205 + dataplane sr-mpls + flex-algo 206 + dataplane sr-mpls + flex-algo 207 + dataplane sr-mpls + ! + segment-routing on + segment-routing global-block 20000 23999 + segment-routing node-msd 8 + segment-routing prefix 3.3.3.3/32 index 3 + segment-routing prefix 3.3.3.3/32 algorithm 201 index 103 + segment-routing prefix 3.3.3.3/32 algorithm 202 index 203 + segment-routing prefix 3.3.3.3/32 algorithm 203 index 303 + segment-routing prefix 3.3.3.3/32 algorithm 204 index 403 + segment-routing prefix 3.3.3.3/32 algorithm 205 index 503 + segment-routing prefix 3.3.3.3/32 algorithm 206 index 603 + segment-routing prefix 3.3.3.3/32 algorithm 207 index 703 + segment-routing prefix 2001:db8:1000::3/128 index 1003 + segment-routing prefix 2001:db8:1000::3/128 algorithm 201 index 1103 + segment-routing prefix 2001:db8:1000::3/128 algorithm 202 index 1203 + segment-routing prefix 2001:db8:1000::3/128 algorithm 203 index 1303 + segment-routing prefix 2001:db8:1000::3/128 algorithm 204 index 1403 + segment-routing prefix 2001:db8:1000::3/128 algorithm 205 index 1503 + segment-routing prefix 2001:db8:1000::3/128 algorithm 206 index 1603 + segment-routing prefix 2001:db8:1000::3/128 algorithm 207 index 1703 +! diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step1/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step1/show_isis_flex_algo.ref new file mode 100644 index 0000000000..7954734936 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step1/show_isis_flex_algo.ref @@ -0,0 +1,125 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set
\ No newline at end of file diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step1/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step1/show_mpls_table.ref new file mode 100644 index 0000000000..9ab0c74d0a --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step1/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20202, + "outLabelStack":[ + 20202 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20301, + "outLabelStack":[ + 20301 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20502, + "outLabelStack":[ + 20502 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20601, + "outLabelStack":[ + 20601 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21202, + "outLabelStack":[ + 21202 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21301":{ + "inLabel":21301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21301, + "outLabelStack":[ + 21301 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21302":{ + "inLabel":21302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21502, + "outLabelStack":[ + 21502 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21601, + "outLabelStack":[ + 21601 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step10/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step10/show_isis_flex_algo.ref new file mode 100644 index 0000000000..85182db33a --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step10/show_isis_flex_algo.ref @@ -0,0 +1,125 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step10/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step10/show_mpls_table.ref new file mode 100644 index 0000000000..9ab0c74d0a --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step10/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20202, + "outLabelStack":[ + 20202 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20301, + "outLabelStack":[ + 20301 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20502, + "outLabelStack":[ + 20502 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20601, + "outLabelStack":[ + 20601 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21202, + "outLabelStack":[ + 21202 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21301":{ + "inLabel":21301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21301, + "outLabelStack":[ + 21301 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21302":{ + "inLabel":21302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21502, + "outLabelStack":[ + 21502 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21601, + "outLabelStack":[ + 21601 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step11/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step11/show_isis_flex_algo.ref new file mode 100644 index 0000000000..85182db33a --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step11/show_isis_flex_algo.ref @@ -0,0 +1,125 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step11/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step11/show_mpls_table.ref new file mode 100644 index 0000000000..57755b00e6 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step11/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20202, + "outLabelStack":[ + 20202 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20311":{ + "inLabel":20311, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20311, + "outLabelStack":[ + 20311 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20502, + "outLabelStack":[ + 20502 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20601, + "outLabelStack":[ + 20601 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21202, + "outLabelStack":[ + 21202 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21302":{ + "inLabel":21302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21311":{ + "inLabel":21311, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21311, + "outLabelStack":[ + 21311 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21502, + "outLabelStack":[ + 21502 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21601, + "outLabelStack":[ + 21601 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step2/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step2/show_isis_flex_algo.ref new file mode 100644 index 0000000000..85182db33a --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step2/show_isis_flex_algo.ref @@ -0,0 +1,125 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step2/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step2/show_mpls_table.ref new file mode 100644 index 0000000000..9ab0c74d0a --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step2/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20202, + "outLabelStack":[ + 20202 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20301, + "outLabelStack":[ + 20301 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20502, + "outLabelStack":[ + 20502 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20601, + "outLabelStack":[ + 20601 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21202, + "outLabelStack":[ + 21202 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21301":{ + "inLabel":21301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21301, + "outLabelStack":[ + 21301 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21302":{ + "inLabel":21302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21502, + "outLabelStack":[ + 21502 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21601, + "outLabelStack":[ + 21601 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step3/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step3/show_isis_flex_algo.ref new file mode 100644 index 0000000000..2ccc4f1655 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step3/show_isis_flex_algo.ref @@ -0,0 +1,114 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: Not found + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step3/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step3/show_mpls_table.ref new file mode 100644 index 0000000000..1b57f575b2 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step3/show_mpls_table.ref @@ -0,0 +1,450 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20202, + "outLabelStack":[ + 20202 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20502, + "outLabelStack":[ + 20502 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20601, + "outLabelStack":[ + 20601 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21202, + "outLabelStack":[ + 21202 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21502, + "outLabelStack":[ + 21502 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21601, + "outLabelStack":[ + 21601 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step4/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step4/show_isis_flex_algo.ref new file mode 100644 index 0000000000..85182db33a --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step4/show_isis_flex_algo.ref @@ -0,0 +1,125 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step4/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step4/show_mpls_table.ref new file mode 100644 index 0000000000..9ab0c74d0a --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step4/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20202, + "outLabelStack":[ + 20202 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20301, + "outLabelStack":[ + 20301 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20502, + "outLabelStack":[ + 20502 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20601, + "outLabelStack":[ + 20601 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21202, + "outLabelStack":[ + 21202 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21301":{ + "inLabel":21301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21301, + "outLabelStack":[ + 21301 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21302":{ + "inLabel":21302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21502, + "outLabelStack":[ + 21502 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21601, + "outLabelStack":[ + 21601 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step5/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step5/show_isis_flex_algo.ref new file mode 100644 index 0000000000..85182db33a --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step5/show_isis_flex_algo.ref @@ -0,0 +1,125 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step5/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step5/show_mpls_table.ref new file mode 100644 index 0000000000..9ab0c74d0a --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step5/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20202, + "outLabelStack":[ + 20202 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20301, + "outLabelStack":[ + 20301 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20502, + "outLabelStack":[ + 20502 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20601, + "outLabelStack":[ + 20601 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21202, + "outLabelStack":[ + 21202 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21301":{ + "inLabel":21301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21301, + "outLabelStack":[ + 21301 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21302":{ + "inLabel":21302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21502, + "outLabelStack":[ + 21502 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21601, + "outLabelStack":[ + 21601 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step6/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step6/show_isis_flex_algo.ref new file mode 100644 index 0000000000..903c0f2bed --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step6/show_isis_flex_algo.ref @@ -0,0 +1,111 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: None + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step6/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step6/show_mpls_table.ref new file mode 100644 index 0000000000..1b57f575b2 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step6/show_mpls_table.ref @@ -0,0 +1,450 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20202, + "outLabelStack":[ + 20202 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20502, + "outLabelStack":[ + 20502 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20601, + "outLabelStack":[ + 20601 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21202, + "outLabelStack":[ + 21202 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21502, + "outLabelStack":[ + 21502 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21601, + "outLabelStack":[ + 21601 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step7/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step7/show_isis_flex_algo.ref new file mode 100644 index 0000000000..f36d96579d --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step7/show_isis_flex_algo.ref @@ -0,0 +1,107 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step7/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step7/show_mpls_table.ref new file mode 100644 index 0000000000..1b57f575b2 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step7/show_mpls_table.ref @@ -0,0 +1,450 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20202, + "outLabelStack":[ + 20202 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20502, + "outLabelStack":[ + 20502 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20601, + "outLabelStack":[ + 20601 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21202, + "outLabelStack":[ + 21202 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21502, + "outLabelStack":[ + 21502 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21601, + "outLabelStack":[ + 21601 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step8/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step8/show_isis_flex_algo.ref new file mode 100644 index 0000000000..85182db33a --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step8/show_isis_flex_algo.ref @@ -0,0 +1,125 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step8/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step8/show_mpls_table.ref new file mode 100644 index 0000000000..9ab0c74d0a --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step8/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20202, + "outLabelStack":[ + 20202 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20301, + "outLabelStack":[ + 20301 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20502, + "outLabelStack":[ + 20502 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20601, + "outLabelStack":[ + 20601 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21202, + "outLabelStack":[ + 21202 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21301":{ + "inLabel":21301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21301, + "outLabelStack":[ + 21301 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21302":{ + "inLabel":21302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21502, + "outLabelStack":[ + 21502 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21601, + "outLabelStack":[ + 21601 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step9/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step9/show_isis_flex_algo.ref new file mode 100644 index 0000000000..85182db33a --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step9/show_isis_flex_algo.ref @@ -0,0 +1,125 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step9/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step9/show_mpls_table.ref new file mode 100644 index 0000000000..8ae983a790 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step9/show_mpls_table.ref @@ -0,0 +1,482 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20202, + "outLabelStack":[ + 20202 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20502, + "outLabelStack":[ + 20502 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20601, + "outLabelStack":[ + 20601 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21202, + "outLabelStack":[ + 21202 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21302":{ + "inLabel":21302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21502, + "outLabelStack":[ + 21502 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21601, + "outLabelStack":[ + 21601 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo1/rt3/zebra.conf new file mode 100644 index 0000000000..fb45ee1282 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/zebra.conf @@ -0,0 +1,39 @@ +log file zebra.log +! +hostname rt3 +! +log stdout notifications +log monitor notifications +log commands +! +!debug zebra packet +!debug zebra dplane +!debug zebra kernel +! +affinity-map red bit-position 0 +affinity-map blue bit-position 1 +affinity-map green bit-position 2 +affinity-map yellow bit-position 3 +affinity-map orange bit-position 4 +! +interface lo + ip address 3.3.3.3/32 + ipv6 address 2001:db8:1000::3/128 +! +interface eth-rt1 + ip address 10.13.0.3/24 + link-params + affinity green yellow orange + exit-link-params +! +interface eth-rt2 + ip address 10.23.0.3/24 + link-params + affinity blue yellow orange + exit-link-params +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/isis_sr_flex_algo_topo1/test_isis_sr_flex_algo_topo1.py b/tests/topotests/isis_sr_flex_algo_topo1/test_isis_sr_flex_algo_topo1.py new file mode 100755 index 0000000000..85600beb0e --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/test_isis_sr_flex_algo_topo1.py @@ -0,0 +1,583 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Part of NetDEF Topology Tests +# +# Copyright 2021 by LINE Corporation, Hiroki Shirokura <hiroki.shirokura@linecorp.com> +# Copyright 2023 6WIND S.A. + +""" +test_isis_sr_flex_algo_topo1.py: + +[+] Flex-Algos 201 exclude red +[+] Flex-Algos 202 exclude blue +[+] Flex-Algos 203 exclude green +[+] Flex-Algos 204 include-any blue green +[+] Flex-Algos 205 include-any red green +[+] Flex-Algos 206 include-any red blue +[+] Flex-Algos 207 include-all yellow orange + + +--------+ 10.12.0.0/24 +--------+ + | | red | | + | RT1 |----------------| RT2 | + | | | | + +--------+ +--------+ + 10.13.0.0/24 \\ / 10.23.0.0/24 + green \\ / blue + yellow \\ / yellow + orange +--------+ orange + | | + | RT3 | + | | + +--------+ +""" + +import os +import sys +import pytest +import json +import tempfile +from functools import partial + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + + +pytestmark = [pytest.mark.isisd] + +# Global multi-dimensional dictionary containing all expected outputs +outputs = {} + + +def build_topo(tgen): + "Build function" + + def connect_routers(tgen, left_idx, right_idx): + left = "rt{}".format(left_idx) + right = "rt{}".format(right_idx) + switch = tgen.add_switch("s-{}-{}".format(left, right)) + switch.add_link(tgen.gears[left], nodeif="eth-{}".format(right)) + switch.add_link(tgen.gears[right], nodeif="eth-{}".format(left)) + l_addr = "52:54:00:{}:{}:{}".format(left_idx, right_idx, left_idx) + tgen.gears[left].run("ip link set eth-{} down".format(right)) + tgen.gears[left].run("ip link set eth-{} address {}".format(right, l_addr)) + tgen.gears[left].run("ip link set eth-{} up".format(right)) + r_addr = "52:54:00:{}:{}:{}".format(left_idx, right_idx, right_idx) + tgen.gears[right].run("ip link set eth-{} down".format(left)) + tgen.gears[right].run("ip link set eth-{} address {}".format(left, r_addr)) + tgen.gears[right].run("ip link set eth-{} up".format(left)) + + tgen.add_router("rt1") + tgen.add_router("rt2") + tgen.add_router("rt3") + connect_routers(tgen, 1, 2) + connect_routers(tgen, 2, 3) + connect_routers(tgen, 3, 1) + + # + # Populate multi-dimensional dictionary containing all expected outputs + # + number_of_steps = 11 + filenames = [ + "show_mpls_table.ref", + "show_isis_flex_algo.ref", + ] + for rname in ["rt1", "rt2", "rt3"]: + outputs[rname] = {} + for step in range(1, number_of_steps + 1): + outputs[rname][step] = {} + for filename in filenames: + # Get snapshots relative to the expected network convergence + filename_pullpath = "{}/{}/step{}/{}".format(CWD, rname, step, filename) + outputs[rname][step][filename] = open(filename_pullpath).read() + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(build_topo, mod.__name__) + frrdir = tgen.config.get(tgen.CONFIG_SECTION, "frrdir") + if not os.path.isfile(os.path.join(frrdir, "pathd")): + pytest.skip("pathd daemon wasn't built") + tgen.start_topology() + router_list = tgen.routers() + + # For all registered routers, load the zebra configuration file + for rname, router in router_list.items(): + router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))) + router.load_config( TopoRouter.RD_ISIS, os.path.join(CWD, "{}/isisd.conf".format(rname))) + tgen.start_router() + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + tgen.stop_topology() + + +def setup_testcase(msg): + logger.info(msg) + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + return tgen + + +def router_compare_json_output(rname, command, reference): + "Compare router JSON output" + + logger.info('Comparing router "%s" "%s" output', rname, command) + + tgen = get_topogen() + expected = json.loads(reference) + + # Run test function until we get an result. Wait at most 60 seconds. + test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected) + _, diff = topotest.run_and_expect(test_func, None, count=120, wait=0.5) + assertmsg = '"{}" JSON output mismatches the expected result'.format(rname) + assert diff is None, assertmsg + + +def router_compare_output(rname, command, reference): + "Compare router output" + + logger.info('Comparing router "%s" "%s" output', rname, command) + + tgen = get_topogen() + + # Run test function until we get an result. Wait at most 60 seconds. + test_func = partial(topotest.router_output_cmp, tgen.gears[rname], command, reference) + result, diff = topotest.run_and_expect(test_func, "", count=120, wait=0.5) + assertmsg = '{} command "{}" output mismatches the expected result:\n{}'.format(rname, command, diff) + assert result, assertmsg + + +# +# Step 1 +# +# Test initial network convergenece +# +# All flex-algo are defined and its fib entries are installed +# +def test_step1_mpls_lfib(): + logger.info("Test (step 1)") + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # For Developers + # tgen.mininet_cli() + for rname in ["rt1", "rt2", "rt3"]: + router_compare_output( + rname, "show isis flex-algo", + outputs[rname][1]["show_isis_flex_algo.ref"]) + router_compare_json_output( + rname, "show mpls table json", + outputs[rname][1]["show_mpls_table.ref"]) + + +# +# Step 2 +# +# Action(s): +# - Disable flex-algo-203 definition advertisement on rt1 +# +# Expected change(s): +# - Nothing +# +# Description: +# No change occurs because it refers to the FAD set in rt2. +# +def test_step2_mpls_lfib(): + logger.info("Test (step 2)") + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.gears["rt1"].vtysh_cmd( + """ + configure terminal + router isis 1 + flex-algo 203 + no advertise-definition + """) + + # For Developers + # tgen.mininet_cli() + for rname in ["rt1", "rt2", "rt3"]: + router_compare_output( + rname, "show isis flex-algo", + outputs[rname][2]["show_isis_flex_algo.ref"]) + router_compare_json_output( + rname, "show mpls table json", + outputs[rname][2]["show_mpls_table.ref"]) + + +# +# Step 3 +# +# Action(s): +# - Disable flex-algo-203 definition advertisement on rt2 +# +# Expected change(s): +# - rt1,rt2,rt3 should uninstall all Prefix-SIDs of flex-algo-203 +# +# Description: +# When all FADs are disappeared, all their prefix sid routes are withdrawn. +# +def test_step3_mpls_lfib(): + logger.info("Test (step 3)") + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.gears["rt2"].vtysh_cmd( + """ + configure terminal + router isis 1 + flex-algo 203 + no advertise-definition + """) + + # For Developers + # tgen.mininet_cli() + for rname in ["rt1", "rt2", "rt3"]: + router_compare_output( + rname, "show isis flex-algo", + outputs[rname][3]["show_isis_flex_algo.ref"]) + router_compare_json_output( + rname, "show mpls table json", + outputs[rname][3]["show_mpls_table.ref"]) + + +# +# Step 4 +# +# Action(s): +# - Enable flex-algo-203 definition advertisement on rt2 +# +# Expected change(s): +# - rt1,rt2,rt3 should install all Prefix-SIDs of flex-algo-203 +# +# Description: +# Since the FAD is restored, the reachability to the Prefix-SID is restored. +# +def test_step4_mpls_lfib(): + logger.info("Test (step 4)") + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.gears["rt2"].vtysh_cmd( + """ + configure terminal + router isis 1 + flex-algo 203 + advertise-definition + """) + + # For Developers + # tgen.mininet_cli() + for rname in ["rt1", "rt2", "rt3"]: + router_compare_output( + rname, "show isis flex-algo", + outputs[rname][4]["show_isis_flex_algo.ref"]) + router_compare_json_output( + rname, "show mpls table json", + outputs[rname][4]["show_mpls_table.ref"]) + + +# +# Step 5 +# +# Action(s): +# - Enable flex-algo-203 definition advertisement on rt1 +# +# Expected change(s): +# - Nothing +# +# Description: +# This does not affect the FIB, since there is already a FAD for rt2. +# However, the FAD owner will be changed from rt2 to rt1. +# +def test_step5_mpls_lfib(): + logger.info("Test (step 5)") + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.gears["rt1"].vtysh_cmd( + """ + configure terminal + router isis 1 + flex-algo 203 + advertise-definition + """) + + # For Developers + # tgen.mininet_cli() + for rname in ["rt1", "rt2", "rt3"]: + router_compare_output( + rname, "show isis flex-algo", + outputs[rname][5]["show_isis_flex_algo.ref"]) + router_compare_json_output( + rname, "show mpls table json", + outputs[rname][5]["show_mpls_table.ref"]) + + +# +# Step 6 +# +# Action(s): +# - Disable flex-algo-203 SR-MPLS dataplane on rt1 +# - Disable flex-algo-203 SR-MPLS dataplane on rt2 +# - Disable flex-algo-203 SR-MPLS dataplane on rt3 +# +# Expected change(s): +# - rt1,rt2,rt3 should uninstall all Prefix-SIDs of flex-algo-203 +# +# Description: +# Clear the Flex-Algo 203 whole settings on each routers. All routes related +# to it will be withdrawn. +# +def test_step6_mpls_lfib(): + logger.info("Test (step 6)") + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["rt1", "rt2", "rt3"]: + tgen.gears[rname].vtysh_cmd( + """ + configure terminal + router isis 1 + flex-algo 203 + no dataplane sr-mpls + """) + + # For Developers + # tgen.mininet_cli() + for rname in ["rt1", "rt2", "rt3"]: + router_compare_output( + rname, "show isis flex-algo", + outputs[rname][6]["show_isis_flex_algo.ref"]) + router_compare_json_output( + rname, "show mpls table json", + outputs[rname][6]["show_mpls_table.ref"]) + + +# +# Step 7 +# +# Action(s): +# - Disable flex-algo-203 all configuration on rt1 +# - Disable flex-algo-203 all configuration on rt2 +# - Disable flex-algo-203 all configuration on rt3 +# +# Expected change(s): +# - rt1,rt2,rt3 should uninstall all Prefix-SIDs of flex-algo-203 +# +# Description: +# Clear the Flex-Algo 203 whole settings on each routers. All routes related +# to it will be withdrawn. +# +def test_step7_mpls_lfib(): + logger.info("Test (step 7)") + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["rt1", "rt2", "rt3"]: + tgen.gears[rname].vtysh_cmd( + """ + configure terminal + router isis 1 + no flex-algo 203 + """) + + # For Developers + # tgen.mininet_cli() + for rname in ["rt1", "rt2", "rt3"]: + router_compare_output( + rname, "show isis flex-algo", + outputs[rname][7]["show_isis_flex_algo.ref"]) + router_compare_json_output( + rname, "show mpls table json", + outputs[rname][7]["show_mpls_table.ref"]) + +# +# Step 8 +# +# Action(s): +# - Enable flex-algo-203 all configuration on rt1 +# - Enable flex-algo-203 all configuration on rt2 +# - Enable flex-algo-203 all configuration on rt3 +# +# Expected change(s): +# - rt1,rt2,rt3 should install all Prefix-SIDs of flex-algo-203 +# +# Description: +# All configurations were backed. +# +def test_step8_mpls_lfib(): + logger.info("Test (step 8)") + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.gears["rt1"].vtysh_cmd( + """ + configure terminal + router isis 1 + flex-algo 203 + advertise-definition + affinity exclude-any green + dataplane sr-mpls + """) + + tgen.gears["rt2"].vtysh_cmd( + """ + configure terminal + router isis 1 + flex-algo 203 + advertise-definition + affinity exclude-any green + dataplane sr-mpls + """) + + tgen.gears["rt3"].vtysh_cmd( + """ + configure terminal + router isis 1 + flex-algo 203 + dataplane sr-mpls + """) + + # For Developers + # tgen.mininet_cli() + for rname in ["rt1", "rt2", "rt3"]: + router_compare_output( + rname, "show isis flex-algo", + outputs[rname][8]["show_isis_flex_algo.ref"]) + router_compare_json_output( + rname, "show mpls table json", + outputs[rname][8]["show_mpls_table.ref"]) + + +# +# Step 9 +# +# Action(s): +# - Disable algorithm prefix-sid of algo-203 on rt1 +# +# Expected change(s): +# - rt1 should uninstall all Prefix-SIDs of flex-algo-203 +# - rt2 should uninstall Prefix-SIDs of rt1's flex-algo-203 +# - rt3 should uninstall Prefix-SIDs of rt1's flex-algo-203 +# +def test_step9_mpls_lfib(): + logger.info("Test (step 9)") + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.gears["rt1"].vtysh_cmd( + """ + configure terminal + router isis 1 + no segment-routing prefix 1.1.1.1/32 algorithm 203 index 301 + no segment-routing prefix 2001:db8:1000::1/128 algorithm 203 index 1301 + """) + + # For Developers + # tgen.mininet_cli() + for rname in ["rt1", "rt2", "rt3"]: + router_compare_output( + rname, "show isis flex-algo", + outputs[rname][9]["show_isis_flex_algo.ref"]) + router_compare_json_output( + rname, "show mpls table json", + outputs[rname][9]["show_mpls_table.ref"]) + + +# +# Step 10 +# +# Action(s): +# - Enable algorithm prefix-sid of algo-203 on rt1 +# +# Expected change(s): +# - rt1 should install all Prefix-SIDs of flex-algo-203 +# - rt2 should install Prefix-SIDs of rt1's flex-algo-203 +# - rt3 should install Prefix-SIDs of rt1's flex-algo-203 +# +def test_step10_mpls_lfib(): + logger.info("Test (step 10)") + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.gears["rt1"].vtysh_cmd( + """ + configure terminal + router isis 1 + segment-routing prefix 1.1.1.1/32 algorithm 203 index 301 + segment-routing prefix 2001:db8:1000::1/128 algorithm 203 index 1301 + """) + + # For Developers + # tgen.mininet_cli() + for rname in ["rt1", "rt2", "rt3"]: + router_compare_output( + rname, "show isis flex-algo", + outputs[rname][10]["show_isis_flex_algo.ref"]) + router_compare_json_output( + rname, "show mpls table json", + outputs[rname][10]["show_mpls_table.ref"]) + + +# +# Step 11 +# +# Action(s): +# - Update algorithm prefix-sid of algo-203 on rt1 from 301 to 311 +# +# Expected change(s): +# - rt2 should update Prefix-SIDs of rt1's flex-algo-203 from 301 to 311 +# - rt3 should update Prefix-SIDs of rt1's flex-algo-203 from 301 to 311 +# +def test_step11_mpls_lfib(): + logger.info("Test (step 11)") + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.gears["rt1"].vtysh_cmd( + """ + configure terminal + router isis 1 + segment-routing prefix 1.1.1.1/32 algorithm 203 index 311 + segment-routing prefix 2001:db8:1000::1/128 algorithm 203 index 1311 + """) + + # For Developers + # tgen.mininet_cli() + for rname in ["rt1", "rt2", "rt3"]: + router_compare_output( + rname, "show isis flex-algo", + outputs[rname][11]["show_isis_flex_algo.ref"]) + router_compare_json_output( + rname, "show mpls table json", + outputs[rname][11]["show_mpls_table.ref"]) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/isis_sr_flex_algo_topo2/README.md b/tests/topotests/isis_sr_flex_algo_topo2/README.md new file mode 100644 index 0000000000..20282c49ee --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/README.md @@ -0,0 +1,8 @@ + +## test + +``` +fdk-enter rt9.pid iperf3 -s +fdk-enter rt0.pid iperf3 -B 111.111.111.111 -c 222.222.222.222 -P20 -t 100000 +fdk-enter rt0.pid watch -n0.1 ip -s link show +``` diff --git a/tests/topotests/isis_sr_flex_algo_topo2/configure-te.sh b/tests/topotests/isis_sr_flex_algo_topo2/configure-te.sh new file mode 100755 index 0000000000..527f05b60b --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/configure-te.sh @@ -0,0 +1,46 @@ +#!/bin/sh + +if [ $# -ne 1 ]; then + echo "invalid command syntax" 1>&2 + echo "Usage: $0 <0|128|129|130>" 1>&2 + exit 1 +fi + +case "$1" in + 0 ) echo ;; + 128 ) echo ;; + 129 ) echo ;; + 130 ) echo ;; + * ) echo "error" ; exit ;; +esac + +R0=$(cat /tmp/topotests/isis_sr_flex_algo_topo2.test_isis_sr_flex_algo_topo2/rt0.pid) +R9=$(cat /tmp/topotests/isis_sr_flex_algo_topo2.test_isis_sr_flex_algo_topo2/rt9.pid) + +set -x + +cat <<EOF | nsenter -a -t $R0 vtysh +conf te +segment-routing + traffic-eng + policy color 1 endpoint 9.9.9.9 + name sid-algorithm + binding-sid 111 + candidate-path preference 100 name sid-algorithm explicit segment-list sid-algorithm-$1 + exit + exit +exit +EOF + +cat <<EOF | nsenter -a -t $R9 vtysh +conf te +segment-routing + traffic-eng + policy color 1 endpoint 10.10.10.10 + name sid-algorithm + binding-sid 222 + candidate-path preference 100 name sid-algorithm explicit segment-list sid-algorithm-$1 + exit + exit +exit +EOF diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt0/bgpd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt0/bgpd.conf new file mode 100644 index 0000000000..3915bec385 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt0/bgpd.conf @@ -0,0 +1,17 @@ +! +router bgp 1 + bgp router-id 10.10.10.10 + no bgp network import-check + neighbor 9.9.9.9 remote-as 1 + neighbor 9.9.9.9 update-source 10.10.10.10 + ! + address-family ipv4 unicast + network 10.255.0.0/24 + neighbor 9.9.9.9 next-hop-self + neighbor 9.9.9.9 route-map sr-te in + exit-address-family +! +route-map sr-te permit 10 + set sr-te color 1 +exit +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt0/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt0/isisd.conf new file mode 100644 index 0000000000..cbf25504ef --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt0/isisd.conf @@ -0,0 +1,56 @@ +password 1 +hostname rt0 +log file isisd.log +! +!debug isis events +!debug isis spf-events +!debug isis route-events +!debug isis sr-events +!debug isis lsp-gen +! +affinity-map blue bit-position 0 +! +interface lo + ip router isis 1 + isis passive +! +interface eth-rt1 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt5 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +router isis 1 + lsp-gen-interval 2 + net 49.0000.0000.0000.1000.00 + is-type level-1 + topology ipv6-unicast + mpls-te on + ! + flex-algo 128 + dataplane sr-mpls + advertise-definition + ! + flex-algo 129 + dataplane sr-mpls + advertise-definition + ! + flex-algo 130 + dataplane sr-mpls + advertise-definition + affinity include-any blue + ! + segment-routing on + segment-routing global-block 20000 23999 + segment-routing node-msd 8 + segment-routing prefix 10.10.10.10/32 index 0 + segment-routing prefix 10.10.10.10/32 algorithm 128 index 100 + segment-routing prefix 10.10.10.10/32 algorithm 129 index 200 + segment-routing prefix 10.10.10.10/32 algorithm 130 index 300 +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt0/pathd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt0/pathd.conf new file mode 100644 index 0000000000..c51b4e0247 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt0/pathd.conf @@ -0,0 +1,20 @@ +log file pathd.log +! +hostname rt0 +! +segment-routing + traffic-eng + segment-list sid-algorithm-0 + index 10 mpls label 20009 + exit + segment-list sid-algorithm-128 + index 10 mpls label 20109 + exit + segment-list sid-algorithm-129 + index 10 mpls label 20209 + exit + segment-list sid-algorithm-130 + index 10 mpls label 20309 + exit + exit +exit diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt0/step1/route.json b/tests/topotests/isis_sr_flex_algo_topo2/rt0/step1/route.json new file mode 100644 index 0000000000..51a1c25556 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt0/step1/route.json @@ -0,0 +1,438 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.1" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20002, + "outLabelStack":[ + 20002 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.1" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20003, + "outLabelStack":[ + 20003 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.1" + } + ] + }, + "20004":{ + "inLabel":20004, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20004, + "outLabelStack":[ + 20004 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.1" + } + ] + }, + "20005":{ + "inLabel":20005, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.5" + } + ] + }, + "20006":{ + "inLabel":20006, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20006, + "outLabelStack":[ + 20006 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.5" + } + ] + }, + "20007":{ + "inLabel":20007, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20007, + "outLabelStack":[ + 20007 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.5" + } + ] + }, + "20008":{ + "inLabel":20008, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20008, + "outLabelStack":[ + 20008 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.5" + } + ] + }, + "20009":{ + "inLabel":20009, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20009, + "outLabelStack":[ + 20009 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.5" + }, + { + "type":"SR (IS-IS)", + "outLabel":20009, + "outLabelStack":[ + 20009 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.1" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.1" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20102, + "outLabelStack":[ + 20102 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.1" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20103, + "outLabelStack":[ + 20103 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.1" + } + ] + }, + "20104":{ + "inLabel":20104, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20104, + "outLabelStack":[ + 20104 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.1" + } + ] + }, + "20109":{ + "inLabel":20109, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20109, + "outLabelStack":[ + 20109 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.1" + } + ] + }, + "20205":{ + "inLabel":20205, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.5" + } + ] + }, + "20206":{ + "inLabel":20206, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20206, + "outLabelStack":[ + 20206 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.5" + } + ] + }, + "20207":{ + "inLabel":20207, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20207, + "outLabelStack":[ + 20207 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.5" + } + ] + }, + "20208":{ + "inLabel":20208, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20208, + "outLabelStack":[ + 20208 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.5" + } + ] + }, + "20209":{ + "inLabel":20209, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20209, + "outLabelStack":[ + 20209 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.5" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.1" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20302, + "outLabelStack":[ + 20302 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.1" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20303, + "outLabelStack":[ + 20303 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.1" + } + ] + }, + "20305":{ + "inLabel":20305, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.5" + } + ] + }, + "20306":{ + "inLabel":20306, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20306, + "outLabelStack":[ + 20306 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.5" + } + ] + }, + "20307":{ + "inLabel":20307, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20307, + "outLabelStack":[ + 20307 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.5" + } + ] + }, + "20309":{ + "inLabel":20309, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20309, + "outLabelStack":[ + 20309 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.5" + }, + { + "type":"SR (IS-IS)", + "outLabel":20309, + "outLabelStack":[ + 20309 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.1" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt0/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt0/zebra.conf new file mode 100644 index 0000000000..89837d4cf5 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt0/zebra.conf @@ -0,0 +1,34 @@ +log file zebra.log +! +hostname rt0 +! +!log stdout notifications +!log monitor notifications +!log commands +! +debug zebra packet +debug zebra dplane +debug zebra kernel +! +affinity-map blue bit-position 0 +! +interface lo + ip address 10.10.10.10/32 +! +interface eth-rt1 + ip address 10.1.0.10/24 + link-params + affinity blue + exit-link-params +! +interface eth-rt5 + ip address 10.5.0.10/24 + link-params + affinity blue + exit-link-params +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt1/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt1/isisd.conf new file mode 100644 index 0000000000..b6bba0c1c3 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt1/isisd.conf @@ -0,0 +1,60 @@ +password 1 +hostname rt1 +log file isisd.log +! +!debug isis events +!debug isis spf-events +!debug isis route-events +!debug isis sr-events +!debug isis lsp-gen +! +affinity-map blue bit-position 0 +! +interface lo + ip router isis 1 + isis passive +! +interface eth-rt0 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt2 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt4 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt5 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +router isis 1 + lsp-gen-interval 2 + net 49.0000.0000.0000.1001.00 + is-type level-1 + topology ipv6-unicast + mpls-te on + ! + flex-algo 128 + dataplane sr-mpls + flex-algo 130 + dataplane sr-mpls + ! + segment-routing on + segment-routing global-block 20000 23999 + segment-routing node-msd 8 + segment-routing prefix 1.1.1.1/32 index 1 + segment-routing prefix 1.1.1.1/32 algorithm 128 index 101 + segment-routing prefix 1.1.1.1/32 algorithm 129 index 201 + segment-routing prefix 1.1.1.1/32 algorithm 130 index 301 +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt1/step1/route.json b/tests/topotests/isis_sr_flex_algo_topo2/rt1/step1/route.json new file mode 100644 index 0000000000..50066250b8 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt1/step1/route.json @@ -0,0 +1,428 @@ +{ + "20000":{ + "inLabel":20000, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.10" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20003, + "outLabelStack":[ + 20003 + ], + "distance":150, + "installed":true, + "nexthop":"10.14.0.4" + }, + { + "type":"SR (IS-IS)", + "outLabel":20003, + "outLabelStack":[ + 20003 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20004":{ + "inLabel":20004, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.14.0.4" + } + ] + }, + "20005":{ + "inLabel":20005, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.15.0.5" + } + ] + }, + "20006":{ + "inLabel":20006, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20006, + "outLabelStack":[ + 20006 + ], + "distance":150, + "installed":true, + "nexthop":"10.15.0.5" + }, + { + "type":"SR (IS-IS)", + "outLabel":20006, + "outLabelStack":[ + 20006 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20007":{ + "inLabel":20007, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20007, + "outLabelStack":[ + 20007 + ], + "distance":150, + "installed":true, + "nexthop":"10.15.0.5" + }, + { + "type":"SR (IS-IS)", + "outLabel":20007, + "outLabelStack":[ + 20007 + ], + "distance":150, + "installed":true, + "nexthop":"10.14.0.4" + }, + { + "type":"SR (IS-IS)", + "outLabel":20007, + "outLabelStack":[ + 20007 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20008":{ + "inLabel":20008, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20008, + "outLabelStack":[ + 20008 + ], + "distance":150, + "installed":true, + "nexthop":"10.15.0.5" + }, + { + "type":"SR (IS-IS)", + "outLabel":20008, + "outLabelStack":[ + 20008 + ], + "distance":150, + "installed":true, + "nexthop":"10.14.0.4" + } + ] + }, + "20009":{ + "inLabel":20009, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20009, + "outLabelStack":[ + 20009 + ], + "distance":150, + "installed":true, + "nexthop":"10.14.0.4" + }, + { + "type":"SR (IS-IS)", + "outLabel":20009, + "outLabelStack":[ + 20009 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20100":{ + "inLabel":20100, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.10" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20103, + "outLabelStack":[ + 20103 + ], + "distance":150, + "installed":true, + "nexthop":"10.14.0.4" + }, + { + "type":"SR (IS-IS)", + "outLabel":20103, + "outLabelStack":[ + 20103 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20104":{ + "inLabel":20104, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.14.0.4" + } + ] + }, + "20109":{ + "inLabel":20109, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20109, + "outLabelStack":[ + 20109 + ], + "distance":150, + "installed":true, + "nexthop":"10.14.0.4" + }, + { + "type":"SR (IS-IS)", + "outLabel":20109, + "outLabelStack":[ + 20109 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20300":{ + "inLabel":20300, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.10" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20303, + "outLabelStack":[ + 20303 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20305":{ + "inLabel":20305, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20305, + "outLabelStack":[ + 20305 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.10" + } + ] + }, + "20306":{ + "inLabel":20306, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20306, + "outLabelStack":[ + 20306 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.10" + } + ] + }, + "20307":{ + "inLabel":20307, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20307, + "outLabelStack":[ + 20307 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + }, + { + "type":"SR (IS-IS)", + "outLabel":20307, + "outLabelStack":[ + 20307 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.10" + } + ] + }, + "20309":{ + "inLabel":20309, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20309, + "outLabelStack":[ + 20309 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt1/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt1/zebra.conf new file mode 100644 index 0000000000..25a96290ae --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt1/zebra.conf @@ -0,0 +1,40 @@ +log file zebra.log +! +hostname rt1 +! +log stdout notifications +log monitor notifications +log commands +! +!debug zebra packet +!debug zebra dplane +!debug zebra kernel +! +affinity-map blue bit-position 0 +! +interface lo + ip address 1.1.1.1/32 +! +interface eth-rt0 + ip address 10.1.0.1/24 + link-params + affinity blue + exit-link-params +! +interface eth-rt2 + ip address 10.12.0.1/24 + link-params + affinity blue + exit-link-params +! +interface eth-rt4 + ip address 10.14.0.1/24 +! +interface eth-rt5 + ip address 10.15.0.1/24 +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt2/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt2/isisd.conf new file mode 100644 index 0000000000..f051a68e21 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt2/isisd.conf @@ -0,0 +1,54 @@ +password 1 +hostname rt2 +log file isisd.log +! +!debug isis events +!debug isis route-events +!debug isis spf-events +!debug isis sr-events +!debug isis lsp-gen +! +affinity-map blue bit-position 0 +! +interface lo + ip router isis 1 + isis passive +! +interface eth-rt1 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt3 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt6 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +router isis 1 + lsp-gen-interval 2 + net 49.0000.0000.0000.1002.00 + is-type level-1 + topology ipv6-unicast + mpls-te on + ! + flex-algo 128 + dataplane sr-mpls + flex-algo 130 + dataplane sr-mpls + ! + segment-routing on + segment-routing global-block 20000 23999 + segment-routing node-msd 8 + segment-routing prefix 2.2.2.2/32 index 2 + segment-routing prefix 2.2.2.2/32 algorithm 128 index 102 + segment-routing prefix 2.2.2.2/32 algorithm 129 index 202 + segment-routing prefix 2.2.2.2/32 algorithm 130 index 302 +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt2/step1/route.json b/tests/topotests/isis_sr_flex_algo_topo2/rt2/step1/route.json new file mode 100644 index 0000000000..679b41db03 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt2/step1/route.json @@ -0,0 +1,408 @@ +{ + "20000":{ + "inLabel":20000, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20000, + "outLabelStack":[ + 20000 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20004":{ + "inLabel":20004, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20004, + "outLabelStack":[ + 20004 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":20004, + "outLabelStack":[ + 20004 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20005":{ + "inLabel":20005, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20005, + "outLabelStack":[ + 20005 + ], + "distance":150, + "installed":true, + "nexthop":"10.26.0.6" + }, + { + "type":"SR (IS-IS)", + "outLabel":20005, + "outLabelStack":[ + 20005 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20006":{ + "inLabel":20006, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.26.0.6" + } + ] + }, + "20007":{ + "inLabel":20007, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20007, + "outLabelStack":[ + 20007 + ], + "distance":150, + "installed":true, + "nexthop":"10.26.0.6" + }, + { + "type":"SR (IS-IS)", + "outLabel":20007, + "outLabelStack":[ + 20007 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20008":{ + "inLabel":20008, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20008, + "outLabelStack":[ + 20008 + ], + "distance":150, + "installed":true, + "nexthop":"10.26.0.6" + }, + { + "type":"SR (IS-IS)", + "outLabel":20008, + "outLabelStack":[ + 20008 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":20008, + "outLabelStack":[ + 20008 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20009":{ + "inLabel":20009, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20009, + "outLabelStack":[ + 20009 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20100":{ + "inLabel":20100, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20100, + "outLabelStack":[ + 20100 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20104":{ + "inLabel":20104, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20104, + "outLabelStack":[ + 20104 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":20104, + "outLabelStack":[ + 20104 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20109":{ + "inLabel":20109, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20109, + "outLabelStack":[ + 20109 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20300":{ + "inLabel":20300, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20300, + "outLabelStack":[ + 20300 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20305":{ + "inLabel":20305, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20305, + "outLabelStack":[ + 20305 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20306":{ + "inLabel":20306, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20306, + "outLabelStack":[ + 20306 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":20306, + "outLabelStack":[ + 20306 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20307":{ + "inLabel":20307, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20307, + "outLabelStack":[ + 20307 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20309":{ + "inLabel":20309, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20309, + "outLabelStack":[ + 20309 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt2/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt2/zebra.conf new file mode 100644 index 0000000000..d739a732a9 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt2/zebra.conf @@ -0,0 +1,37 @@ +log file zebra.log +! +hostname rt2 +! +log stdout notifications +log monitor notifications +log commands +! +!debug zebra packet +!debug zebra dplane +!debug zebra kernel +! +affinity-map blue bit-position 0 +! +interface lo + ip address 2.2.2.2/32 +! +interface eth-rt1 + ip address 10.12.0.2/24 + link-params + affinity blue + exit-link-params +! +interface eth-rt3 + ip address 10.23.0.2/24 + link-params + affinity blue + exit-link-params +! +interface eth-rt6 + ip address 10.26.0.2/24 +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt3/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt3/isisd.conf new file mode 100644 index 0000000000..644e656bfd --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt3/isisd.conf @@ -0,0 +1,60 @@ +password 1 +hostname rt3 +log file isisd.log +! +!debug isis events +!debug isis route-events +!debug isis spf-events +!debug isis sr-events +!debug isis lsp-gen +! +affinity-map blue bit-position 0 +! +interface lo + ip router isis 1 + isis passive +! +interface eth-rt2 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt4 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt7 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt9 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +router isis 1 + lsp-gen-interval 2 + net 49.0000.0000.0000.1003.00 + is-type level-1 + topology ipv6-unicast + mpls-te on + ! + flex-algo 128 + dataplane sr-mpls + flex-algo 130 + dataplane sr-mpls + ! + segment-routing on + segment-routing global-block 20000 23999 + segment-routing node-msd 8 + segment-routing prefix 3.3.3.3/32 index 3 + segment-routing prefix 3.3.3.3/32 algorithm 128 index 103 + segment-routing prefix 3.3.3.3/32 algorithm 129 index 203 + segment-routing prefix 3.3.3.3/32 algorithm 130 index 303 +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt3/step1/route.json b/tests/topotests/isis_sr_flex_algo_topo2/rt3/step1/route.json new file mode 100644 index 0000000000..f930faa61f --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt3/step1/route.json @@ -0,0 +1,428 @@ +{ + "20000":{ + "inLabel":20000, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20000, + "outLabelStack":[ + 20000 + ], + "distance":150, + "installed":true, + "nexthop":"10.34.0.4" + }, + { + "type":"SR (IS-IS)", + "outLabel":20000, + "outLabelStack":[ + 20000 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20001, + "outLabelStack":[ + 20001 + ], + "distance":150, + "installed":true, + "nexthop":"10.34.0.4" + }, + { + "type":"SR (IS-IS)", + "outLabel":20001, + "outLabelStack":[ + 20001 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20004":{ + "inLabel":20004, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.34.0.4" + } + ] + }, + "20005":{ + "inLabel":20005, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20005, + "outLabelStack":[ + 20005 + ], + "distance":150, + "installed":true, + "nexthop":"10.37.0.7" + }, + { + "type":"SR (IS-IS)", + "outLabel":20005, + "outLabelStack":[ + 20005 + ], + "distance":150, + "installed":true, + "nexthop":"10.34.0.4" + }, + { + "type":"SR (IS-IS)", + "outLabel":20005, + "outLabelStack":[ + 20005 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20006":{ + "inLabel":20006, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20006, + "outLabelStack":[ + 20006 + ], + "distance":150, + "installed":true, + "nexthop":"10.37.0.7" + }, + { + "type":"SR (IS-IS)", + "outLabel":20006, + "outLabelStack":[ + 20006 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20007":{ + "inLabel":20007, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.37.0.7" + } + ] + }, + "20008":{ + "inLabel":20008, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20008, + "outLabelStack":[ + 20008 + ], + "distance":150, + "installed":true, + "nexthop":"10.37.0.7" + }, + { + "type":"SR (IS-IS)", + "outLabel":20008, + "outLabelStack":[ + 20008 + ], + "distance":150, + "installed":true, + "nexthop":"10.34.0.4" + } + ] + }, + "20009":{ + "inLabel":20009, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.9" + } + ] + }, + "20100":{ + "inLabel":20100, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20100, + "outLabelStack":[ + 20100 + ], + "distance":150, + "installed":true, + "nexthop":"10.34.0.4" + }, + { + "type":"SR (IS-IS)", + "outLabel":20100, + "outLabelStack":[ + 20100 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20101, + "outLabelStack":[ + 20101 + ], + "distance":150, + "installed":true, + "nexthop":"10.34.0.4" + }, + { + "type":"SR (IS-IS)", + "outLabel":20101, + "outLabelStack":[ + 20101 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20104":{ + "inLabel":20104, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.34.0.4" + } + ] + }, + "20109":{ + "inLabel":20109, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.9" + } + ] + }, + "20300":{ + "inLabel":20300, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20300, + "outLabelStack":[ + 20300 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20301, + "outLabelStack":[ + 20301 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20305":{ + "inLabel":20305, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20305, + "outLabelStack":[ + 20305 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.9" + }, + { + "type":"SR (IS-IS)", + "outLabel":20305, + "outLabelStack":[ + 20305 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20306":{ + "inLabel":20306, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20306, + "outLabelStack":[ + 20306 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.9" + } + ] + }, + "20307":{ + "inLabel":20307, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20307, + "outLabelStack":[ + 20307 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.9" + } + ] + }, + "20309":{ + "inLabel":20309, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.9" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt3/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt3/zebra.conf new file mode 100644 index 0000000000..5c3bed0763 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt3/zebra.conf @@ -0,0 +1,40 @@ +log file zebra.log +! +hostname rt3 +! +log stdout notifications +log monitor notifications +log commands +! +!debug zebra packet +!debug zebra dplane +!debug zebra kernel +! +affinity-map blue bit-position 0 +! +interface lo + ip address 3.3.3.3/32 +! +interface eth-rt2 + ip address 10.23.0.3/24 + link-params + affinity blue + exit-link-params +! +interface eth-rt4 + ip address 10.34.0.3/24 +! +interface eth-rt7 + ip address 10.37.0.3/24 +! +interface eth-rt9 + ip address 10.39.0.3/24 + link-params + affinity blue + exit-link-params +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt4/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt4/isisd.conf new file mode 100644 index 0000000000..1ab200fbd8 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt4/isisd.conf @@ -0,0 +1,52 @@ +password 1 +hostname rt4 +log file isisd.log +! +!debug isis events +!debug isis spf-events +!debug isis route-events +!debug isis sr-events +!debug isis lsp-gen +! +affinity-map blue bit-position 0 +! +interface lo + ip router isis 1 + isis passive +! +interface eth-rt1 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt3 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt8 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +router isis 1 + lsp-gen-interval 2 + net 49.0000.0000.0000.1004.00 + is-type level-1 + topology ipv6-unicast + mpls-te on + ! + flex-algo 128 + dataplane sr-mpls + ! + segment-routing on + segment-routing global-block 20000 23999 + segment-routing node-msd 8 + segment-routing prefix 4.4.4.4/32 index 4 + segment-routing prefix 4.4.4.4/32 algorithm 128 index 104 + segment-routing prefix 4.4.4.4/32 algorithm 129 index 204 + segment-routing prefix 4.4.4.4/32 algorithm 130 index 304 +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt4/step1/route.json b/tests/topotests/isis_sr_flex_algo_topo2/rt4/step1/route.json new file mode 100644 index 0000000000..141e40d455 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt4/step1/route.json @@ -0,0 +1,286 @@ +{ + "20000":{ + "inLabel":20000, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20000, + "outLabelStack":[ + 20000 + ], + "distance":150, + "installed":true, + "nexthop":"10.14.0.1" + } + ] + }, + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.14.0.1" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20002, + "outLabelStack":[ + 20002 + ], + "distance":150, + "installed":true, + "nexthop":"10.34.0.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":20002, + "outLabelStack":[ + 20002 + ], + "distance":150, + "installed":true, + "nexthop":"10.14.0.1" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.34.0.3" + } + ] + }, + "20005":{ + "inLabel":20005, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20005, + "outLabelStack":[ + 20005 + ], + "distance":150, + "installed":true, + "nexthop":"10.48.0.8" + }, + { + "type":"SR (IS-IS)", + "outLabel":20005, + "outLabelStack":[ + 20005 + ], + "distance":150, + "installed":true, + "nexthop":"10.14.0.1" + } + ] + }, + "20006":{ + "inLabel":20006, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20006, + "outLabelStack":[ + 20006 + ], + "distance":150, + "installed":true, + "nexthop":"10.48.0.8" + }, + { + "type":"SR (IS-IS)", + "outLabel":20006, + "outLabelStack":[ + 20006 + ], + "distance":150, + "installed":true, + "nexthop":"10.34.0.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":20006, + "outLabelStack":[ + 20006 + ], + "distance":150, + "installed":true, + "nexthop":"10.14.0.1" + } + ] + }, + "20007":{ + "inLabel":20007, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20007, + "outLabelStack":[ + 20007 + ], + "distance":150, + "installed":true, + "nexthop":"10.48.0.8" + }, + { + "type":"SR (IS-IS)", + "outLabel":20007, + "outLabelStack":[ + 20007 + ], + "distance":150, + "installed":true, + "nexthop":"10.34.0.3" + } + ] + }, + "20008":{ + "inLabel":20008, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.48.0.8" + } + ] + }, + "20009":{ + "inLabel":20009, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20009, + "outLabelStack":[ + 20009 + ], + "distance":150, + "installed":true, + "nexthop":"10.34.0.3" + } + ] + }, + "20100":{ + "inLabel":20100, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20100, + "outLabelStack":[ + 20100 + ], + "distance":150, + "installed":true, + "nexthop":"10.14.0.1" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.14.0.1" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20102, + "outLabelStack":[ + 20102 + ], + "distance":150, + "installed":true, + "nexthop":"10.34.0.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":20102, + "outLabelStack":[ + 20102 + ], + "distance":150, + "installed":true, + "nexthop":"10.14.0.1" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.34.0.3" + } + ] + }, + "20109":{ + "inLabel":20109, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20109, + "outLabelStack":[ + 20109 + ], + "distance":150, + "installed":true, + "nexthop":"10.34.0.3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt4/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt4/zebra.conf new file mode 100644 index 0000000000..9c00013e70 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt4/zebra.conf @@ -0,0 +1,31 @@ +log file zebra.log +! +hostname rt4 +! +log stdout notifications +log monitor notifications +log commands +! +!debug zebra packet +!debug zebra dplane +!debug zebra kernel +! +affinity-map blue bit-position 0 +! +interface lo + ip address 4.4.4.4/32 +! +interface eth-rt1 + ip address 10.14.0.4/24 +! +interface eth-rt3 + ip address 10.34.0.4/24 +! +interface eth-rt8 + ip address 10.48.0.4/24 +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt5/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt5/isisd.conf new file mode 100644 index 0000000000..54cc37711e --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt5/isisd.conf @@ -0,0 +1,60 @@ +password 1 +hostname rt5 +log file isisd.log +! +!debug isis events +!debug isis spf-events +!debug isis route-events +!debug isis sr-events +!debug isis lsp-gen +! +affinity-map blue bit-position 0 +! +interface lo + ip router isis 1 + isis passive +! +interface eth-rt0 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt1 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt6 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt8 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +router isis 1 + lsp-gen-interval 2 + net 49.0000.0000.0000.1005.00 + is-type level-1 + topology ipv6-unicast + mpls-te on + ! + flex-algo 129 + dataplane sr-mpls + flex-algo 130 + dataplane sr-mpls + ! + segment-routing on + segment-routing global-block 20000 23999 + segment-routing node-msd 8 + segment-routing prefix 5.5.5.5/32 index 5 + segment-routing prefix 5.5.5.5/32 algorithm 128 index 105 + segment-routing prefix 5.5.5.5/32 algorithm 129 index 205 + segment-routing prefix 5.5.5.5/32 algorithm 130 index 305 +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt5/step1/route.json b/tests/topotests/isis_sr_flex_algo_topo2/rt5/step1/route.json new file mode 100644 index 0000000000..82ebfc075f --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt5/step1/route.json @@ -0,0 +1,428 @@ +{ + "20000":{ + "inLabel":20000, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.10" + } + ] + }, + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.15.0.1" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20002, + "outLabelStack":[ + 20002 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.6" + }, + { + "type":"SR (IS-IS)", + "outLabel":20002, + "outLabelStack":[ + 20002 + ], + "distance":150, + "installed":true, + "nexthop":"10.15.0.1" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20003, + "outLabelStack":[ + 20003 + ], + "distance":150, + "installed":true, + "nexthop":"10.58.0.8" + }, + { + "type":"SR (IS-IS)", + "outLabel":20003, + "outLabelStack":[ + 20003 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.6" + }, + { + "type":"SR (IS-IS)", + "outLabel":20003, + "outLabelStack":[ + 20003 + ], + "distance":150, + "installed":true, + "nexthop":"10.15.0.1" + } + ] + }, + "20004":{ + "inLabel":20004, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20004, + "outLabelStack":[ + 20004 + ], + "distance":150, + "installed":true, + "nexthop":"10.58.0.8" + }, + { + "type":"SR (IS-IS)", + "outLabel":20004, + "outLabelStack":[ + 20004 + ], + "distance":150, + "installed":true, + "nexthop":"10.15.0.1" + } + ] + }, + "20006":{ + "inLabel":20006, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.6" + } + ] + }, + "20007":{ + "inLabel":20007, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20007, + "outLabelStack":[ + 20007 + ], + "distance":150, + "installed":true, + "nexthop":"10.58.0.8" + }, + { + "type":"SR (IS-IS)", + "outLabel":20007, + "outLabelStack":[ + 20007 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.6" + } + ] + }, + "20008":{ + "inLabel":20008, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.58.0.8" + } + ] + }, + "20009":{ + "inLabel":20009, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20009, + "outLabelStack":[ + 20009 + ], + "distance":150, + "installed":true, + "nexthop":"10.58.0.8" + }, + { + "type":"SR (IS-IS)", + "outLabel":20009, + "outLabelStack":[ + 20009 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.6" + } + ] + }, + "20200":{ + "inLabel":20200, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.10" + } + ] + }, + "20206":{ + "inLabel":20206, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.6" + } + ] + }, + "20207":{ + "inLabel":20207, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20207, + "outLabelStack":[ + 20207 + ], + "distance":150, + "installed":true, + "nexthop":"10.58.0.8" + }, + { + "type":"SR (IS-IS)", + "outLabel":20207, + "outLabelStack":[ + 20207 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.6" + } + ] + }, + "20208":{ + "inLabel":20208, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.58.0.8" + } + ] + }, + "20209":{ + "inLabel":20209, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20209, + "outLabelStack":[ + 20209 + ], + "distance":150, + "installed":true, + "nexthop":"10.58.0.8" + }, + { + "type":"SR (IS-IS)", + "outLabel":20209, + "outLabelStack":[ + 20209 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.6" + } + ] + }, + "20300":{ + "inLabel":20300, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.10" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20301, + "outLabelStack":[ + 20301 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.10" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20302, + "outLabelStack":[ + 20302 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.10" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20303, + "outLabelStack":[ + 20303 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.6" + }, + { + "type":"SR (IS-IS)", + "outLabel":20303, + "outLabelStack":[ + 20303 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.10" + } + ] + }, + "20306":{ + "inLabel":20306, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.6" + } + ] + }, + "20307":{ + "inLabel":20307, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20307, + "outLabelStack":[ + 20307 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.6" + } + ] + }, + "20309":{ + "inLabel":20309, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20309, + "outLabelStack":[ + 20309 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.6" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt5/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt5/zebra.conf new file mode 100644 index 0000000000..61c599db7b --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt5/zebra.conf @@ -0,0 +1,40 @@ +log file zebra.log +! +hostname rt5 +! +log stdout notifications +log monitor notifications +log commands +! +!debug zebra packet +!debug zebra dplane +!debug zebra kernel +! +affinity-map blue bit-position 0 +! +interface lo + ip address 5.5.5.5/32 +! +interface eth-rt0 + ip address 10.5.0.5/24 + link-params + affinity blue + exit-link-params +! +interface eth-rt1 + ip address 10.15.0.5/24 +! +interface eth-rt6 + ip address 10.56.0.5/24 + link-params + affinity blue + exit-link-params +! +interface eth-rt8 + ip address 10.58.0.5/24 +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt6/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt6/isisd.conf new file mode 100644 index 0000000000..fc8660cfa6 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt6/isisd.conf @@ -0,0 +1,54 @@ +password 1 +hostname rt6 +log file isisd.log +! +!debug isis events +!debug isis spf-events +!debug isis route-events +!debug isis sr-events +!debug isis lsp-gen +! +affinity-map blue bit-position 0 +! +interface lo + ip router isis 1 + isis passive +! +interface eth-rt2 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt5 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt7 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +router isis 1 + lsp-gen-interval 2 + net 49.0000.0000.0000.1006.00 + is-type level-1 + topology ipv6-unicast + mpls-te on + ! + flex-algo 129 + dataplane sr-mpls + flex-algo 130 + dataplane sr-mpls + ! + segment-routing on + segment-routing global-block 20000 23999 + segment-routing node-msd 8 + segment-routing prefix 6.6.6.6/32 index 6 + segment-routing prefix 6.6.6.6/32 algorithm 128 index 106 + segment-routing prefix 6.6.6.6/32 algorithm 129 index 206 + segment-routing prefix 6.6.6.6/32 algorithm 130 index 306 +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt6/step1/route.json b/tests/topotests/isis_sr_flex_algo_topo2/rt6/step1/route.json new file mode 100644 index 0000000000..2cc7277b41 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt6/step1/route.json @@ -0,0 +1,408 @@ +{ + "20000":{ + "inLabel":20000, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20000, + "outLabelStack":[ + 20000 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.5" + } + ] + }, + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20001, + "outLabelStack":[ + 20001 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.5" + }, + { + "type":"SR (IS-IS)", + "outLabel":20001, + "outLabelStack":[ + 20001 + ], + "distance":150, + "installed":true, + "nexthop":"10.26.0.2" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.26.0.2" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20003, + "outLabelStack":[ + 20003 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.7" + }, + { + "type":"SR (IS-IS)", + "outLabel":20003, + "outLabelStack":[ + 20003 + ], + "distance":150, + "installed":true, + "nexthop":"10.26.0.2" + } + ] + }, + "20004":{ + "inLabel":20004, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20004, + "outLabelStack":[ + 20004 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.7" + }, + { + "type":"SR (IS-IS)", + "outLabel":20004, + "outLabelStack":[ + 20004 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.5" + }, + { + "type":"SR (IS-IS)", + "outLabel":20004, + "outLabelStack":[ + 20004 + ], + "distance":150, + "installed":true, + "nexthop":"10.26.0.2" + } + ] + }, + "20005":{ + "inLabel":20005, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.5" + } + ] + }, + "20007":{ + "inLabel":20007, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.7" + } + ] + }, + "20008":{ + "inLabel":20008, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20008, + "outLabelStack":[ + 20008 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.7" + }, + { + "type":"SR (IS-IS)", + "outLabel":20008, + "outLabelStack":[ + 20008 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.5" + } + ] + }, + "20009":{ + "inLabel":20009, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20009, + "outLabelStack":[ + 20009 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.7" + } + ] + }, + "20200":{ + "inLabel":20200, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20200, + "outLabelStack":[ + 20200 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.5" + } + ] + }, + "20205":{ + "inLabel":20205, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.5" + } + ] + }, + "20207":{ + "inLabel":20207, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.7" + } + ] + }, + "20208":{ + "inLabel":20208, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20208, + "outLabelStack":[ + 20208 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.7" + }, + { + "type":"SR (IS-IS)", + "outLabel":20208, + "outLabelStack":[ + 20208 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.5" + } + ] + }, + "20209":{ + "inLabel":20209, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20209, + "outLabelStack":[ + 20209 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.7" + } + ] + }, + "20300":{ + "inLabel":20300, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20300, + "outLabelStack":[ + 20300 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.5" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20301, + "outLabelStack":[ + 20301 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.5" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20302, + "outLabelStack":[ + 20302 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.7" + }, + { + "type":"SR (IS-IS)", + "outLabel":20302, + "outLabelStack":[ + 20302 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.5" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20303, + "outLabelStack":[ + 20303 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.7" + } + ] + }, + "20305":{ + "inLabel":20305, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.5" + } + ] + }, + "20307":{ + "inLabel":20307, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.7" + } + ] + }, + "20309":{ + "inLabel":20309, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20309, + "outLabelStack":[ + 20309 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.7" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt6/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt6/zebra.conf new file mode 100644 index 0000000000..b63401e114 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt6/zebra.conf @@ -0,0 +1,37 @@ +log file zebra.log +! +hostname rt6 +! +log stdout notifications +log monitor notifications +log commands +! +!debug zebra packet +!debug zebra dplane +!debug zebra kernel +! +affinity-map blue bit-position 0 +! +interface lo + ip address 6.6.6.6/32 +! +interface eth-rt2 + ip address 10.26.0.6/24 +! +interface eth-rt5 + ip address 10.56.0.6/24 + link-params + affinity blue + exit-link-params +! +interface eth-rt7 + ip address 10.67.0.6/24 + link-params + affinity blue + exit-link-params +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt7/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt7/isisd.conf new file mode 100644 index 0000000000..10dc9812a4 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt7/isisd.conf @@ -0,0 +1,60 @@ +password 1 +hostname rt7 +log file isisd.log +! +!debug isis events +!debug isis spf-events +!debug isis route-events +!debug isis sr-events +!debug isis lsp-gen +! +affinity-map blue bit-position 0 +! +interface lo + ip router isis 1 + isis passive +! +interface eth-rt3 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt6 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt8 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt9 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +router isis 1 + lsp-gen-interval 2 + net 49.0000.0000.0000.1007.00 + is-type level-1 + topology ipv6-unicast + mpls-te on + ! + flex-algo 129 + dataplane sr-mpls + flex-algo 130 + dataplane sr-mpls + ! + segment-routing on + segment-routing global-block 20000 23999 + segment-routing node-msd 8 + segment-routing prefix 7.7.7.7/32 index 7 + segment-routing prefix 7.7.7.7/32 algorithm 128 index 107 + segment-routing prefix 7.7.7.7/32 algorithm 129 index 207 + segment-routing prefix 7.7.7.7/32 algorithm 130 index 307 +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt7/step1/route.json b/tests/topotests/isis_sr_flex_algo_topo2/rt7/step1/route.json new file mode 100644 index 0000000000..aeaa6046ab --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt7/step1/route.json @@ -0,0 +1,428 @@ +{ + "20000":{ + "inLabel":20000, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20000, + "outLabelStack":[ + 20000 + ], + "distance":150, + "installed":true, + "nexthop":"10.78.0.8" + }, + { + "type":"SR (IS-IS)", + "outLabel":20000, + "outLabelStack":[ + 20000 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.6" + } + ] + }, + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20001, + "outLabelStack":[ + 20001 + ], + "distance":150, + "installed":true, + "nexthop":"10.78.0.8" + }, + { + "type":"SR (IS-IS)", + "outLabel":20001, + "outLabelStack":[ + 20001 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.6" + }, + { + "type":"SR (IS-IS)", + "outLabel":20001, + "outLabelStack":[ + 20001 + ], + "distance":150, + "installed":true, + "nexthop":"10.37.0.3" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20002, + "outLabelStack":[ + 20002 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.6" + }, + { + "type":"SR (IS-IS)", + "outLabel":20002, + "outLabelStack":[ + 20002 + ], + "distance":150, + "installed":true, + "nexthop":"10.37.0.3" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.37.0.3" + } + ] + }, + "20004":{ + "inLabel":20004, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20004, + "outLabelStack":[ + 20004 + ], + "distance":150, + "installed":true, + "nexthop":"10.78.0.8" + }, + { + "type":"SR (IS-IS)", + "outLabel":20004, + "outLabelStack":[ + 20004 + ], + "distance":150, + "installed":true, + "nexthop":"10.37.0.3" + } + ] + }, + "20005":{ + "inLabel":20005, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20005, + "outLabelStack":[ + 20005 + ], + "distance":150, + "installed":true, + "nexthop":"10.78.0.8" + }, + { + "type":"SR (IS-IS)", + "outLabel":20005, + "outLabelStack":[ + 20005 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.6" + } + ] + }, + "20006":{ + "inLabel":20006, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.6" + } + ] + }, + "20008":{ + "inLabel":20008, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.78.0.8" + } + ] + }, + "20009":{ + "inLabel":20009, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.9" + } + ] + }, + "20200":{ + "inLabel":20200, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20200, + "outLabelStack":[ + 20200 + ], + "distance":150, + "installed":true, + "nexthop":"10.78.0.8" + }, + { + "type":"SR (IS-IS)", + "outLabel":20200, + "outLabelStack":[ + 20200 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.6" + } + ] + }, + "20205":{ + "inLabel":20205, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20205, + "outLabelStack":[ + 20205 + ], + "distance":150, + "installed":true, + "nexthop":"10.78.0.8" + }, + { + "type":"SR (IS-IS)", + "outLabel":20205, + "outLabelStack":[ + 20205 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.6" + } + ] + }, + "20206":{ + "inLabel":20206, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.6" + } + ] + }, + "20208":{ + "inLabel":20208, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.78.0.8" + } + ] + }, + "20209":{ + "inLabel":20209, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.9" + } + ] + }, + "20300":{ + "inLabel":20300, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20300, + "outLabelStack":[ + 20300 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.6" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20301, + "outLabelStack":[ + 20301 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.9" + }, + { + "type":"SR (IS-IS)", + "outLabel":20301, + "outLabelStack":[ + 20301 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.6" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20302, + "outLabelStack":[ + 20302 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.9" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20303, + "outLabelStack":[ + 20303 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.9" + } + ] + }, + "20305":{ + "inLabel":20305, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20305, + "outLabelStack":[ + 20305 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.6" + } + ] + }, + "20306":{ + "inLabel":20306, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.6" + } + ] + }, + "20309":{ + "inLabel":20309, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.9" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt7/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt7/zebra.conf new file mode 100644 index 0000000000..b5a28c7f1a --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt7/zebra.conf @@ -0,0 +1,40 @@ +log file zebra.log +! +hostname rt7 +! +log stdout notifications +log monitor notifications +log commands +! +!debug zebra packet +!debug zebra dplane +!debug zebra kernel +! +affinity-map blue bit-position 0 +! +interface lo + ip address 7.7.7.7/32 +! +interface eth-rt3 + ip address 10.37.0.7/24 +! +interface eth-rt6 + ip address 10.67.0.7/24 + link-params + affinity blue + exit-link-params +! +interface eth-rt8 + ip address 10.78.0.7/24 +! +interface eth-rt9 + ip address 10.79.0.7/24 + link-params + affinity blue + exit-link-params +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt8/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt8/isisd.conf new file mode 100644 index 0000000000..4ca45a8ad9 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt8/isisd.conf @@ -0,0 +1,50 @@ +password 1 +hostname rt8 +log file isisd.log +! +!debug isis events +!debug isis spf-events +!debug isis route-events +!debug isis sr-events +!debug isis lsp-gen +! +interface lo + ip router isis 1 + isis passive +! +interface eth-rt4 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt5 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt7 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +router isis 1 + lsp-gen-interval 2 + net 49.0000.0000.0000.1008.00 + is-type level-1 + topology ipv6-unicast + mpls-te on + ! + flex-algo 129 + dataplane sr-mpls + ! + segment-routing on + segment-routing global-block 20000 23999 + segment-routing node-msd 8 + segment-routing prefix 8.8.8.8/32 index 8 + segment-routing prefix 8.8.8.8/32 algorithm 128 index 108 + segment-routing prefix 8.8.8.8/32 algorithm 129 index 208 + segment-routing prefix 8.8.8.8/32 algorithm 130 index 308 +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt8/step1/route.json b/tests/topotests/isis_sr_flex_algo_topo2/rt8/step1/route.json new file mode 100644 index 0000000000..27470b76cc --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt8/step1/route.json @@ -0,0 +1,286 @@ +{ + "20000":{ + "inLabel":20000, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20000, + "outLabelStack":[ + 20000 + ], + "distance":150, + "installed":true, + "nexthop":"10.58.0.5" + } + ] + }, + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20001, + "outLabelStack":[ + 20001 + ], + "distance":150, + "installed":true, + "nexthop":"10.58.0.5" + }, + { + "type":"SR (IS-IS)", + "outLabel":20001, + "outLabelStack":[ + 20001 + ], + "distance":150, + "installed":true, + "nexthop":"10.48.0.4" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20002, + "outLabelStack":[ + 20002 + ], + "distance":150, + "installed":true, + "nexthop":"10.78.0.7" + }, + { + "type":"SR (IS-IS)", + "outLabel":20002, + "outLabelStack":[ + 20002 + ], + "distance":150, + "installed":true, + "nexthop":"10.58.0.5" + }, + { + "type":"SR (IS-IS)", + "outLabel":20002, + "outLabelStack":[ + 20002 + ], + "distance":150, + "installed":true, + "nexthop":"10.48.0.4" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20003, + "outLabelStack":[ + 20003 + ], + "distance":150, + "installed":true, + "nexthop":"10.78.0.7" + }, + { + "type":"SR (IS-IS)", + "outLabel":20003, + "outLabelStack":[ + 20003 + ], + "distance":150, + "installed":true, + "nexthop":"10.48.0.4" + } + ] + }, + "20004":{ + "inLabel":20004, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.48.0.4" + } + ] + }, + "20005":{ + "inLabel":20005, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.58.0.5" + } + ] + }, + "20006":{ + "inLabel":20006, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20006, + "outLabelStack":[ + 20006 + ], + "distance":150, + "installed":true, + "nexthop":"10.78.0.7" + }, + { + "type":"SR (IS-IS)", + "outLabel":20006, + "outLabelStack":[ + 20006 + ], + "distance":150, + "installed":true, + "nexthop":"10.58.0.5" + } + ] + }, + "20007":{ + "inLabel":20007, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.78.0.7" + } + ] + }, + "20009":{ + "inLabel":20009, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20009, + "outLabelStack":[ + 20009 + ], + "distance":150, + "installed":true, + "nexthop":"10.78.0.7" + } + ] + }, + "20200":{ + "inLabel":20200, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20200, + "outLabelStack":[ + 20200 + ], + "distance":150, + "installed":true, + "nexthop":"10.58.0.5" + } + ] + }, + "20205":{ + "inLabel":20205, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.58.0.5" + } + ] + }, + "20206":{ + "inLabel":20206, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20206, + "outLabelStack":[ + 20206 + ], + "distance":150, + "installed":true, + "nexthop":"10.78.0.7" + }, + { + "type":"SR (IS-IS)", + "outLabel":20206, + "outLabelStack":[ + 20206 + ], + "distance":150, + "installed":true, + "nexthop":"10.58.0.5" + } + ] + }, + "20207":{ + "inLabel":20207, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.78.0.7" + } + ] + }, + "20209":{ + "inLabel":20209, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20209, + "outLabelStack":[ + 20209 + ], + "distance":150, + "installed":true, + "nexthop":"10.78.0.7" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt8/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt8/zebra.conf new file mode 100644 index 0000000000..dd63f8cc2f --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt8/zebra.conf @@ -0,0 +1,29 @@ +log file zebra.log +! +hostname rt8 +! +log stdout notifications +log monitor notifications +log commands +! +!debug zebra packet +!debug zebra dplane +!debug zebra kernel +! +interface lo + ip address 8.8.8.8/32 +! +interface eth-rt4 + ip address 10.48.0.8/24 +! +interface eth-rt5 + ip address 10.58.0.8/24 +! +interface eth-rt7 + ip address 10.78.0.8/24 +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt9/bgpd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt9/bgpd.conf new file mode 100644 index 0000000000..386d8118e7 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt9/bgpd.conf @@ -0,0 +1,17 @@ +! +router bgp 1 + bgp router-id 9.9.9.9 + no bgp network import-check + neighbor 10.10.10.10 remote-as 1 + neighbor 10.10.10.10 update-source 9.9.9.9 + ! + address-family ipv4 unicast + network 10.255.9.0/24 + neighbor 10.10.10.10 next-hop-self + neighbor 10.10.10.10 route-map sr-te in + exit-address-family +! +route-map sr-te permit 10 + set sr-te color 1 +exit +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt9/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt9/isisd.conf new file mode 100644 index 0000000000..89eab274c7 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt9/isisd.conf @@ -0,0 +1,56 @@ +password 1 +hostname rt9 +log file isisd.log +! +!debug isis events +!debug isis spf-events +!debug isis route-events +!debug isis sr-events +!debug isis lsp-gen +! +affinity-map blue bit-position 0 +! +interface lo + ip router isis 1 + isis passive +! +interface eth-rt3 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt7 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +router isis 1 + lsp-gen-interval 2 + net 49.0000.0000.0000.1009.00 + is-type level-1 + topology ipv6-unicast + mpls-te on + ! + flex-algo 128 + dataplane sr-mpls + advertise-definition + ! + flex-algo 129 + dataplane sr-mpls + advertise-definition + ! + flex-algo 130 + dataplane sr-mpls + advertise-definition + affinity include-any blue + ! + segment-routing on + segment-routing global-block 20000 23999 + segment-routing node-msd 8 + segment-routing prefix 9.9.9.9/32 index 9 + segment-routing prefix 9.9.9.9/32 algorithm 128 index 109 + segment-routing prefix 9.9.9.9/32 algorithm 129 index 209 + segment-routing prefix 9.9.9.9/32 algorithm 130 index 309 +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt9/pathd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt9/pathd.conf new file mode 100644 index 0000000000..3f9a8d9059 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt9/pathd.conf @@ -0,0 +1,20 @@ +log file pathd.log +! +hostname rt9 +! +segment-routing + traffic-eng + segment-list sid-algorithm-0 + index 10 mpls label 20000 + exit + segment-list sid-algorithm-128 + index 10 mpls label 20100 + exit + segment-list sid-algorithm-129 + index 10 mpls label 20200 + exit + segment-list sid-algorithm-130 + index 10 mpls label 20300 + exit + exit +exit diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt9/step1/route.json b/tests/topotests/isis_sr_flex_algo_topo2/rt9/step1/route.json new file mode 100644 index 0000000000..e98680c5dc --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt9/step1/route.json @@ -0,0 +1,438 @@ +{ + "20000":{ + "inLabel":20000, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20000, + "outLabelStack":[ + 20000 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.7" + }, + { + "type":"SR (IS-IS)", + "outLabel":20000, + "outLabelStack":[ + 20000 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.3" + } + ] + }, + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20001, + "outLabelStack":[ + 20001 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.3" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20002, + "outLabelStack":[ + 20002 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.3" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.3" + } + ] + }, + "20004":{ + "inLabel":20004, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20004, + "outLabelStack":[ + 20004 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.3" + } + ] + }, + "20005":{ + "inLabel":20005, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20005, + "outLabelStack":[ + 20005 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.7" + } + ] + }, + "20006":{ + "inLabel":20006, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20006, + "outLabelStack":[ + 20006 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.7" + } + ] + }, + "20007":{ + "inLabel":20007, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.7" + } + ] + }, + "20008":{ + "inLabel":20008, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20008, + "outLabelStack":[ + 20008 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.7" + } + ] + }, + "20100":{ + "inLabel":20100, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20100, + "outLabelStack":[ + 20100 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.3" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20101, + "outLabelStack":[ + 20101 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.3" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20102, + "outLabelStack":[ + 20102 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.3" + } + ] + }, + "20104":{ + "inLabel":20104, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20104, + "outLabelStack":[ + 20104 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.3" + } + ] + }, + "20200":{ + "inLabel":20200, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20200, + "outLabelStack":[ + 20200 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.7" + } + ] + }, + "20205":{ + "inLabel":20205, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20205, + "outLabelStack":[ + 20205 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.7" + } + ] + }, + "20206":{ + "inLabel":20206, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20206, + "outLabelStack":[ + 20206 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.7" + } + ] + }, + "20207":{ + "inLabel":20207, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.7" + } + ] + }, + "20208":{ + "inLabel":20208, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20208, + "outLabelStack":[ + 20208 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.7" + } + ] + }, + "20300":{ + "inLabel":20300, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20300, + "outLabelStack":[ + 20300 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.7" + }, + { + "type":"SR (IS-IS)", + "outLabel":20300, + "outLabelStack":[ + 20300 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.3" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20301, + "outLabelStack":[ + 20301 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.3" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20302, + "outLabelStack":[ + 20302 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.3" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.3" + } + ] + }, + "20305":{ + "inLabel":20305, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20305, + "outLabelStack":[ + 20305 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.7" + } + ] + }, + "20306":{ + "inLabel":20306, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20306, + "outLabelStack":[ + 20306 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.7" + } + ] + }, + "20307":{ + "inLabel":20307, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.7" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt9/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt9/zebra.conf new file mode 100644 index 0000000000..378a1969be --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt9/zebra.conf @@ -0,0 +1,34 @@ +log file zebra.log +! +hostname rt9 +! +log stdout notifications +log monitor notifications +log commands +! +!debug zebra packet +!debug zebra dplane +!debug zebra kernel +! +affinity-map blue bit-position 0 +! +interface lo + ip address 9.9.9.9/32 +! +interface eth-rt3 + ip address 10.39.0.9/24 + link-params + affinity blue + exit-link-params +! +interface eth-rt7 + ip address 10.79.0.9/24 + link-params + affinity blue + exit-link-params +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/test_isis_sr_flex_algo_topo2.py b/tests/topotests/isis_sr_flex_algo_topo2/test_isis_sr_flex_algo_topo2.py new file mode 100755 index 0000000000..6a5f81def6 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/test_isis_sr_flex_algo_topo2.py @@ -0,0 +1,187 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Part of NetDEF Topology Tests +# +# Copyright 2021 by LINE Corporation, Hiroki Shirokura <hiroki.shirokura@linecorp.com> +# Copyright 2023 6WIND S.A. + +""" +test_isis_sr_flex_algo_topo2.py: + +[+] Flex-Algos 128 +[+] Flex-Algos 129 +[+] Flex-Algos 130 include-any blue + + +--------+ +--------+ + | | | | + | RT1 |------------------| RT2 | + | | | | + +--------+ +--------+ + / | \\ | \\ + / | \\ | \\ ++--------+ | \\ | \\ +| | | +--------+ | +--------+ +| RT0 | | | | | | | +| | | | RT4 |------------------| RT3 | ++--------+ | | | | | | + \\ | +--------+ | +--------+ + \\ | | | | \\ + +--------+ | +--------+ | \\ + | | | | | | +--------+ + | RT5 |-------|----------| RT6 | | | | + | | | | | | | RT9 | + +--------+ | +--------+ | | | + \\ | \\ | +--------+ + \\ | \\ | / + \\ | \\ | / + +--------+ +--------+ + | | | | + | RT8 |------------------| RT7 | + | | | | + +--------+ +--------+ +""" + +import os +import sys +import pytest +import json +import time +from functools import partial + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + + +pytestmark = [pytest.mark.isisd] + + +def build_topo(tgen): + "Build function" + + routers = [] + for i in range(0, 10): + rt = tgen.add_router("rt{}".format(i)) + rt.run("sysctl -w net.ipv4.fib_multipath_hash_policy=1") + + def connect_routers(tgen, left_idx, right_idx): + left = "rt{}".format(left_idx) + right = "rt{}".format(right_idx) + switch = tgen.add_switch("s-{}-{}".format(left, right)) + switch.add_link(tgen.gears[left], nodeif="eth-{}".format(right)) + switch.add_link(tgen.gears[right], nodeif="eth-{}".format(left)) + l_addr = "52:54:00:{}:{}:{}".format(left_idx, right_idx, left_idx) + tgen.gears[left].run("ip link set eth-{} down".format(right)) + tgen.gears[left].run("ip link set eth-{} address {}".format(right, l_addr)) + tgen.gears[left].run("ip link set eth-{} up".format(right)) + tgen.gears[left].run("sysctl -w net.mpls.conf.eth-{}.input=1".format(right)) + r_addr = "52:54:00:{}:{}:{}".format(left_idx, right_idx, right_idx) + tgen.gears[right].run("ip link set eth-{} down".format(left)) + tgen.gears[right].run("ip link set eth-{} address {}".format(left, r_addr)) + tgen.gears[right].run("ip link set eth-{} up".format(left)) + tgen.gears[right].run("sysctl -w net.mpls.conf.eth-{}.input=1".format(left)) + + connect_routers(tgen, 0, 1) + connect_routers(tgen, 0, 5) + connect_routers(tgen, 1, 2) + connect_routers(tgen, 1, 4) + connect_routers(tgen, 1, 5) + connect_routers(tgen, 2, 3) + connect_routers(tgen, 2, 6) + connect_routers(tgen, 3, 4) + connect_routers(tgen, 3, 7) + connect_routers(tgen, 3, 9) + connect_routers(tgen, 4, 8) + connect_routers(tgen, 5, 6) + connect_routers(tgen, 5, 8) + connect_routers(tgen, 6, 7) + connect_routers(tgen, 7, 8) + connect_routers(tgen, 7, 9) + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(build_topo, mod.__name__) + frrdir = tgen.config.get(tgen.CONFIG_SECTION, "frrdir") + if not os.path.isfile(os.path.join(frrdir, "pathd")): + pytest.skip("pathd daemon wasn't built") + tgen.start_topology() + router_list = tgen.routers() + + # For all registered routers, load the zebra configuration file + for rname, router in router_list.items(): + router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))) + router.load_config( TopoRouter.RD_ISIS, os.path.join(CWD, "{}/isisd.conf".format(rname))) + if rname in ["rt0", "rt9"]: + router.load_config( TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))) + router.load_config( TopoRouter.RD_PATH, os.path.join(CWD, "{}/pathd.conf".format(rname))) + router.run("ip link add dum0 type dummy") + router.run("ip link set dum0 up") + if rname == "rt0": + router.run("ip addr add 10.255.0.1/24 dev dum0") + elif rname == "rt9": + router.run("ip addr add 10.255.9.1/24 dev dum0") + tgen.start_router() + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + tgen.stop_topology() + + +def setup_testcase(msg): + logger.info(msg) + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + return tgen + +def open_json_file(filename): + try: + with open(filename, "r") as f: + return json.load(f) + except IOError: + assert False, "Could not read file {}".format(filename) + + +def check_rib(name, cmd, expected_file): + def _check(name, cmd, expected_file): + logger.info("polling") + tgen = get_topogen() + router = tgen.gears[name] + output = json.loads(router.vtysh_cmd(cmd)) + expected = open_json_file("{}/{}".format(CWD, expected_file)) + return topotest.json_cmp(output, expected) + + logger.info("[+] check {} \"{}\" {}".format(name, cmd, expected_file)) + tgen = get_topogen() + func = partial(_check, name, cmd, expected_file) + success, result = topotest.run_and_expect(func, None, count=120, wait=0.5) + assert result is None, "Failed" + + +def test_rib(): + check_rib("rt0", "show mpls table json", "rt0/step1/route.json") + check_rib("rt1", "show mpls table json", "rt1/step1/route.json") + check_rib("rt2", "show mpls table json", "rt2/step1/route.json") + check_rib("rt3", "show mpls table json", "rt3/step1/route.json") + check_rib("rt4", "show mpls table json", "rt4/step1/route.json") + check_rib("rt5", "show mpls table json", "rt5/step1/route.json") + check_rib("rt6", "show mpls table json", "rt6/step1/route.json") + check_rib("rt7", "show mpls table json", "rt7/step1/route.json") + check_rib("rt8", "show mpls table json", "rt8/step1/route.json") + check_rib("rt9", "show mpls table json", "rt9/step1/route.json") + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/ldp_oc_acl_topo1/r1/show_ip_ospf_neighbor.json b/tests/topotests/ldp_oc_acl_topo1/r1/show_ip_ospf_neighbor.json index 63281e9be3..c1c231de3d 100644 --- a/tests/topotests/ldp_oc_acl_topo1/r1/show_ip_ospf_neighbor.json +++ b/tests/topotests/ldp_oc_acl_topo1/r1/show_ip_ospf_neighbor.json @@ -2,9 +2,9 @@ "neighbors":{ "2.2.2.2":[ { - "priority":2, + "nbrPriority":2, "converged":"Full", - "address":"10.0.1.2", + "ifaceAddress":"10.0.1.2", "ifaceName":"r1-eth0:10.0.1.1" } ] diff --git a/tests/topotests/ldp_oc_acl_topo1/r2/show_ip_ospf_neighbor.json b/tests/topotests/ldp_oc_acl_topo1/r2/show_ip_ospf_neighbor.json index f361d605ce..ee69af5e23 100644 --- a/tests/topotests/ldp_oc_acl_topo1/r2/show_ip_ospf_neighbor.json +++ b/tests/topotests/ldp_oc_acl_topo1/r2/show_ip_ospf_neighbor.json @@ -2,25 +2,25 @@ "neighbors":{ "1.1.1.1":[ { - "priority":1, + "nbrPriority":1, "converged":"Full", - "address":"10.0.1.1", + "ifaceAddress":"10.0.1.1", "ifaceName":"r2-eth0:10.0.1.2" } ], "3.3.3.3":[ { - "priority":2, + "nbrPriority":2, "converged":"Full", - "address":"10.0.2.3", + "ifaceAddress":"10.0.2.3", "ifaceName":"r2-eth1:10.0.2.2" } ], "4.4.4.4":[ { - "priority":3, + "nbrPriority":3, "converged":"Full", - "address":"10.0.2.4", + "ifaceAddress":"10.0.2.4", "ifaceName":"r2-eth1:10.0.2.2" } ] diff --git a/tests/topotests/ldp_oc_acl_topo1/r3/show_ip_ospf_neighbor.json b/tests/topotests/ldp_oc_acl_topo1/r3/show_ip_ospf_neighbor.json index 38794357ff..3f76542e94 100644 --- a/tests/topotests/ldp_oc_acl_topo1/r3/show_ip_ospf_neighbor.json +++ b/tests/topotests/ldp_oc_acl_topo1/r3/show_ip_ospf_neighbor.json @@ -2,17 +2,17 @@ "neighbors":{ "2.2.2.2":[ { - "priority":1, + "nbrPriority":1, "converged":"Full", - "address":"10.0.2.2", + "ifaceAddress":"10.0.2.2", "ifaceName":"r3-eth0:10.0.2.3" } ], "4.4.4.4":[ { - "priority":3, + "nbrPriority":3, "converged":"Full", - "address":"10.0.2.4", + "ifaceAddress":"10.0.2.4", "ifaceName":"r3-eth0:10.0.2.3" } ] diff --git a/tests/topotests/ldp_oc_acl_topo1/r4/show_ip_ospf_neighbor.json b/tests/topotests/ldp_oc_acl_topo1/r4/show_ip_ospf_neighbor.json index fccca693b9..5395cd25c9 100644 --- a/tests/topotests/ldp_oc_acl_topo1/r4/show_ip_ospf_neighbor.json +++ b/tests/topotests/ldp_oc_acl_topo1/r4/show_ip_ospf_neighbor.json @@ -3,17 +3,17 @@ "neighbors":{ "2.2.2.2":[ { - "priority":1, + "nbrPriority":1, "converged":"Full", - "address":"10.0.2.2", + "ifaceAddress":"10.0.2.2", "ifaceName":"r4-eth0:10.0.2.4" } ], "3.3.3.3":[ { - "priority":2, + "nbrPriority":2, "converged":"Full", - "address":"10.0.2.3", + "ifaceAddress":"10.0.2.3", "ifaceName":"r4-eth0:10.0.2.4" } ] diff --git a/tests/topotests/ldp_oc_topo1/r1/show_ip_ospf_neighbor.json b/tests/topotests/ldp_oc_topo1/r1/show_ip_ospf_neighbor.json index 63281e9be3..c1c231de3d 100644 --- a/tests/topotests/ldp_oc_topo1/r1/show_ip_ospf_neighbor.json +++ b/tests/topotests/ldp_oc_topo1/r1/show_ip_ospf_neighbor.json @@ -2,9 +2,9 @@ "neighbors":{ "2.2.2.2":[ { - "priority":2, + "nbrPriority":2, "converged":"Full", - "address":"10.0.1.2", + "ifaceAddress":"10.0.1.2", "ifaceName":"r1-eth0:10.0.1.1" } ] diff --git a/tests/topotests/ldp_oc_topo1/r2/show_ip_ospf_neighbor.json b/tests/topotests/ldp_oc_topo1/r2/show_ip_ospf_neighbor.json index f361d605ce..ee69af5e23 100644 --- a/tests/topotests/ldp_oc_topo1/r2/show_ip_ospf_neighbor.json +++ b/tests/topotests/ldp_oc_topo1/r2/show_ip_ospf_neighbor.json @@ -2,25 +2,25 @@ "neighbors":{ "1.1.1.1":[ { - "priority":1, + "nbrPriority":1, "converged":"Full", - "address":"10.0.1.1", + "ifaceAddress":"10.0.1.1", "ifaceName":"r2-eth0:10.0.1.2" } ], "3.3.3.3":[ { - "priority":2, + "nbrPriority":2, "converged":"Full", - "address":"10.0.2.3", + "ifaceAddress":"10.0.2.3", "ifaceName":"r2-eth1:10.0.2.2" } ], "4.4.4.4":[ { - "priority":3, + "nbrPriority":3, "converged":"Full", - "address":"10.0.2.4", + "ifaceAddress":"10.0.2.4", "ifaceName":"r2-eth1:10.0.2.2" } ] diff --git a/tests/topotests/ldp_oc_topo1/r3/show_ip_ospf_neighbor.json b/tests/topotests/ldp_oc_topo1/r3/show_ip_ospf_neighbor.json index 38794357ff..3f76542e94 100644 --- a/tests/topotests/ldp_oc_topo1/r3/show_ip_ospf_neighbor.json +++ b/tests/topotests/ldp_oc_topo1/r3/show_ip_ospf_neighbor.json @@ -2,17 +2,17 @@ "neighbors":{ "2.2.2.2":[ { - "priority":1, + "nbrPriority":1, "converged":"Full", - "address":"10.0.2.2", + "ifaceAddress":"10.0.2.2", "ifaceName":"r3-eth0:10.0.2.3" } ], "4.4.4.4":[ { - "priority":3, + "nbrPriority":3, "converged":"Full", - "address":"10.0.2.4", + "ifaceAddress":"10.0.2.4", "ifaceName":"r3-eth0:10.0.2.3" } ] diff --git a/tests/topotests/ldp_oc_topo1/r4/show_ip_ospf_neighbor.json b/tests/topotests/ldp_oc_topo1/r4/show_ip_ospf_neighbor.json index fccca693b9..5395cd25c9 100644 --- a/tests/topotests/ldp_oc_topo1/r4/show_ip_ospf_neighbor.json +++ b/tests/topotests/ldp_oc_topo1/r4/show_ip_ospf_neighbor.json @@ -3,17 +3,17 @@ "neighbors":{ "2.2.2.2":[ { - "priority":1, + "nbrPriority":1, "converged":"Full", - "address":"10.0.2.2", + "ifaceAddress":"10.0.2.2", "ifaceName":"r4-eth0:10.0.2.4" } ], "3.3.3.3":[ { - "priority":2, + "nbrPriority":2, "converged":"Full", - "address":"10.0.2.3", + "ifaceAddress":"10.0.2.3", "ifaceName":"r4-eth0:10.0.2.4" } ] diff --git a/tests/topotests/ldp_sync_ospf_topo1/r1/show_ip_ospf_neighbor.json b/tests/topotests/ldp_sync_ospf_topo1/r1/show_ip_ospf_neighbor.json index 7efde22f3f..e25523d18d 100644 --- a/tests/topotests/ldp_sync_ospf_topo1/r1/show_ip_ospf_neighbor.json +++ b/tests/topotests/ldp_sync_ospf_topo1/r1/show_ip_ospf_neighbor.json @@ -2,24 +2,24 @@ "neighbors": { "2.2.2.2": [ { - "dbSummaryCounter": 0, - "retransmitCounter": 0, - "priority": 1, + "databaseSummaryListCounter": 0, + "linkStateRetransmissionListCounter": 0, + "nbrPriority": 1, "converged": "Full", - "address": "10.0.1.2", + "ifaceAddress": "10.0.1.2", "ifaceName": "r1-eth1:10.0.1.1", - "requestCounter": 0 + "linkStateRequestListCounter": 0 } ], "3.3.3.3": [ { - "dbSummaryCounter": 0, - "retransmitCounter": 0, - "priority": 1, + "databaseSummaryListCounter": 0, + "linkStateRetransmissionListCounter": 0, + "nbrPriority": 1, "converged": "Full", - "address": "10.0.2.3", + "ifaceAddress": "10.0.2.3", "ifaceName": "r1-eth2:10.0.2.1", - "requestCounter": 0 + "linkStateRequestListCounter": 0 } ] } diff --git a/tests/topotests/ldp_sync_ospf_topo1/r2/show_ip_ospf_neighbor.json b/tests/topotests/ldp_sync_ospf_topo1/r2/show_ip_ospf_neighbor.json index 5bea193e01..fa2ea86d67 100644 --- a/tests/topotests/ldp_sync_ospf_topo1/r2/show_ip_ospf_neighbor.json +++ b/tests/topotests/ldp_sync_ospf_topo1/r2/show_ip_ospf_neighbor.json @@ -2,24 +2,24 @@ "neighbors": { "1.1.1.1": [ { - "priority":1, + "nbrPriority":1, "converged":"Full", - "address":"10.0.1.1", + "ifaceAddress":"10.0.1.1", "ifaceName":"r2-eth1:10.0.1.2", - "retransmitCounter":0, - "requestCounter":0, - "dbSummaryCounter":0 + "linkStateRetransmissionListCounter":0, + "linkStateRequestListCounter":0, + "databaseSummaryListCounter":0 } ], "3.3.3.3": [ { - "priority":1, + "nbrPriority":1, "converged":"Full", - "address":"10.0.3.3", + "ifaceAddress":"10.0.3.3", "ifaceName":"r2-eth2:10.0.3.2", - "retransmitCounter":0, - "requestCounter":0, - "dbSummaryCounter":0 + "linkStateRetransmissionListCounter":0, + "linkStateRequestListCounter":0, + "databaseSummaryListCounter":0 } ] } diff --git a/tests/topotests/ldp_sync_ospf_topo1/r3/show_ip_ospf_neighbor.json b/tests/topotests/ldp_sync_ospf_topo1/r3/show_ip_ospf_neighbor.json index 9966297d8a..bf77e088d5 100644 --- a/tests/topotests/ldp_sync_ospf_topo1/r3/show_ip_ospf_neighbor.json +++ b/tests/topotests/ldp_sync_ospf_topo1/r3/show_ip_ospf_neighbor.json @@ -2,24 +2,24 @@ "neighbors": { "1.1.1.1": [ { - "priority":1, + "nbrPriority":1, "converged":"Full", - "address":"10.0.2.1", + "ifaceAddress":"10.0.2.1", "ifaceName":"r3-eth1:10.0.2.3", - "retransmitCounter":0, - "requestCounter":0, - "dbSummaryCounter":0 + "linkStateRetransmissionListCounter":0, + "linkStateRequestListCounter":0, + "databaseSummaryListCounter":0 } ], "2.2.2.2": [ { - "priority":1, + "nbrPriority":1, "converged":"Full", - "address":"10.0.3.2", + "ifaceAddress":"10.0.3.2", "ifaceName":"r3-eth2:10.0.3.3", - "retransmitCounter":0, - "requestCounter":0, - "dbSummaryCounter":0 + "linkStateRetransmissionListCounter":0, + "linkStateRequestListCounter":0, + "databaseSummaryListCounter":0 } ] } diff --git a/tests/topotests/ldp_topo1/r1/show_ip_ospf_neighbor.json b/tests/topotests/ldp_topo1/r1/show_ip_ospf_neighbor.json index d9192f1104..f47c2dfad7 100644 --- a/tests/topotests/ldp_topo1/r1/show_ip_ospf_neighbor.json +++ b/tests/topotests/ldp_topo1/r1/show_ip_ospf_neighbor.json @@ -2,12 +2,12 @@ "neighbors": { "2.2.2.2": [ { - "dbSummaryCounter": 0, - "retransmitCounter": 0, - "priority": 1, + "databaseSummaryListCounter": 0, + "linkStateRetransmissionListCounter": 0, + "nbrPriority": 1, "converged": "Full", - "address": "10.0.1.2", - "requestCounter": 0 + "ifaceAddress": "10.0.1.2", + "linkStateRequestListCounter": 0 } ] } diff --git a/tests/topotests/ldp_topo1/r2/show_ip_ospf_neighbor.json b/tests/topotests/ldp_topo1/r2/show_ip_ospf_neighbor.json index ea78592bd5..901282f876 100644 --- a/tests/topotests/ldp_topo1/r2/show_ip_ospf_neighbor.json +++ b/tests/topotests/ldp_topo1/r2/show_ip_ospf_neighbor.json @@ -2,40 +2,40 @@ "neighbors": { "1.1.1.1": [ { - "dbSummaryCounter": 0, - "retransmitCounter": 0, - "priority": 1, + "databaseSummaryListCounter": 0, + "linkStateRetransmissionListCounter": 0, + "nbrPriority": 1, "converged": "Full", - "address": "10.0.1.1", - "requestCounter": 0 + "ifaceAddress": "10.0.1.1", + "linkStateRequestListCounter": 0 } ], "3.3.3.3": [ { - "dbSummaryCounter": 0, - "retransmitCounter": 0, - "priority": 1, + "databaseSummaryListCounter": 0, + "linkStateRetransmissionListCounter": 0, + "nbrPriority": 1, "converged": "Full", - "address": "10.0.2.3", - "requestCounter": 0 + "ifaceAddress": "10.0.2.3", + "linkStateRequestListCounter": 0 }, { - "dbSummaryCounter": 0, - "retransmitCounter": 0, - "priority": 1, + "databaseSummaryListCounter": 0, + "linkStateRetransmissionListCounter": 0, + "nbrPriority": 1, "converged": "Full", - "address": "10.0.3.3", - "requestCounter": 0 + "ifaceAddress": "10.0.3.3", + "linkStateRequestListCounter": 0 } ], "4.4.4.4": [ { - "dbSummaryCounter": 0, - "retransmitCounter": 0, - "priority": 1, + "databaseSummaryListCounter": 0, + "linkStateRetransmissionListCounter": 0, + "nbrPriority": 1, "converged": "Full", - "address": "10.0.2.4", - "requestCounter": 0 + "ifaceAddress": "10.0.2.4", + "linkStateRequestListCounter": 0 } ] } diff --git a/tests/topotests/ldp_topo1/r3/show_ip_ospf_neighbor.json b/tests/topotests/ldp_topo1/r3/show_ip_ospf_neighbor.json index d3c50247ea..164040ae3e 100644 --- a/tests/topotests/ldp_topo1/r3/show_ip_ospf_neighbor.json +++ b/tests/topotests/ldp_topo1/r3/show_ip_ospf_neighbor.json @@ -2,30 +2,30 @@ "neighbors": { "2.2.2.2": [ { - "dbSummaryCounter": 0, - "retransmitCounter": 0, - "priority": 1, + "databaseSummaryListCounter": 0, + "linkStateRetransmissionListCounter": 0, + "nbrPriority": 1, "converged": "Full", - "address": "10.0.2.2", - "requestCounter": 0 + "ifaceAddress": "10.0.2.2", + "linkStateRequestListCounter": 0 }, { - "dbSummaryCounter": 0, - "retransmitCounter": 0, - "priority": 1, + "databaseSummaryListCounter": 0, + "linkStateRetransmissionListCounter": 0, + "nbrPriority": 1, "converged": "Full", - "address": "10.0.3.2", - "requestCounter": 0 + "ifaceAddress": "10.0.3.2", + "linkStateRequestListCounter": 0 } ], "4.4.4.4": [ { - "dbSummaryCounter": 0, - "retransmitCounter": 0, - "priority": 1, + "databaseSummaryListCounter": 0, + "linkStateRetransmissionListCounter": 0, + "nbrPriority": 1, "converged": "Full", - "address": "10.0.2.4", - "requestCounter": 0 + "ifaceAddress": "10.0.2.4", + "linkStateRequestListCounter": 0 } ] } diff --git a/tests/topotests/ldp_topo1/r4/show_ip_ospf_neighbor.json b/tests/topotests/ldp_topo1/r4/show_ip_ospf_neighbor.json index 20751a2884..98c759a6ff 100644 --- a/tests/topotests/ldp_topo1/r4/show_ip_ospf_neighbor.json +++ b/tests/topotests/ldp_topo1/r4/show_ip_ospf_neighbor.json @@ -2,22 +2,22 @@ "neighbors": { "2.2.2.2": [ { - "dbSummaryCounter": 0, - "retransmitCounter": 0, - "priority": 1, + "databaseSummaryListCounter": 0, + "linkStateRetransmissionListCounter": 0, + "nbrPriority": 1, "converged": "Full", - "address": "10.0.2.2", - "requestCounter": 0 + "ifaceAddress": "10.0.2.2", + "linkStateRequestListCounter": 0 } ], "3.3.3.3": [ { - "dbSummaryCounter": 0, - "retransmitCounter": 0, - "priority": 1, + "databaseSummaryListCounter": 0, + "linkStateRetransmissionListCounter": 0, + "nbrPriority": 1, "converged": "Full", - "address": "10.0.2.3", - "requestCounter": 0 + "ifaceAddress": "10.0.2.3", + "linkStateRequestListCounter": 0 } ] } diff --git a/tests/topotests/ldp_vpls_topo1/r1/show_ip_ospf_neighbor.json b/tests/topotests/ldp_vpls_topo1/r1/show_ip_ospf_neighbor.json index 90c8195416..9acb4f7b8c 100644 --- a/tests/topotests/ldp_vpls_topo1/r1/show_ip_ospf_neighbor.json +++ b/tests/topotests/ldp_vpls_topo1/r1/show_ip_ospf_neighbor.json @@ -2,24 +2,24 @@ "neighbors": { "2.2.2.2": [ { - "dbSummaryCounter": 0, - "retransmitCounter": 0, - "priority": 2, + "databaseSummaryListCounter": 0, + "linkStateRetransmissionListCounter": 0, + "nbrPriority": 2, "converged": "Full", - "address": "10.0.1.2", + "ifaceAddress": "10.0.1.2", "ifaceName": "r1-eth1:10.0.1.1", - "requestCounter": 0 + "linkStateRequestListCounter": 0 } ], "3.3.3.3": [ { - "dbSummaryCounter": 0, - "retransmitCounter": 0, - "priority": 2, + "databaseSummaryListCounter": 0, + "linkStateRetransmissionListCounter": 0, + "nbrPriority": 2, "converged": "Full", - "address": "10.0.2.3", + "ifaceAddress": "10.0.2.3", "ifaceName": "r1-eth2:10.0.2.1", - "requestCounter": 0 + "linkStateRequestListCounter": 0 } ] } diff --git a/tests/topotests/ldp_vpls_topo1/r2/show_ip_ospf_neighbor.json b/tests/topotests/ldp_vpls_topo1/r2/show_ip_ospf_neighbor.json index 29dde53c6d..6634199902 100644 --- a/tests/topotests/ldp_vpls_topo1/r2/show_ip_ospf_neighbor.json +++ b/tests/topotests/ldp_vpls_topo1/r2/show_ip_ospf_neighbor.json @@ -2,24 +2,24 @@ "neighbors": { "1.1.1.1": [ { - "priority":1, + "nbrPriority":1, "converged":"Full", - "address":"10.0.1.1", + "ifaceAddress":"10.0.1.1", "ifaceName":"r2-eth1:10.0.1.2", - "retransmitCounter":0, - "requestCounter":0, - "dbSummaryCounter":0 + "linkStateRetransmissionListCounter":0, + "linkStateRequestListCounter":0, + "databaseSummaryListCounter":0 } ], "3.3.3.3": [ { - "priority":2, + "nbrPriority":2, "converged":"Full", - "address":"10.0.3.3", + "ifaceAddress":"10.0.3.3", "ifaceName":"r2-eth2:10.0.3.2", - "retransmitCounter":0, - "requestCounter":0, - "dbSummaryCounter":0 + "linkStateRetransmissionListCounter":0, + "linkStateRequestListCounter":0, + "databaseSummaryListCounter":0 } ] } diff --git a/tests/topotests/ldp_vpls_topo1/r3/show_ip_ospf_neighbor.json b/tests/topotests/ldp_vpls_topo1/r3/show_ip_ospf_neighbor.json index 9966297d8a..bf77e088d5 100644 --- a/tests/topotests/ldp_vpls_topo1/r3/show_ip_ospf_neighbor.json +++ b/tests/topotests/ldp_vpls_topo1/r3/show_ip_ospf_neighbor.json @@ -2,24 +2,24 @@ "neighbors": { "1.1.1.1": [ { - "priority":1, + "nbrPriority":1, "converged":"Full", - "address":"10.0.2.1", + "ifaceAddress":"10.0.2.1", "ifaceName":"r3-eth1:10.0.2.3", - "retransmitCounter":0, - "requestCounter":0, - "dbSummaryCounter":0 + "linkStateRetransmissionListCounter":0, + "linkStateRequestListCounter":0, + "databaseSummaryListCounter":0 } ], "2.2.2.2": [ { - "priority":1, + "nbrPriority":1, "converged":"Full", - "address":"10.0.3.2", + "ifaceAddress":"10.0.3.2", "ifaceName":"r3-eth2:10.0.3.3", - "retransmitCounter":0, - "requestCounter":0, - "dbSummaryCounter":0 + "linkStateRetransmissionListCounter":0, + "linkStateRequestListCounter":0, + "databaseSummaryListCounter":0 } ] } diff --git a/tests/topotests/lib/common_config.py b/tests/topotests/lib/common_config.py index e5a1e75837..d19d8db75c 100644 --- a/tests/topotests/lib/common_config.py +++ b/tests/topotests/lib/common_config.py @@ -492,7 +492,7 @@ def save_initial_config_on_routers(tgen): # Get all running configs in parallel procs = {} for rname in router_list: - logger.info("Fetching running config for router %s", rname) + logger.debug("Fetching running config for router %s", rname) procs[rname] = router_list[rname].popen( ["/usr/bin/env", "vtysh", "-c", "show running-config no-header"], stdin=None, @@ -548,7 +548,7 @@ def reset_config_on_routers(tgen, routerName=None): # procs = {} for rname in router_list: - logger.info("Fetching running config for router %s", rname) + logger.debug("Fetching running config for router %s", rname) procs[rname] = router_list[rname].popen( ["/usr/bin/env", "vtysh", "-c", "show running-config no-header"], stdin=None, @@ -570,7 +570,7 @@ def reset_config_on_routers(tgen, routerName=None): # procs = {} for rname in router_list: - logger.info( + logger.debug( "Generating delta for router %s to new configuration (gen %d)", rname, gen ) procs[rname] = tgen.net.popen( @@ -599,7 +599,7 @@ def reset_config_on_routers(tgen, routerName=None): # procs = {} for rname in router_list: - logger.info("Applying delta config on router %s", rname) + logger.debug("Applying delta config on router %s", rname) procs[rname] = router_list[rname].popen( ["/usr/bin/env", "vtysh", "-f", delta_fmt.format(rname, gen)], @@ -611,7 +611,7 @@ def reset_config_on_routers(tgen, routerName=None): output, _ = p.communicate() vtysh_command = "vtysh -f {}".format(delta_fmt.format(rname, gen)) if not p.returncode: - router_list[rname].logger.info( + router_list[rname].logger.debug( '\nvtysh config apply => "{}"\nvtysh output <= "{}"'.format( vtysh_command, output ) @@ -640,7 +640,7 @@ def reset_config_on_routers(tgen, routerName=None): if show_router_config: procs = {} for rname in router_list: - logger.info("Fetching running config for router %s", rname) + logger.debug("Fetching running config for router %s", rname) procs[rname] = router_list[rname].popen( ["/usr/bin/env", "vtysh", "-c", "show running-config no-header"], stdin=None, @@ -657,7 +657,7 @@ def reset_config_on_routers(tgen, routerName=None): output, ) else: - logger.info( + logger.debug( "Configuration on router %s after reset:\n%s", rname, output ) @@ -742,7 +742,7 @@ def load_config_to_routers(tgen, routers, save_bkup=False): frr_cfg_bkup = frr_cfg_bkup_fmt.format(rname) with open(frr_cfg_file, "r+") as cfg: data = cfg.read() - logger.info( + logger.debug( "Applying following configuration on router %s (gen: %d):\n%s", rname, gen, @@ -775,7 +775,7 @@ def load_config_to_routers(tgen, routers, save_bkup=False): frr_cfg_file = frr_cfg_file_fmt.format(rname) vtysh_command = "vtysh -f " + frr_cfg_file if not p.returncode: - router_list[rname].logger.info( + router_list[rname].logger.debug( '\nvtysh config apply => "{}"\nvtysh output <= "{}"'.format( vtysh_command, output ) @@ -821,7 +821,7 @@ def load_config_to_routers(tgen, routers, save_bkup=False): output, ) else: - logger.info("New configuration for router %s:\n%s", rname, output) + logger.debug("New configuration for router %s:\n%s", rname, output) logger.debug("Exiting API: load_config_to_routers") return not errors @@ -957,10 +957,10 @@ def generate_support_bundle(): bundle_procs[rname] = tgen.net[rname].popen(gen_sup_cmd, stdin=None) for rname, rnode in router_list.items(): - logger.info("Waiting on support bundle for %s", rname) + logger.debug("Waiting on support bundle for %s", rname) output, error = bundle_procs[rname].communicate() if output: - logger.info( + logger.debug( "Output from collecting support bundle for %s:\n%s", rname, output ) if error: @@ -1234,15 +1234,15 @@ def add_interfaces_to_vlan(tgen, input_dict): cmd = "ip link add link {} name {} type vlan id {}".format( interface, vlan_intf, vlan ) - logger.info("[DUT: %s]: Running command: %s", dut, cmd) + logger.debug("[DUT: %s]: Running command: %s", dut, cmd) result = rnode.run(cmd) - logger.info("result %s", result) + logger.debug("result %s", result) # Bringing interface up cmd = "ip link set {} up".format(vlan_intf) - logger.info("[DUT: %s]: Running command: %s", dut, cmd) + logger.debug("[DUT: %s]: Running command: %s", dut, cmd) result = rnode.run(cmd) - logger.info("result %s", result) + logger.debug("result %s", result) # Assigning IP address ifaddr = ipaddress.ip_interface( @@ -1254,9 +1254,9 @@ def add_interfaces_to_vlan(tgen, input_dict): cmd = "ip -{0} a flush {1} scope global && ip a add {2} dev {1} && ip l set {1} up".format( ifaddr.version, vlan_intf, ifaddr ) - logger.info("[DUT: %s]: Running command: %s", dut, cmd) + logger.debug("[DUT: %s]: Running command: %s", dut, cmd) result = rnode.run(cmd) - logger.info("result %s", result) + logger.debug("result %s", result) def tcpdump_capture_start( @@ -1567,12 +1567,16 @@ def create_vrf_cfg(tgen, topo, input_dict=None, build=False): vrf["name"], vrf["id"] ) - logger.info("[DUT: %s]: Running kernel cmd [%s]", c_router, cmd) + logger.debug( + "[DUT: %s]: Running kernel cmd [%s]", c_router, cmd + ) rnode.run(cmd) # Kernel cmd - Bring down VRF cmd = "ip link set dev {} down".format(name) - logger.info("[DUT: %s]: Running kernel cmd [%s]", c_router, cmd) + logger.debug( + "[DUT: %s]: Running kernel cmd [%s]", c_router, cmd + ) rnode.run(cmd) else: @@ -1581,14 +1585,14 @@ def create_vrf_cfg(tgen, topo, input_dict=None, build=False): cmd = "ip link add {} type vrf table {}".format( name, table_id ) - logger.info( + logger.debug( "[DUT: %s]: Running kernel cmd " "[%s]", c_router, cmd ) rnode.run(cmd) # Kernel cmd - Bring up VRF cmd = "ip link set dev {} up".format(name) - logger.info( + logger.debug( "[DUT: %s]: Running kernel " "cmd [%s]", c_router, cmd ) rnode.run(cmd) @@ -1616,7 +1620,7 @@ def create_vrf_cfg(tgen, topo, input_dict=None, build=False): interface_name, _vrf ) - logger.info( + logger.debug( "[DUT: %s]: Running" " kernel cmd [%s]", c_router, cmd, @@ -1683,7 +1687,7 @@ def create_interface_in_kernel( cmd = "ip -{0} a flush {1} scope global && ip a add {2} dev {1} && ip l set {1} up".format( ifaddr.version, name, ifaddr ) - logger.info("[DUT: %s]: Running command: %s", dut, cmd) + logger.debug("[DUT: %s]: Running command: %s", dut, cmd) rnode.run(cmd) if vrf: @@ -1715,7 +1719,7 @@ def shutdown_bringup_interface_in_kernel(tgen, dut, intf_name, ifaceaction=False action = "down" cmd = "{} {} {}".format(cmd, intf_name, action) - logger.info("[DUT: %s]: Running command: %s", dut, cmd) + logger.debug("[DUT: %s]: Running command: %s", dut, cmd) rnode.run(cmd) @@ -1968,7 +1972,7 @@ def retry(retry_timeout, initial_wait=0, expected=True, diag_pct=0.75): ) if initial_wait > 0: - logger.info("Waiting for [%s]s as initial delay", initial_wait) + logger.debug("Waiting for [%s]s as initial delay", initial_wait) sleep(initial_wait) invert_logic = not _expected @@ -2027,13 +2031,13 @@ def retry(retry_timeout, initial_wait=0, expected=True, diag_pct=0.75): return saved_failure if saved_failure: - logger.info( + logger.debug( "RETRY DIAG: [failure] Sleeping %ds until next retry with %.1f retry time left - too see if timeout was too short", retry_sleep, seconds_left, ) else: - logger.info( + logger.debug( "Sleeping %ds until next retry with %.1f retry time left", retry_sleep, seconds_left, @@ -3357,7 +3361,19 @@ def socat_send_mld_join( # Run socat command to send IGMP join logger.info("[DUT: {}]: Running command: [{}]".format(server, socat_cmd)) - output = rnode.run("set +m; {} sleep 0.5".format(socat_cmd)) + output = rnode.run("set +m; {} echo $!".format(socat_cmd)) + + # Check if socat join process is running + if output: + pid = output.split()[0] + rnode.run("touch /var/run/frr/socat_join.pid") + rnode.run("echo %s >> /var/run/frr/socat_join.pid" % pid) + else: + errormsg = "Socat join is not sent for {}. Error {}".format( + mld_group, output + ) + logger.error(output) + return errormsg logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) return True @@ -3415,7 +3431,7 @@ def socat_send_pim6_traffic( if multicast_hops: socat_cmd += "multicast-hops=255'" - socat_cmd += " &>{}/socat.logs &".format(tgen.logdir) + socat_cmd += " >{}/socat.logs &".format(tgen.logdir) # Run socat command to send pim6 traffic logger.info( @@ -3435,7 +3451,20 @@ def socat_send_pim6_traffic( ) rnode.run("chmod 755 {}".format(traffic_shell_script)) - output = rnode.run("{} &> /dev/null".format(traffic_shell_script)) + output = rnode.run("{} &>/dev/null & echo $!".format(traffic_shell_script)) + + # Check if socat traffic process is running + if output: + pid = output.split()[0] + rnode.run("touch /var/run/frr/socat_traffic.pid") + rnode.run("echo %s >> /var/run/frr/socat_traffic.pid" % pid) + + else: + errormsg = "Socat traffic is not sent for {}. Error {}".format( + mld_group, output + ) + logger.error(output) + return errormsg logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) return True @@ -3465,18 +3494,30 @@ def kill_socat(tgen, dut=None, action=None): if dut is not None and router != dut: continue + traffic_shell_script = "{}/{}/traffic.sh".format(tgen.logdir, router) + pid_socat_join = rnode.run("cat /var/run/frr/socat_join.pid") + pid_socat_traffic = rnode.run("cat /var/run/frr/socat_traffic.pid") if action == "remove_mld_join": - cmd = "ps -ef | grep socat | grep UDP6-RECV | grep {}".format(router) + pids = pid_socat_join elif action == "remove_mld_traffic": - cmd = "ps -ef | grep socat | grep UDP6-SEND | grep {}".format(router) + pids = pid_socat_traffic else: - cmd = "ps -ef | grep socat".format(router) - - awk_cmd = "awk -F' ' '{print $2}' | xargs kill -9 &>/dev/null &" - cmd = "{} | {}".format(cmd, awk_cmd) + pids = "\n".join([pid_socat_join, pid_socat_traffic]) - logger.debug("[DUT: {}]: Running command: [{}]".format(router, cmd)) - rnode.run(cmd) + if os.path.exists(traffic_shell_script): + cmd = ( + "ps -ef | grep %s | awk -F' ' '{print $2}' | xargs kill -9" + % traffic_shell_script + ) + logger.debug("[DUT: {}]: Running command: [{}]".format(router, cmd)) + rnode.run(cmd) + + for pid in pids.split("\n"): + pid = pid.strip() + if pid.isdigit(): + cmd = "set +m; kill -9 %s &> /dev/null" % pid + logger.debug("[DUT: {}]: Running command: [{}]".format(router, cmd)) + rnode.run(cmd) logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) diff --git a/tests/topotests/lib/grpc-query.py b/tests/topotests/lib/grpc-query.py index 6457bbefdd..5dd12d581e 100755 --- a/tests/topotests/lib/grpc-query.py +++ b/tests/topotests/lib/grpc-query.py @@ -21,7 +21,8 @@ try: import grpc import grpc_tools - from micronet import commander + sys.path.append(os.path.dirname(CWD)) + from munet.base import commander commander.cmd_raises(f"cp {CWD}/../../../grpc/frr-northbound.proto .") commander.cmd_raises( diff --git a/tests/topotests/lib/micronet.py b/tests/topotests/lib/micronet.py index 1381009168..f4aa8278f1 100644 --- a/tests/topotests/lib/micronet.py +++ b/tests/topotests/lib/micronet.py @@ -3,1004 +3,22 @@ # # July 9 2021, Christian Hopps <chopps@labn.net> # -# Copyright (c) 2021, LabN Consulting, L.L.C. +# Copyright (c) 2021-2023, LabN Consulting, L.L.C. # -import datetime -import logging -import os -import re -import shlex -import subprocess -import sys -import tempfile -import time as time_mod -import traceback - -root_hostname = subprocess.check_output("hostname") - -# This allows us to cleanup any leftovers later on -os.environ["MICRONET_PID"] = str(os.getpid()) - - -class Timeout(object): - def __init__(self, delta): - self.started_on = datetime.datetime.now() - self.expires_on = self.started_on + datetime.timedelta(seconds=delta) - - def elapsed(self): - elapsed = datetime.datetime.now() - self.started_on - return elapsed.total_seconds() - - def is_expired(self): - return datetime.datetime.now() > self.expires_on - - -def is_string(value): - """Return True if value is a string.""" - try: - return isinstance(value, basestring) # type: ignore - except NameError: - return isinstance(value, str) - - -def shell_quote(command): - """Return command wrapped in single quotes.""" - if sys.version_info[0] >= 3: - return shlex.quote(command) - return "'{}'".format(command.replace("'", "'\"'\"'")) # type: ignore - - -def cmd_error(rc, o, e): - s = "rc {}".format(rc) - o = "\n\tstdout: " + o.strip() if o and o.strip() else "" - e = "\n\tstderr: " + e.strip() if e and e.strip() else "" - return s + o + e - - -def proc_error(p, o, e): - args = p.args if is_string(p.args) else " ".join(p.args) - s = "rc {} pid {}\n\targs: {}".format(p.returncode, p.pid, args) - o = "\n\tstdout: " + o.strip() if o and o.strip() else "" - e = "\n\tstderr: " + e.strip() if e and e.strip() else "" - return s + o + e - - -def comm_error(p): - rc = p.poll() - assert rc is not None - if not hasattr(p, "saved_output"): - p.saved_output = p.communicate() - return proc_error(p, *p.saved_output) - - -class Commander(object): # pylint: disable=R0205 - """ - Commander. - - An object that can execute commands. - """ - - tmux_wait_gen = 0 - - def __init__(self, name, logger=None): - """Create a Commander.""" - self.name = name - self.last = None - self.exec_paths = {} - self.pre_cmd = [] - self.pre_cmd_str = "" - - if not logger: - self.logger = logging.getLogger(__name__ + ".commander." + name) - else: - self.logger = logger - - self.cwd = self.cmd_raises("pwd").strip() - - def set_logger(self, logfile): - self.logger = logging.getLogger(__name__ + ".commander." + self.name) - if is_string(logfile): - handler = logging.FileHandler(logfile, mode="w") - else: - handler = logging.StreamHandler(logfile) - - fmtstr = "%(asctime)s.%(msecs)03d %(levelname)s: {}({}): %(message)s".format( - self.__class__.__name__, self.name - ) - handler.setFormatter(logging.Formatter(fmt=fmtstr)) - self.logger.addHandler(handler) - - def set_pre_cmd(self, pre_cmd=None): - if not pre_cmd: - self.pre_cmd = [] - self.pre_cmd_str = "" - else: - self.pre_cmd = pre_cmd - self.pre_cmd_str = " ".join(self.pre_cmd) + " " - - def __str__(self): - return "Commander({})".format(self.name) - - def get_exec_path(self, binary): - """Return the full path to the binary executable. - - `binary` :: binary name or list of binary names - """ - if is_string(binary): - bins = [binary] - else: - bins = binary - for b in bins: - if b in self.exec_paths: - return self.exec_paths[b] - - rc, output, _ = self.cmd_status("which " + b, warn=False) - if not rc: - return os.path.abspath(output.strip()) - return None - - def get_tmp_dir(self, uniq): - return os.path.join(tempfile.mkdtemp(), uniq) - - def test(self, flags, arg): - """Run test binary, with flags and arg""" - test_path = self.get_exec_path(["test"]) - rc, output, _ = self.cmd_status([test_path, flags, arg], warn=False) - return not rc - - def path_exists(self, path): - """Check if path exists.""" - return self.test("-e", path) - - def _get_cmd_str(self, cmd): - if is_string(cmd): - return self.pre_cmd_str + cmd - cmd = self.pre_cmd + cmd - return " ".join(cmd) - - def _get_sub_args(self, cmd, defaults, **kwargs): - if is_string(cmd): - defaults["shell"] = True - pre_cmd = self.pre_cmd_str - else: - defaults["shell"] = False - pre_cmd = self.pre_cmd - cmd = [str(x) for x in cmd] - defaults.update(kwargs) - return pre_cmd, cmd, defaults - - def _popen(self, method, cmd, skip_pre_cmd=False, **kwargs): - if sys.version_info[0] >= 3: - defaults = { - "encoding": "utf-8", - "stdout": subprocess.PIPE, - "stderr": subprocess.PIPE, - } - else: - defaults = { - "stdout": subprocess.PIPE, - "stderr": subprocess.PIPE, - } - pre_cmd, cmd, defaults = self._get_sub_args(cmd, defaults, **kwargs) - - self.logger.debug('%s: %s("%s", kwargs: %s)', self, method, cmd, defaults) - - actual_cmd = cmd if skip_pre_cmd else pre_cmd + cmd - p = subprocess.Popen(actual_cmd, **defaults) - if not hasattr(p, "args"): - p.args = actual_cmd - return p, actual_cmd - - def set_cwd(self, cwd): - self.logger.warning("%s: 'cd' (%s) does not work outside namespaces", self, cwd) - self.cwd = cwd - - def popen(self, cmd, **kwargs): - """ - Creates a pipe with the given `command`. - - Args: - command: `str` or `list` of command to open a pipe with. - **kwargs: kwargs is eventually passed on to Popen. If `command` is a string - then will be invoked with shell=True, otherwise `command` is a list and - will be invoked with shell=False. - - Returns: - a subprocess.Popen object. - """ - p, _ = self._popen("popen", cmd, **kwargs) - return p - - def cmd_status(self, cmd, raises=False, warn=True, stdin=None, **kwargs): - """Execute a command.""" - - # We are not a shell like mininet, so we need to intercept this - chdir = False - if not is_string(cmd): - cmds = cmd - else: - # XXX we can drop this when the code stops assuming it works - m = re.match(r"cd(\s*|\s+(\S+))$", cmd) - if m and m.group(2): - self.logger.warning( - "Bad call to 'cd' (chdir) emulating, use self.set_cwd():\n%s", - "".join(traceback.format_stack(limit=12)), - ) - assert is_string(cmd) - chdir = True - cmd += " && pwd" - - # If we are going to run under bash then we don't need shell=True! - cmds = ["/bin/bash", "-c", cmd] - - pinput = None - - if is_string(stdin) or isinstance(stdin, bytes): - pinput = stdin - stdin = subprocess.PIPE - - p, actual_cmd = self._popen("cmd_status", cmds, stdin=stdin, **kwargs) - stdout, stderr = p.communicate(input=pinput) - rc = p.wait() - - # For debugging purposes. - self.last = (rc, actual_cmd, cmd, stdout, stderr) - - if rc: - if warn: - self.logger.warning( - "%s: proc failed: %s:", self, proc_error(p, stdout, stderr) - ) - if raises: - # error = Exception("stderr: {}".format(stderr)) - # This annoyingly doesn't' show stderr when printed normally - error = subprocess.CalledProcessError(rc, actual_cmd) - error.stdout, error.stderr = stdout, stderr - raise error - elif chdir: - self.set_cwd(stdout.strip()) - - return rc, stdout, stderr - - def cmd_legacy(self, cmd, **kwargs): - """Execute a command with stdout and stderr joined, *IGNORES ERROR*.""" - - defaults = {"stderr": subprocess.STDOUT} - defaults.update(kwargs) - _, stdout, _ = self.cmd_status(cmd, raises=False, **defaults) - return stdout - - def cmd_raises(self, cmd, **kwargs): - """Execute a command. Raise an exception on errors""" - - rc, stdout, _ = self.cmd_status(cmd, raises=True, **kwargs) - assert rc == 0 - return stdout - - # Run a command in a new window (gnome-terminal, screen, tmux, xterm) - def run_in_window( - self, - cmd, - wait_for=False, - background=False, - name=None, - title=None, - forcex=False, - new_window=False, - tmux_target=None, - ): - """ - Run a command in a new window (TMUX, Screen or XTerm). - - Args: - wait_for: True to wait for exit from command or `str` as channel neme to signal on exit, otherwise False - background: Do not change focus to new window. - title: Title for new pane (tmux) or window (xterm). - name: Name of the new window (tmux) - forcex: Force use of X11. - new_window: Open new window (instead of pane) in TMUX - tmux_target: Target for tmux pane. - - Returns: - the pane/window identifier from TMUX (depends on `new_window`) - """ - - channel = None - if is_string(wait_for): - channel = wait_for - elif wait_for is True: - channel = "{}-wait-{}".format(os.getpid(), Commander.tmux_wait_gen) - Commander.tmux_wait_gen += 1 - - sudo_path = self.get_exec_path(["sudo"]) - nscmd = sudo_path + " " + self.pre_cmd_str + cmd - if "TMUX" in os.environ and not forcex: - cmd = [self.get_exec_path("tmux")] - if new_window: - cmd.append("new-window") - cmd.append("-P") - if name: - cmd.append("-n") - cmd.append(name) - if tmux_target: - cmd.append("-t") - cmd.append(tmux_target) - else: - cmd.append("split-window") - cmd.append("-P") - cmd.append("-h") - if not tmux_target: - tmux_target = os.getenv("TMUX_PANE", "") - if background: - cmd.append("-d") - if tmux_target: - cmd.append("-t") - cmd.append(tmux_target) - if title: - nscmd = "printf '\033]2;{}\033\\'; {}".format(title, nscmd) - if channel: - nscmd = 'trap "tmux wait -S {}; exit 0" EXIT; {}'.format(channel, nscmd) - cmd.append(nscmd) - elif "STY" in os.environ and not forcex: - # wait for not supported in screen for now - channel = None - cmd = [self.get_exec_path("screen")] - if title: - cmd.append("-t") - cmd.append(title) - if not os.path.exists( - "/run/screen/S-{}/{}".format(os.environ["USER"], os.environ["STY"]) - ): - cmd = ["sudo", "-u", os.environ["SUDO_USER"]] + cmd - cmd.extend(nscmd.split(" ")) - elif "DISPLAY" in os.environ: - # We need it broken up for xterm - user_cmd = cmd - cmd = [self.get_exec_path("xterm")] - if "SUDO_USER" in os.environ: - cmd = [self.get_exec_path("sudo"), "-u", os.environ["SUDO_USER"]] + cmd - if title: - cmd.append("-T") - cmd.append(title) - cmd.append("-e") - cmd.append(sudo_path) - cmd.extend(self.pre_cmd) - cmd.extend(["bash", "-c", user_cmd]) - # if channel: - # return self.cmd_raises(cmd, skip_pre_cmd=True) - # else: - p = self.popen( - cmd, - skip_pre_cmd=True, - stdin=None, - shell=False, - ) - time_mod.sleep(2) - if p.poll() is not None: - self.logger.error("%s: Failed to launch xterm: %s", self, comm_error(p)) - return p - else: - self.logger.error( - "DISPLAY, STY, and TMUX not in environment, can't open window" - ) - raise Exception("Window requestd but TMUX, Screen and X11 not available") - - pane_info = self.cmd_raises(cmd, skip_pre_cmd=True).strip() - - # Re-adjust the layout - if "TMUX" in os.environ: - self.cmd_status( - "tmux select-layout -t {} tiled".format( - pane_info if not tmux_target else tmux_target - ), - skip_pre_cmd=True, - ) - - # Wait here if we weren't handed the channel to wait for - if channel and wait_for is True: - cmd = [self.get_exec_path("tmux"), "wait", channel] - self.cmd_status(cmd, skip_pre_cmd=True) - - return pane_info - - def delete(self): - pass - - -class LinuxNamespace(Commander): - """ - A linux Namespace. - - An object that creates and executes commands in a linux namespace - """ - - def __init__( - self, - name, - net=True, - mount=True, - uts=True, - cgroup=False, - ipc=False, - pid=False, - time=False, - user=False, - set_hostname=True, - private_mounts=None, - logger=None, - ): - """ - Create a new linux namespace. - - Args: - name: Internal name for the namespace. - net: Create network namespace. - mount: Create network namespace. - uts: Create UTS (hostname) namespace. - cgroup: Create cgroup namespace. - ipc: Create IPC namespace. - pid: Create PID namespace, also mounts new /proc. - time: Create time namespace. - user: Create user namespace, also keeps capabilities. - set_hostname: Set the hostname to `name`, uts must also be True. - private_mounts: List of strings of the form - "[/external/path:]/internal/path. If no external path is specified a - tmpfs is mounted on the internal path. Any paths specified are first - passed to `mkdir -p`. - logger: Passed to superclass. - """ - super(LinuxNamespace, self).__init__(name, logger) - - self.logger.debug("%s: Creating", self) - - self.intfs = [] - - nslist = [] - cmd = ["/usr/bin/unshare"] - flags = "" - self.a_flags = [] - self.ifnetns = {} - - if cgroup: - nslist.append("cgroup") - flags += "C" - if ipc: - nslist.append("ipc") - flags += "i" - if mount: - nslist.append("mnt") - flags += "m" - if net: - nslist.append("net") - flags += "n" - if pid: - nslist.append("pid") - flags += "f" - flags += "p" - cmd.append("--mount-proc") - if time: - # XXX this filename is probably wrong - nslist.append("time") - flags += "T" - if user: - nslist.append("user") - flags += "U" - cmd.append("--keep-caps") - if uts: - nslist.append("uts") - flags += "u" - - if flags: - aflags = flags.replace("f", "") - if aflags: - self.a_flags = ["-" + x for x in aflags] - cmd.extend(["-" + x for x in flags]) - - if pid: - cmd.append(commander.get_exec_path("tini")) - cmd.append("-vvv") - cmd.append("/bin/cat") - - # Using cat and a stdin PIPE is nice as it will exit when we do. However, we - # also detach it from the pgid so that signals do not propagate to it. This is - # b/c it would exit early (e.g., ^C) then, at least the main micronet proc which - # has no other processes like frr daemons running, will take the main network - # namespace with it, which will remove the bridges and the veth pair (because - # the bridge side veth is deleted). - self.logger.debug("%s: creating namespace process: %s", self, cmd) - p = subprocess.Popen( - cmd, - stdin=subprocess.PIPE, - stdout=open("/dev/null", "w"), - stderr=open("/dev/null", "w"), - text=True, - start_new_session=True, # detach from pgid so signals don't propagate - shell=False, - ) - self.p = p - self.pid = p.pid - - self.logger.debug("%s: namespace pid: %d", self, self.pid) - - # ----------------------------------------------- - # Now let's wait until unshare completes it's job - # ----------------------------------------------- - timeout = Timeout(30) - while p.poll() is None and not timeout.is_expired(): - for fname in tuple(nslist): - ours = os.readlink("/proc/self/ns/{}".format(fname)) - theirs = os.readlink("/proc/{}/ns/{}".format(self.pid, fname)) - # See if their namespace is different - if ours != theirs: - nslist.remove(fname) - if not nslist: - break - elapsed = int(timeout.elapsed()) - if elapsed <= 3: - time_mod.sleep(0.1) - elif elapsed > 10: - self.logger.warning("%s: unshare taking more than %ss", self, elapsed) - time_mod.sleep(3) - else: - self.logger.info("%s: unshare taking more than %ss", self, elapsed) - time_mod.sleep(1) - assert p.poll() is None, "unshare unexpectedly exited!" - assert not nslist, "unshare never unshared!" - - # Set pre-command based on our namespace proc - self.base_pre_cmd = ["/usr/bin/nsenter", *self.a_flags, "-t", str(self.pid)] - if not pid: - self.base_pre_cmd.append("-F") - self.set_pre_cmd(self.base_pre_cmd + ["--wd=" + self.cwd]) - - # Remount sysfs and cgroup to pickup any changes - self.cmd_raises("mount -t sysfs sysfs /sys") - self.cmd_raises( - "mount -o rw,nosuid,nodev,noexec,relatime -t cgroup2 cgroup /sys/fs/cgroup" - ) - - # Set the hostname to the namespace name - if uts and set_hostname: - # Debugging get the root hostname - self.cmd_raises("hostname " + self.name) - nroot = subprocess.check_output("hostname") - if root_hostname != nroot: - result = self.p.poll() - assert root_hostname == nroot, "STATE of namespace process {}".format( - result - ) - - if private_mounts: - if is_string(private_mounts): - private_mounts = [private_mounts] - for m in private_mounts: - s = m.split(":", 1) - if len(s) == 1: - self.tmpfs_mount(s[0]) - else: - self.bind_mount(s[0], s[1]) - - o = self.cmd_legacy("ls -l /proc/{}/ns".format(self.pid)) - self.logger.debug("namespaces:\n %s", o) - - # Doing this here messes up all_protocols ipv6 check - self.cmd_raises("ip link set lo up") - - def __str__(self): - return "LinuxNamespace({})".format(self.name) - - def tmpfs_mount(self, inner): - self.cmd_raises("mkdir -p " + inner) - self.cmd_raises("mount -n -t tmpfs tmpfs " + inner) - - def bind_mount(self, outer, inner): - self.cmd_raises("mkdir -p " + inner) - self.cmd_raises("mount --rbind {} {} ".format(outer, inner)) - - def add_vlan(self, vlanname, linkiface, vlanid): - self.logger.debug("Adding VLAN interface: %s (%s)", vlanname, vlanid) - ip_path = self.get_exec_path("ip") - assert ip_path, "XXX missing ip command!" - self.cmd_raises( - [ - ip_path, - "link", - "add", - "link", - linkiface, - "name", - vlanname, - "type", - "vlan", - "id", - vlanid, - ] - ) - self.cmd_raises([ip_path, "link", "set", "dev", vlanname, "up"]) - - def add_loop(self, loopname): - self.logger.debug("Adding Linux iface: %s", loopname) - ip_path = self.get_exec_path("ip") - assert ip_path, "XXX missing ip command!" - self.cmd_raises([ip_path, "link", "add", loopname, "type", "dummy"]) - self.cmd_raises([ip_path, "link", "set", "dev", loopname, "up"]) - - def add_l3vrf(self, vrfname, tableid): - self.logger.debug("Adding Linux VRF: %s", vrfname) - ip_path = self.get_exec_path("ip") - assert ip_path, "XXX missing ip command!" - self.cmd_raises( - [ip_path, "link", "add", vrfname, "type", "vrf", "table", tableid] - ) - self.cmd_raises([ip_path, "link", "set", "dev", vrfname, "up"]) - - def del_iface(self, iface): - self.logger.debug("Removing Linux Iface: %s", iface) - ip_path = self.get_exec_path("ip") - assert ip_path, "XXX missing ip command!" - self.cmd_raises([ip_path, "link", "del", iface]) - - def attach_iface_to_l3vrf(self, ifacename, vrfname): - self.logger.debug("Attaching Iface %s to Linux VRF %s", ifacename, vrfname) - ip_path = self.get_exec_path("ip") - assert ip_path, "XXX missing ip command!" - if vrfname: - self.cmd_raises( - [ip_path, "link", "set", "dev", ifacename, "master", vrfname] - ) - else: - self.cmd_raises([ip_path, "link", "set", "dev", ifacename, "nomaster"]) - - def add_netns(self, ns): - self.logger.debug("Adding network namespace %s", ns) - - ip_path = self.get_exec_path("ip") - assert ip_path, "XXX missing ip command!" - if os.path.exists("/run/netns/{}".format(ns)): - self.logger.warning("%s: Removing existing nsspace %s", self, ns) - try: - self.delete_netns(ns) - except Exception as ex: - self.logger.warning( - "%s: Couldn't remove existing nsspace %s: %s", - self, - ns, - str(ex), - exc_info=True, - ) - self.cmd_raises([ip_path, "netns", "add", ns]) - - def delete_netns(self, ns): - self.logger.debug("Deleting network namespace %s", ns) - - ip_path = self.get_exec_path("ip") - assert ip_path, "XXX missing ip command!" - self.cmd_raises([ip_path, "netns", "delete", ns]) - - def set_intf_netns(self, intf, ns, up=False): - # In case a user hard-codes 1 thinking it "resets" - ns = str(ns) - if ns == "1": - ns = str(self.pid) - - self.logger.debug("Moving interface %s to namespace %s", intf, ns) - - cmd = "ip link set {} netns " + ns - if up: - cmd += " up" - self.intf_ip_cmd(intf, cmd) - if ns == str(self.pid): - # If we are returning then remove from dict - if intf in self.ifnetns: - del self.ifnetns[intf] - else: - self.ifnetns[intf] = ns - - def reset_intf_netns(self, intf): - self.logger.debug("Moving interface %s to default namespace", intf) - self.set_intf_netns(intf, str(self.pid)) - - def intf_ip_cmd(self, intf, cmd): - """Run an ip command for considering an interfaces possible namespace. - - `cmd` - format is run using the interface name on the command - """ - if intf in self.ifnetns: - assert cmd.startswith("ip ") - cmd = "ip -n " + self.ifnetns[intf] + cmd[2:] - self.cmd_raises(cmd.format(intf)) - - def set_cwd(self, cwd): - # Set pre-command based on our namespace proc - self.logger.debug("%s: new CWD %s", self, cwd) - self.set_pre_cmd(self.base_pre_cmd + ["--wd=" + cwd]) - - def register_interface(self, ifname): - if ifname not in self.intfs: - self.intfs.append(ifname) - - def delete(self): - if self.p and self.p.poll() is None: - if sys.version_info[0] >= 3: - try: - self.p.terminate() - self.p.communicate(timeout=10) - except subprocess.TimeoutExpired: - self.p.kill() - self.p.communicate(timeout=2) - else: - self.p.kill() - self.p.communicate() - self.set_pre_cmd(["/bin/false"]) - - -class SharedNamespace(Commander): - """ - Share another namespace. - - An object that executes commands in an existing pid's linux namespace - """ - - def __init__(self, name, pid, aflags=("-a",), logger=None): - """ - Share a linux namespace. - - Args: - name: Internal name for the namespace. - pid: PID of the process to share with. - """ - super(SharedNamespace, self).__init__(name, logger) - - self.logger.debug("%s: Creating", self) - - self.pid = pid - self.intfs = [] - self.a_flags = aflags - - # Set pre-command based on our namespace proc - self.set_pre_cmd( - ["/usr/bin/nsenter", *self.a_flags, "-t", str(self.pid), "--wd=" + self.cwd] - ) - - def __str__(self): - return "SharedNamespace({})".format(self.name) - - def set_cwd(self, cwd): - # Set pre-command based on our namespace proc - self.logger.debug("%s: new CWD %s", self, cwd) - self.set_pre_cmd( - ["/usr/bin/nsenter", *self.a_flags, "-t", str(self.pid), "--wd=" + cwd] - ) - - def register_interface(self, ifname): - if ifname not in self.intfs: - self.intfs.append(ifname) - - -class Bridge(SharedNamespace): - """ - A linux bridge. - """ - - next_brid_ord = 0 - - @classmethod - def _get_next_brid(cls): - brid_ord = cls.next_brid_ord - cls.next_brid_ord += 1 - return brid_ord - - def __init__(self, name=None, unet=None, logger=None): - """Create a linux Bridge.""" - - self.unet = unet - self.brid_ord = self._get_next_brid() - if name: - self.brid = name - else: - self.brid = "br{}".format(self.brid_ord) - name = self.brid - - super(Bridge, self).__init__(name, unet.pid, aflags=unet.a_flags, logger=logger) - - self.logger.debug("Bridge: Creating") - - assert len(self.brid) <= 16 # Make sure fits in IFNAMSIZE - self.cmd_raises("ip link delete {} || true".format(self.brid)) - self.cmd_raises("ip link add {} type bridge".format(self.brid)) - self.cmd_raises("ip link set {} up".format(self.brid)) - - self.logger.debug("%s: Created, Running", self) - - def __str__(self): - return "Bridge({})".format(self.brid) - - def delete(self): - """Stop the bridge (i.e., delete the linux resources).""" - - rc, o, e = self.cmd_status("ip link show {}".format(self.brid), warn=False) - if not rc: - rc, o, e = self.cmd_status( - "ip link delete {}".format(self.brid), warn=False - ) - if rc: - self.logger.error( - "%s: error deleting bridge %s: %s", - self, - self.brid, - cmd_error(rc, o, e), - ) - else: - self.logger.debug("%s: Deleted.", self) - - -class Micronet(LinuxNamespace): # pylint: disable=R0205 - """ - Micronet. - """ - - def __init__(self): - """Create a Micronet.""" - - self.hosts = {} - self.switches = {} - self.links = {} - self.macs = {} - self.rmacs = {} - - super(Micronet, self).__init__("micronet", mount=True, net=True, uts=True) - - self.logger.debug("%s: Creating", self) - - def __str__(self): - return "Micronet()" - - def __getitem__(self, key): - if key in self.switches: - return self.switches[key] - return self.hosts[key] - - def add_host(self, name, cls=LinuxNamespace, **kwargs): - """Add a host to micronet.""" - - self.logger.debug("%s: add_host %s", self, name) - - self.hosts[name] = cls(name, **kwargs) - # Create a new mounted FS for tracking nested network namespaces creatd by the - # user with `ip netns add` - self.hosts[name].tmpfs_mount("/run/netns") - - def add_link(self, name1, name2, if1, if2): - """Add a link between switch and host to micronet.""" - isp2p = False - if name1 in self.switches: - assert name2 in self.hosts - elif name2 in self.switches: - assert name1 in self.hosts - name1, name2 = name2, name1 - if1, if2 = if2, if1 - else: - # p2p link - assert name1 in self.hosts - assert name2 in self.hosts - isp2p = True - - lname = "{}:{}-{}:{}".format(name1, if1, name2, if2) - self.logger.debug("%s: add_link %s%s", self, lname, " p2p" if isp2p else "") - self.links[lname] = (name1, if1, name2, if2) - - # And create the veth now. - if isp2p: - lhost, rhost = self.hosts[name1], self.hosts[name2] - lifname = "i1{:x}".format(lhost.pid) - rifname = "i2{:x}".format(rhost.pid) - self.cmd_raises( - "ip link add {} type veth peer name {}".format(lifname, rifname) - ) - - self.cmd_raises("ip link set {} netns {}".format(lifname, lhost.pid)) - lhost.cmd_raises("ip link set {} name {}".format(lifname, if1)) - lhost.cmd_raises("ip link set {} up".format(if1)) - lhost.register_interface(if1) - - self.cmd_raises("ip link set {} netns {}".format(rifname, rhost.pid)) - rhost.cmd_raises("ip link set {} name {}".format(rifname, if2)) - rhost.cmd_raises("ip link set {} up".format(if2)) - rhost.register_interface(if2) - else: - switch = self.switches[name1] - host = self.hosts[name2] - - assert len(if1) <= 16 and len(if2) <= 16 # Make sure fits in IFNAMSIZE - - self.logger.debug("%s: Creating veth pair for link %s", self, lname) - self.cmd_raises( - "ip link add {} type veth peer name {} netns {}".format( - if1, if2, host.pid - ) - ) - self.cmd_raises("ip link set {} netns {}".format(if1, switch.pid)) - switch.register_interface(if1) - host.register_interface(if2) - self.cmd_raises("ip link set {} master {}".format(if1, switch.brid)) - self.cmd_raises("ip link set {} up".format(if1)) - host.cmd_raises("ip link set {} up".format(if2)) - - # Cache the MAC values, and reverse mapping - self.get_mac(name1, if1) - self.get_mac(name2, if2) - - def add_switch(self, name): - """Add a switch to micronet.""" - - self.logger.debug("%s: add_switch %s", self, name) - self.switches[name] = Bridge(name, self) - - def get_mac(self, name, ifname): - if name in self.hosts: - dev = self.hosts[name] - else: - dev = self.switches[name] - - if (name, ifname) not in self.macs: - _, output, _ = dev.cmd_status("ip -o link show " + ifname) - m = re.match(".*link/(loopback|ether) ([0-9a-fA-F:]+) .*", output) - mac = m.group(2) - self.macs[(name, ifname)] = mac - self.rmacs[mac] = (name, ifname) - - return self.macs[(name, ifname)] - - def delete(self): - """Delete the micronet topology.""" - - self.logger.debug("%s: Deleting.", self) - - for lname, (_, _, rname, rif) in self.links.items(): - host = self.hosts[rname] - - self.logger.debug("%s: Deleting veth pair for link %s", self, lname) - - rc, o, e = host.cmd_status("ip link delete {}".format(rif), warn=False) - if rc: - self.logger.error( - "Error deleting veth pair %s: %s", lname, cmd_error(rc, o, e) - ) - - self.links = {} - - for host in self.hosts.values(): - try: - host.delete() - except Exception as error: - self.logger.error( - "%s: error while deleting host %s: %s", self, host, error - ) - - self.hosts = {} - - for switch in self.switches.values(): - try: - switch.delete() - except Exception as error: - self.logger.error( - "%s: error while deleting switch %s: %s", self, switch, error - ) - self.switches = {} - - self.logger.debug("%s: Deleted.", self) - - super(Micronet, self).delete() - - -# --------------------------- -# Root level utility function -# --------------------------- - - -def get_exec_path(binary): - base = Commander("base") - return base.get_exec_path(binary) - - -commander = Commander("micronet") +# flake8: noqa + +from munet.base import BaseMunet as Micronet +from munet.base import ( + Bridge, + Commander, + LinuxNamespace, + SharedNamespace, + Timeout, + cmd_error, + comm_error, + commander, + get_exec_path, + proc_error, + root_hostname, + shell_quote, +) diff --git a/tests/topotests/lib/micronet_cli.py b/tests/topotests/lib/micronet_cli.py deleted file mode 100644 index e54b75f710..0000000000 --- a/tests/topotests/lib/micronet_cli.py +++ /dev/null @@ -1,306 +0,0 @@ -# -*- coding: utf-8 eval: (blacken-mode 1) -*- -# SPDX-License-Identifier: GPL-2.0-or-later -# -# July 24 2021, Christian Hopps <chopps@labn.net> -# -# Copyright (c) 2021, LabN Consulting, L.L.C. -# -import argparse -import logging -import os -import pty -import re -import readline -import select -import socket -import subprocess -import sys -import tempfile -import termios -import tty - - -ENDMARKER = b"\x00END\x00" - - -def lineiter(sock): - s = "" - while True: - sb = sock.recv(256) - if not sb: - return - - s += sb.decode("utf-8") - i = s.find("\n") - if i != -1: - yield s[:i] - s = s[i + 1 :] - - -def spawn(unet, host, cmd): - if sys.stdin.isatty(): - old_tty = termios.tcgetattr(sys.stdin) - tty.setraw(sys.stdin.fileno()) - try: - master_fd, slave_fd = pty.openpty() - - # use os.setsid() make it run in a new process group, or bash job - # control will not be enabled - p = unet.hosts[host].popen( - cmd, - preexec_fn=os.setsid, - stdin=slave_fd, - stdout=slave_fd, - stderr=slave_fd, - universal_newlines=True, - ) - - while p.poll() is None: - r, w, e = select.select([sys.stdin, master_fd], [], [], 0.25) - if sys.stdin in r: - d = os.read(sys.stdin.fileno(), 10240) - os.write(master_fd, d) - elif master_fd in r: - o = os.read(master_fd, 10240) - if o: - os.write(sys.stdout.fileno(), o) - finally: - # restore tty settings back - if sys.stdin.isatty(): - termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_tty) - - -def doline(unet, line, writef): - def host_cmd_split(unet, cmd): - csplit = cmd.split() - for i, e in enumerate(csplit): - if e not in unet.hosts: - break - hosts = csplit[:i] - if not hosts: - hosts = sorted(unet.hosts.keys()) - cmd = " ".join(csplit[i:]) - return hosts, cmd - - line = line.strip() - m = re.match(r"^(\S+)(?:\s+(.*))?$", line) - if not m: - return True - - cmd = m.group(1) - oargs = m.group(2) if m.group(2) else "" - if cmd == "q" or cmd == "quit": - return False - if cmd == "hosts": - writef("%% hosts: %s\n" % " ".join(sorted(unet.hosts.keys()))) - elif cmd in ["term", "vtysh", "xterm"]: - args = oargs.split() - if not args or (len(args) == 1 and args[0] == "*"): - args = sorted(unet.hosts.keys()) - hosts = [unet.hosts[x] for x in args if x in unet.hosts] - for host in hosts: - if cmd == "t" or cmd == "term": - host.run_in_window("bash", title="sh-%s" % host) - elif cmd == "v" or cmd == "vtysh": - host.run_in_window("vtysh", title="vt-%s" % host) - elif cmd == "x" or cmd == "xterm": - host.run_in_window("bash", title="sh-%s" % host, forcex=True) - elif cmd == "sh": - hosts, cmd = host_cmd_split(unet, oargs) - for host in hosts: - if sys.stdin.isatty(): - spawn(unet, host, cmd) - else: - if len(hosts) > 1: - writef("------ Host: %s ------\n" % host) - output = unet.hosts[host].cmd_legacy(cmd) - writef(output) - if len(hosts) > 1: - writef("------- End: %s ------\n" % host) - writef("\n") - elif cmd == "h" or cmd == "help": - writef( - """ -Commands: - help :: this help - sh [hosts] <shell-command> :: execute <shell-command> on <host> - term [hosts] :: open shell terminals for hosts - vtysh [hosts] :: open vtysh terminals for hosts - [hosts] <vtysh-command> :: execute vtysh-command on hosts\n\n""" - ) - else: - hosts, cmd = host_cmd_split(unet, line) - for host in hosts: - if len(hosts) > 1: - writef("------ Host: %s ------\n" % host) - output = unet.hosts[host].cmd_legacy('vtysh -c "{}"'.format(cmd)) - writef(output) - if len(hosts) > 1: - writef("------- End: %s ------\n" % host) - writef("\n") - return True - - -def cli_server_setup(unet): - sockdir = tempfile.mkdtemp("-sockdir", "pyt") - sockpath = os.path.join(sockdir, "cli-server.sock") - try: - sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - sock.settimeout(10) - sock.bind(sockpath) - sock.listen(1) - return sock, sockdir, sockpath - except Exception: - unet.cmd_status("rm -rf " + sockdir) - raise - - -def cli_server(unet, server_sock): - sock, addr = server_sock.accept() - - # Go into full non-blocking mode now - sock.settimeout(None) - - for line in lineiter(sock): - line = line.strip() - - def writef(x): - xb = x.encode("utf-8") - sock.send(xb) - - if not doline(unet, line, writef): - return - sock.send(ENDMARKER) - - -def cli_client(sockpath, prompt="unet> "): - sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - sock.settimeout(10) - sock.connect(sockpath) - - # Go into full non-blocking mode now - sock.settimeout(None) - - print("\n--- Micronet CLI Starting ---\n\n") - while True: - if sys.version_info[0] == 2: - line = raw_input(prompt) # pylint: disable=E0602 - else: - line = input(prompt) - if line is None: - return - - # Need to put \n back - line += "\n" - - # Send the CLI command - sock.send(line.encode("utf-8")) - - def bendswith(b, sentinel): - slen = len(sentinel) - return len(b) >= slen and b[-slen:] == sentinel - - # Collect the output - rb = b"" - while not bendswith(rb, ENDMARKER): - lb = sock.recv(4096) - if not lb: - return - rb += lb - - # Remove the marker - rb = rb[: -len(ENDMARKER)] - - # Write the output - sys.stdout.write(rb.decode("utf-8")) - - -def local_cli(unet, outf, prompt="unet> "): - print("\n--- Micronet CLI Starting ---\n\n") - while True: - if sys.version_info[0] == 2: - line = raw_input(prompt) # pylint: disable=E0602 - else: - line = input(prompt) - if line is None: - return - if not doline(unet, line, outf.write): - return - - -def cli( - unet, - histfile=None, - sockpath=None, - force_window=False, - title=None, - prompt=None, - background=True, -): - logger = logging.getLogger("cli-client") - - if prompt is None: - prompt = "unet> " - - if force_window or not sys.stdin.isatty(): - # Run CLI in another window b/c we have no tty. - sock, sockdir, sockpath = cli_server_setup(unet) - - python_path = unet.get_exec_path(["python3", "python"]) - us = os.path.realpath(__file__) - cmd = "{} {}".format(python_path, us) - if histfile: - cmd += " --histfile=" + histfile - if title: - cmd += " --prompt={}".format(title) - cmd += " " + sockpath - - try: - unet.run_in_window(cmd, new_window=True, title=title, background=background) - return cli_server(unet, sock) - finally: - unet.cmd_status("rm -rf " + sockdir) - - if not unet: - logger.debug("client-cli using sockpath %s", sockpath) - - try: - if histfile is None: - histfile = os.path.expanduser("~/.micronet-history.txt") - if not os.path.exists(histfile): - if unet: - unet.cmd("touch " + histfile) - else: - subprocess.run("touch " + histfile) - if histfile: - readline.read_history_file(histfile) - except Exception: - pass - - try: - if sockpath: - cli_client(sockpath, prompt=prompt) - else: - local_cli(unet, sys.stdout, prompt=prompt) - except EOFError: - pass - except Exception as ex: - logger.critical("cli: got exception: %s", ex, exc_info=True) - raise - finally: - readline.write_history_file(histfile) - - -if __name__ == "__main__": - logging.basicConfig(level=logging.DEBUG, filename="/tmp/topotests/cli-client.log") - logger = logging.getLogger("cli-client") - logger.info("Start logging cli-client") - - parser = argparse.ArgumentParser() - parser.add_argument("--histfile", help="file to user for history") - parser.add_argument("--prompt-text", help="prompt string to use") - parser.add_argument("socket", help="path to pair of sockets to communicate over") - args = parser.parse_args() - - prompt = "{}> ".format(args.prompt_text) if args.prompt_text else "unet> " - cli(None, args.histfile, args.socket, prompt=prompt) diff --git a/tests/topotests/lib/micronet_compat.py b/tests/topotests/lib/micronet_compat.py index 5a69c56d8d..211d61fb6d 100644 --- a/tests/topotests/lib/micronet_compat.py +++ b/tests/topotests/lib/micronet_compat.py @@ -3,140 +3,43 @@ # # July 11 2021, Christian Hopps <chopps@labn.net> # -# Copyright (c) 2021, LabN Consulting, L.L.C +# Copyright (c) 2021-2023, LabN Consulting, L.L.C # - -import glob -import logging +import ipaddress import os -import signal -import time - -from lib.micronet import LinuxNamespace, Micronet -from lib.micronet_cli import cli - - -def get_pids_with_env(has_var, has_val=None): - result = {} - for pidenv in glob.iglob("/proc/*/environ"): - pid = pidenv.split("/")[2] - try: - with open(pidenv, "rb") as rfb: - envlist = [ - x.decode("utf-8").split("=", 1) for x in rfb.read().split(b"\0") - ] - envlist = [[x[0], ""] if len(x) == 1 else x for x in envlist] - envdict = dict(envlist) - if has_var not in envdict: - continue - if has_val is None: - result[pid] = envdict - elif envdict[has_var] == str(has_val): - result[pid] = envdict - except Exception: - # E.g., process exited and files are gone - pass - return result - - -def _kill_piddict(pids_by_upid, sig): - for upid, pids in pids_by_upid: - logging.info( - "Sending %s to (%s) of micronet pid %s", sig, ", ".join(pids), upid - ) - for pid in pids: - try: - os.kill(int(pid), sig) - except Exception: - pass - - -def _get_our_pids(): - ourpid = str(os.getpid()) - piddict = get_pids_with_env("MICRONET_PID", ourpid) - pids = [x for x in piddict if x != ourpid] - if pids: - return {ourpid: pids} - return {} - - -def _get_other_pids(): - piddict = get_pids_with_env("MICRONET_PID") - unet_pids = {d["MICRONET_PID"] for d in piddict.values()} - pids_by_upid = {p: set() for p in unet_pids} - for pid, envdict in piddict.items(): - pids_by_upid[envdict["MICRONET_PID"]].add(pid) - # Filter out any child pid sets whos micronet pid is still running - return {x: y for x, y in pids_by_upid.items() if x not in y} - - -def _get_pids_by_upid(ours): - if ours: - return _get_our_pids() - return _get_other_pids() - - -def _cleanup_pids(ours): - pids_by_upid = _get_pids_by_upid(ours).items() - if not pids_by_upid: - return - - _kill_piddict(pids_by_upid, signal.SIGTERM) - - # Give them 5 second to exit cleanly - logging.info("Waiting up to 5s to allow for clean exit of abandon'd pids") - for _ in range(0, 5): - pids_by_upid = _get_pids_by_upid(ours).items() - if not pids_by_upid: - return - time.sleep(1) - - pids_by_upid = _get_pids_by_upid(ours).items() - _kill_piddict(pids_by_upid, signal.SIGKILL) - - -def cleanup_current(): - """Attempt to cleanup preview runs. - Currently this only scans for old processes. - """ - logging.info("reaping current micronet processes") - _cleanup_pids(True) - - -def cleanup_previous(): - """Attempt to cleanup preview runs. - - Currently this only scans for old processes. - """ - logging.info("reaping past micronet processes") - _cleanup_pids(False) +from munet import cli +from munet.base import BaseMunet, LinuxNamespace class Node(LinuxNamespace): """Node (mininet compat).""" - def __init__(self, name, **kwargs): - """ - Create a Node. - """ - self.params = kwargs + def __init__(self, name, rundir=None, **kwargs): + nkwargs = {} + + if "unet" in kwargs: + nkwargs["unet"] = kwargs["unet"] if "private_mounts" in kwargs: - private_mounts = kwargs["private_mounts"] - else: - private_mounts = kwargs.get("privateDirs", []) + nkwargs["private_mounts"] = kwargs["private_mounts"] - logger = kwargs.get("logger") + # This is expected by newer munet CLI code + self.config_dirname = "" + self.config = {"kind": "frr"} + self.mgmt_ip = None + self.mgmt_ip6 = None - super(Node, self).__init__(name, logger=logger, private_mounts=private_mounts) + super().__init__(name, **nkwargs) + + self.rundir = self.unet.rundir.joinpath(self.name) def cmd(self, cmd, **kwargs): """Execute a command, joins stdout, stderr, ignores exit status.""" return super(Node, self).cmd_legacy(cmd, **kwargs) - def config(self, lo="up", **params): + def config_host(self, lo="up", **params): """Called by Micronet when topology is built (but not started).""" # mininet brings up loopback here. del params @@ -148,25 +51,79 @@ class Node(LinuxNamespace): def terminate(self): return + def add_vlan(self, vlanname, linkiface, vlanid): + self.logger.debug("Adding VLAN interface: %s (%s)", vlanname, vlanid) + ip_path = self.get_exec_path("ip") + assert ip_path, "XXX missing ip command!" + self.cmd_raises( + [ + ip_path, + "link", + "add", + "link", + linkiface, + "name", + vlanname, + "type", + "vlan", + "id", + vlanid, + ] + ) + self.cmd_raises([ip_path, "link", "set", "dev", vlanname, "up"]) + + def add_loop(self, loopname): + self.logger.debug("Adding Linux iface: %s", loopname) + ip_path = self.get_exec_path("ip") + assert ip_path, "XXX missing ip command!" + self.cmd_raises([ip_path, "link", "add", loopname, "type", "dummy"]) + self.cmd_raises([ip_path, "link", "set", "dev", loopname, "up"]) + + def add_l3vrf(self, vrfname, tableid): + self.logger.debug("Adding Linux VRF: %s", vrfname) + ip_path = self.get_exec_path("ip") + assert ip_path, "XXX missing ip command!" + self.cmd_raises( + [ip_path, "link", "add", vrfname, "type", "vrf", "table", tableid] + ) + self.cmd_raises([ip_path, "link", "set", "dev", vrfname, "up"]) + + def del_iface(self, iface): + self.logger.debug("Removing Linux Iface: %s", iface) + ip_path = self.get_exec_path("ip") + assert ip_path, "XXX missing ip command!" + self.cmd_raises([ip_path, "link", "del", iface]) + + def attach_iface_to_l3vrf(self, ifacename, vrfname): + self.logger.debug("Attaching Iface %s to Linux VRF %s", ifacename, vrfname) + ip_path = self.get_exec_path("ip") + assert ip_path, "XXX missing ip command!" + if vrfname: + self.cmd_raises( + [ip_path, "link", "set", "dev", ifacename, "master", vrfname] + ) + else: + self.cmd_raises([ip_path, "link", "set", "dev", ifacename, "nomaster"]) + + set_cwd = LinuxNamespace.set_ns_cwd + class Topo(object): # pylint: disable=R0205 def __init__(self, *args, **kwargs): raise Exception("Remove Me") -class Mininet(Micronet): +class Mininet(BaseMunet): """ Mininet using Micronet. """ g_mnet_inst = None - def __init__(self, controller=None): + def __init__(self, rundir=None): """ Create a Micronet. """ - assert not controller - if Mininet.g_mnet_inst is not None: Mininet.g_mnet_inst.stop() Mininet.g_mnet_inst = self @@ -181,7 +138,146 @@ class Mininet(Micronet): # to set permissions to root:frr 770 to make this unneeded in that case # os.umask(0) - super(Mininet, self).__init__() + super(Mininet, self).__init__(pid=False, rundir=rundir) + + # From munet/munet/native.py + with open(os.path.join(self.rundir, "nspid"), "w", encoding="ascii") as f: + f.write(f"{self.pid}\n") + + with open(os.path.join(self.rundir, "nspids"), "w", encoding="ascii") as f: + f.write(f'{" ".join([str(x) for x in self.pids])}\n') + + hosts_file = os.path.join(self.rundir, "hosts.txt") + with open(hosts_file, "w", encoding="ascii") as hf: + hf.write( + f"""127.0.0.1\tlocalhost {self.name} +::1\tip6-localhost ip6-loopback +fe00::0\tip6-localnet +ff00::0\tip6-mcastprefix +ff02::1\tip6-allnodes +ff02::2\tip6-allrouters +""" + ) + self.bind_mount(hosts_file, "/etc/hosts") + + # Common CLI commands for any topology + cdict = { + "commands": [ + # + # Window commands. + # + { + "name": "pcap", + "format": "pcap NETWORK", + "help": ( + "capture packets from NETWORK into file capture-NETWORK.pcap" + " the command is run within a new window which also shows" + " packet summaries. NETWORK can also be an interface specified" + " as HOST:INTF. To capture inside the host namespace." + ), + "exec": "tshark -s 9200 -i {0} -P -w capture-{0}.pcap", + "top-level": True, + "new-window": {"background": True}, + }, + { + "name": "term", + "format": "term HOST [HOST ...]", + "help": "open terminal[s] (TMUX or XTerm) on HOST[S], * for all", + "exec": "bash", + "new-window": True, + }, + { + "name": "vtysh", + "exec": "/usr/bin/vtysh", + "format": "vtysh ROUTER [ROUTER ...]", + "new-window": True, + "kinds": ["frr"], + }, + { + "name": "xterm", + "format": "xterm HOST [HOST ...]", + "help": "open XTerm[s] on HOST[S], * for all", + "exec": "bash", + "new-window": { + "forcex": True, + }, + }, + { + "name": "logd", + "exec": "tail -F %RUNDIR%/{}.log", + "format": "logd HOST [HOST ...] DAEMON", + "help": ( + "tail -f on the logfile of the given " + "DAEMON for the given HOST[S]" + ), + "new-window": True, + }, + { + "name": "stdlog", + "exec": ( + "[ -e %RUNDIR%/frr.log ] && tail -F %RUNDIR%/frr.log " + "|| tail -F /var/log/frr.log" + ), + "format": "stdlog HOST [HOST ...]", + "help": "tail -f on the `frr.log` for the given HOST[S]", + "new-window": True, + }, + { + "name": "stdout", + "exec": "tail -F %RUNDIR%/{0}.err", + "format": "stdout HOST [HOST ...] DAEMON", + "help": ( + "tail -f on the stdout of the given DAEMON for the given HOST[S]" + ), + "new-window": True, + }, + { + "name": "stderr", + "exec": "tail -F %RUNDIR%/{0}.out", + "format": "stderr HOST [HOST ...] DAEMON", + "help": ( + "tail -f on the stderr of the given DAEMON for the given HOST[S]" + ), + "new-window": True, + }, + # + # Non-window commands. + # + { + "name": "", + "exec": "vtysh -c '{}'", + "format": "[ROUTER ...] COMMAND", + "help": "execute vtysh COMMAND on the router[s]", + "kinds": ["frr"], + }, + { + "name": "sh", + "format": "[HOST ...] sh <SHELL-COMMAND>", + "help": "execute <SHELL-COMMAND> on hosts", + "exec": "{}", + }, + { + "name": "shi", + "format": "[HOST ...] shi <INTERACTIVE-COMMAND>", + "help": "execute <INTERACTIVE-COMMAND> on HOST[s]", + "exec": "{}", + "interactive": True, + }, + ] + } + + cli.add_cli_config(self, cdict) + + # shellopt = ( + # self.pytest_config.getoption("--shell") if self.pytest_config else None + # ) + # shellopt = shellopt if shellopt is not None else "" + # if shellopt == "all" or "." in shellopt.split(","): + # self.run_in_window("bash") + + # This is expected by newer munet CLI code + self.config_dirname = "" + self.config = {} self.logger.debug("%s: Creating", self) @@ -219,12 +315,15 @@ class Mininet(Micronet): host.cmd_raises("ip addr add {}/{} dev {}".format(ip, plen, first_intf)) + # can be used by munet cli + host.mgmt_ip = ipaddress.ip_address(ip) + if "defaultRoute" in params: host.cmd_raises( "ip route add default {}".format(params["defaultRoute"]) ) - host.config() + host.config_host() self.configured_hosts.add(name) @@ -250,4 +349,4 @@ class Mininet(Micronet): Mininet.g_mnet_inst = None def cli(self): - cli(self) + cli.cli(self) diff --git a/tests/topotests/lib/ospf.py b/tests/topotests/lib/ospf.py index 23b1f2e533..dad87440bc 100644 --- a/tests/topotests/lib/ospf.py +++ b/tests/topotests/lib/ospf.py @@ -577,15 +577,15 @@ def verify_ospf_neighbor( "ospf": { "neighbors": { "r1": { - "state": "Full", + "nbrState": "Full", "role": "DR" }, "r2": { - "state": "Full", + "nbrState": "Full", "role": "DROther" }, "r3": { - "state": "Full", + "nbrState": "Full", "role": "DROther" } } @@ -642,13 +642,13 @@ def verify_ospf_neighbor( neighbor_ip = neighbor_ip.lower() nbr_rid = data_rid try: - nh_state = show_ospf_json[nbr_rid][0]["state"].split("/")[0] - intf_state = show_ospf_json[nbr_rid][0]["state"].split("/")[1] + nh_state = show_ospf_json[nbr_rid][0]["nbrState"].split("/")[0] + intf_state = show_ospf_json[nbr_rid][0]["nbrState"].split("/")[1] except KeyError: errormsg = "[DUT: {}] OSPF peer {} missing".format(router, nbr_rid) return errormsg - nbr_state = nbr_data.setdefault("state", None) + nbr_state = nbr_data.setdefault("nbrState", None) nbr_role = nbr_data.setdefault("role", None) if nbr_state: @@ -724,8 +724,9 @@ def verify_ospf_neighbor( nh_state = None neighbor_ip = neighbor_ip.lower() nbr_rid = data_rid + try: - nh_state = show_ospf_json[nbr_rid][0]["state"].split("/")[0] + nh_state = show_ospf_json[nbr_rid][0]["nbrState"].split("/")[0] except KeyError: errormsg = "[DUT: {}] OSPF peer {} missing,from " "{} ".format( router, nbr_rid, ospf_nbr @@ -2477,7 +2478,7 @@ def verify_ospf_gr_helper(tgen, topo, dut, input_dict=None): input_dict = { "helperSupport":"Disabled", "strictLsaCheck":"Enabled", - "restartSupoort":"Planned and Unplanned Restarts", + "restartSupport":"Planned and Unplanned Restarts", "supportedGracePeriod":1800 } result = verify_ospf_gr_helper(tgen, topo, dut, input_dict) diff --git a/tests/topotests/lib/pim.py b/tests/topotests/lib/pim.py index 6878d93f37..925890b324 100644 --- a/tests/topotests/lib/pim.py +++ b/tests/topotests/lib/pim.py @@ -5120,6 +5120,75 @@ def verify_pim6_config(tgen, input_dict, expected=True): logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) return True + +@retry(retry_timeout=62) +def verify_local_mld_groups(tgen, dut, interface, group_addresses): + """ + Verify local MLD groups are received from an intended interface + by running "show ipv6 mld join json" command + Parameters + ---------- + * `tgen`: topogen object + * `dut`: device under test + * `interface`: interface, from which IGMP groups are configured + * `group_addresses`: MLD group address + Usage + ----- + dut = "r1" + interface = "r1-r0-eth0" + group_address = "ffaa::1" + result = verify_local_mld_groups(tgen, dut, interface, group_address) + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + if dut not in tgen.routers(): + return False + + rnode = tgen.routers()[dut] + logger.info("[DUT: %s]: Verifying local MLD groups received:", dut) + show_ipv6_local_mld_json = run_frr_cmd( + rnode, "show ipv6 mld join json", isjson=True + ) + + if type(group_addresses) is not list: + group_addresses = [group_addresses] + + if interface not in show_ipv6_local_mld_json["default"]: + + errormsg = ( + "[DUT %s]: Verifying local MLD group received" + " from interface %s [FAILED]!! " % (dut, interface) + ) + return errormsg + + for grp_addr in group_addresses: + found = False + if grp_addr in show_ipv6_local_mld_json["default"][interface]: + found = True + break + if not found: + errormsg = ( + "[DUT %s]: Verifying local MLD group received" + " from interface %s [FAILED]!! " + " Expected: %s " % (dut, interface, grp_addr) + ) + return errormsg + + logger.info( + "[DUT %s]: Verifying local MLD group %s received " + "from interface %s [PASSED]!! ", + dut, + grp_addr, + interface, + ) + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True + # def cleanup(self): # super(McastTesterHelper, self).cleanup() diff --git a/tests/topotests/lib/topogen.py b/tests/topotests/lib/topogen.py index 41da660b7d..7843063165 100644 --- a/tests/topotests/lib/topogen.py +++ b/tests/topotests/lib/topogen.py @@ -33,6 +33,7 @@ import os import platform import pwd import re +import shlex import subprocess import sys from collections import OrderedDict @@ -212,7 +213,10 @@ class Topogen(object): # Mininet(Micronet) to build the actual topology. assert not inspect.isclass(topodef) - self.net = Mininet(controller=None) + self.net = Mininet(rundir=self.logdir) + + # Adjust the parent namespace + topotest.fix_netns_limits(self.net) # New direct way: Either a dictionary defines the topology or a build function # is supplied, or a json filename all of which build the topology by calling @@ -504,7 +508,7 @@ class Topogen(object): def set_error(self, message, code=None): "Sets an error message and signal other tests to skip." - logger.info(message) + logger.info("setting error msg: %s", message) # If no code is defined use a sequential number if code is None: @@ -749,8 +753,8 @@ class TopoRouter(TopoGear): """ super(TopoRouter, self).__init__(tgen, name, **params) self.routertype = params.get("routertype", "frr") - if "privateDirs" not in params: - params["privateDirs"] = self.PRIVATE_DIRS + if "private_mounts" not in params: + params["private_mounts"] = self.PRIVATE_DIRS # Propagate the router log directory logfile = self._setup_tmpdir() @@ -799,7 +803,7 @@ class TopoRouter(TopoGear): grep_cmd = "grep 'ip {}' {}".format(daemonstr, source) else: grep_cmd = "grep 'router {}' {}".format(daemonstr, source) - result = self.run(grep_cmd).strip() + result = self.run(grep_cmd, warn=False).strip() if result: self.load_config(daemon) else: @@ -822,7 +826,7 @@ class TopoRouter(TopoGear): all routers. """ daemonstr = self.RD.get(daemon) - self.logger.info('loading "{}" configuration: {}'.format(daemonstr, source)) + self.logger.debug('loading "{}" configuration: {}'.format(daemonstr, source)) self.net.loadConf(daemonstr, source, param) def check_router_running(self): @@ -943,18 +947,20 @@ class TopoRouter(TopoGear): if daemon is not None: dparam += "-d {}".format(daemon) - vtysh_command = 'vtysh {} -c "{}" 2>/dev/null'.format(dparam, command) + vtysh_command = "vtysh {} -c {} 2>/dev/null".format( + dparam, shlex.quote(command) + ) - self.logger.info('vtysh command => "{}"'.format(command)) + self.logger.debug("vtysh command => {}".format(shlex.quote(command))) output = self.run(vtysh_command) dbgout = output.strip() if dbgout: if "\n" in dbgout: dbgout = dbgout.replace("\n", "\n\t") - self.logger.info("vtysh result:\n\t{}".format(dbgout)) + self.logger.debug("vtysh result:\n\t{}".format(dbgout)) else: - self.logger.info('vtysh result: "{}"'.format(dbgout)) + self.logger.debug('vtysh result: "{}"'.format(dbgout)) if isjson is False: return output @@ -994,7 +1000,7 @@ class TopoRouter(TopoGear): dbgcmds = commands if is_string(commands) else "\n".join(commands) dbgcmds = "\t" + dbgcmds.replace("\n", "\n\t") - self.logger.info("vtysh command => FILE:\n{}".format(dbgcmds)) + self.logger.debug("vtysh command => FILE:\n{}".format(dbgcmds)) res = self.run(vtysh_command) os.unlink(fname) @@ -1003,9 +1009,9 @@ class TopoRouter(TopoGear): if dbgres: if "\n" in dbgres: dbgres = dbgres.replace("\n", "\n\t") - self.logger.info("vtysh result:\n\t{}".format(dbgres)) + self.logger.debug("vtysh result:\n\t{}".format(dbgres)) else: - self.logger.info('vtysh result: "{}"'.format(dbgres)) + self.logger.debug('vtysh result: "{}"'.format(dbgres)) return res def report_memory_leaks(self, testname): @@ -1097,7 +1103,7 @@ class TopoHost(TopoGear): * `ip`: the IP address (string) for the host interface * `defaultRoute`: the default route that will be installed (e.g. 'via 10.0.0.1') - * `privateDirs`: directories that will be mounted on a different domain + * `private_mounts`: directories that will be mounted on a different domain (e.g. '/etc/important_dir'). """ super(TopoHost, self).__init__(tgen, name, **params) @@ -1117,10 +1123,10 @@ class TopoHost(TopoGear): def __str__(self): gear = super(TopoHost, self).__str__() - gear += ' TopoHost<ip="{}",defaultRoute="{}",privateDirs="{}">'.format( + gear += ' TopoHost<ip="{}",defaultRoute="{}",private_mounts="{}">'.format( self.params["ip"], self.params["defaultRoute"], - str(self.params["privateDirs"]), + str(self.params["private_mounts"]), ) return gear @@ -1143,10 +1149,10 @@ class TopoExaBGP(TopoHost): (e.g. 'via 10.0.0.1') Note: the different between a host and a ExaBGP peer is that this class - has a privateDirs already defined and contains functions to handle ExaBGP - things. + has a private_mounts already defined and contains functions to handle + ExaBGP things. """ - params["privateDirs"] = self.PRIVATE_DIRS + params["private_mounts"] = self.PRIVATE_DIRS super(TopoExaBGP, self).__init__(tgen, name, **params) def __str__(self): diff --git a/tests/topotests/lib/topotest.py b/tests/topotests/lib/topotest.py index d35b908e12..2686373ad1 100644 --- a/tests/topotests/lib/topotest.py +++ b/tests/topotests/lib/topotest.py @@ -352,7 +352,7 @@ def run_and_expect(func, what, count=20, wait=3): count, wait ) - logger.info( + logger.debug( "'{}' polling started (interval {} secs, maximum {} tries)".format( func_name, wait, count ) @@ -366,7 +366,7 @@ def run_and_expect(func, what, count=20, wait=3): continue end_time = time.time() - logger.info( + logger.debug( "'{}' succeeded after {:.2f} seconds".format( func_name, end_time - start_time ) @@ -409,7 +409,7 @@ def run_and_expect_type(func, etype, count=20, wait=3, avalue=None): count, wait ) - logger.info( + logger.debug( "'{}' polling started (interval {} secs, maximum wait {} secs)".format( func_name, wait, int(wait * count) ) @@ -432,7 +432,7 @@ def run_and_expect_type(func, etype, count=20, wait=3, avalue=None): continue end_time = time.time() - logger.info( + logger.debug( "'{}' succeeded after {:.2f} seconds".format( func_name, end_time - start_time ) @@ -1130,7 +1130,7 @@ def _sysctl_atleast(commander, variable, min_value): valstr = " ".join([str(x) for x in min_value]) else: valstr = str(min_value) - logger.info("Increasing sysctl %s from %s to %s", variable, cur_val, valstr) + logger.debug("Increasing sysctl %s from %s to %s", variable, cur_val, valstr) commander.cmd_raises('sysctl -w {}="{}"\n'.format(variable, valstr)) @@ -1161,7 +1161,7 @@ def _sysctl_assure(commander, variable, value): valstr = " ".join([str(x) for x in value]) else: valstr = str(value) - logger.info("Changing sysctl %s from %s to %s", variable, cur_val, valstr) + logger.debug("Changing sysctl %s from %s to %s", variable, cur_val, valstr) commander.cmd_raises('sysctl -w {}="{}"\n'.format(variable, valstr)) @@ -1204,7 +1204,7 @@ def rlimit_atleast(rname, min_value, raises=False): soft, hard = cval if soft < min_value: nval = (min_value, hard if min_value < hard else min_value) - logger.info("Increasing rlimit %s from %s to %s", rname, cval, nval) + logger.debug("Increasing rlimit %s from %s to %s", rname, cval, nval) resource.setrlimit(rname, nval) except subprocess.CalledProcessError as error: logger.warning( @@ -1318,7 +1318,7 @@ def setup_node_tmpdir(logdir, name): class Router(Node): "A Node with IPv4/IPv6 forwarding enabled" - def __init__(self, name, **params): + def __init__(self, name, *posargs, **params): # Backward compatibility: # Load configuration defaults like topogen. @@ -1347,7 +1347,7 @@ class Router(Node): l = topolog.get_logger(name, log_level="debug", target=logfile) params["logger"] = l - super(Router, self).__init__(name, **params) + super(Router, self).__init__(name, *posargs, **params) self.daemondir = None self.hasmpls = False @@ -1407,8 +1407,8 @@ class Router(Node): # pylint: disable=W0221 # Some params are only meaningful for the parent class. - def config(self, **params): - super(Router, self).config(**params) + def config_host(self, **params): + super(Router, self).config_host(**params) # User did not specify the daemons directory, try to autodetect it. self.daemondir = params.get("daemondir") @@ -1478,11 +1478,11 @@ class Router(Node): logger.info("%s: stopping %s", self.name, ", ".join([x[0] for x in running])) for name, pid in running: - logger.info("{}: sending SIGTERM to {}".format(self.name, name)) + logger.debug("{}: sending SIGTERM to {}".format(self.name, name)) try: os.kill(pid, signal.SIGTERM) except OSError as err: - logger.info( + logger.debug( "%s: could not kill %s (%s): %s", self.name, name, pid, str(err) ) @@ -1526,7 +1526,10 @@ class Router(Node): def removeIPs(self): for interface in self.intfNames(): try: - self.intf_ip_cmd(interface, "ip address flush " + interface) + self.intf_ip_cmd(interface, "ip -4 address flush " + interface) + self.intf_ip_cmd( + interface, "ip -6 address flush " + interface + " scope global" + ) except Exception as ex: logger.error("%s can't remove IPs %s", self, str(ex)) # pdb.set_trace() @@ -1560,7 +1563,7 @@ class Router(Node): router_relative = os.path.join(script_dir, self.name, tail) if self.path_exists(router_relative): source = router_relative - self.logger.info( + self.logger.debug( "using router relative configuration: {}".format(source) ) @@ -1617,7 +1620,7 @@ class Router(Node): # Auto-Started staticd has no config, so it will read from zebra config else: - logger.info("No daemon {} known".format(daemon)) + logger.warning("No daemon {} known".format(daemon)) # print "Daemons after:", self.daemons def runInWindow(self, cmd, title=None): @@ -1837,6 +1840,8 @@ class Router(Node): logger.info( "%s: %s %s launched in gdb window", self, self.routertype, daemon ) + # Need better check for daemons running. + time.sleep(5) else: if daemon != "snmpd": cmdopt += " -d " @@ -1859,7 +1864,7 @@ class Router(Node): else "", ) else: - logger.info("%s: %s %s started", self, self.routertype, daemon) + logger.debug("%s: %s %s started", self, self.routertype, daemon) # Start mgmtd first if "mgmtd" in daemons_list: @@ -1888,15 +1893,6 @@ class Router(Node): while "snmpd" in daemons_list: daemons_list.remove("snmpd") - if daemons is None: - # Fix Link-Local Addresses on initial startup - # Somehow (on Mininet only), Zebra removes the IPv6 Link-Local addresses on start. Fix this - _, output, _ = self.cmd_status( - "for i in `ls /sys/class/net/` ; do mac=`cat /sys/class/net/$i/address`; echo $i: $mac; [ -z \"$mac\" ] && continue; IFS=':'; set $mac; unset IFS; ip address add dev $i scope link fe80::$(printf %02x $((0x$1 ^ 2)))$2:${3}ff:fe$4:$5$6/64; done", - stderr=subprocess.STDOUT, - ) - logger.debug("Set MACs:\n%s", output) - # Now start all the other daemons for daemon in daemons_list: if self.daemons[daemon] == 0: @@ -1934,7 +1930,7 @@ class Router(Node): daemonpidfile = d.rstrip() daemonpid = self.cmd("cat %s" % daemonpidfile).rstrip() if daemonpid.isdigit() and pid_exists(int(daemonpid)): - logger.info( + logger.debug( "{}: killing {}".format( self.name, os.path.basename(daemonpidfile.rsplit(".", 1)[0]), @@ -2198,7 +2194,7 @@ class Router(Node): log = self.getStdErr(daemon) if "memstats" in log: # Found memory leak - logger.info( + logger.warning( "\nRouter {} {} StdErr Log:\n{}".format(self.name, daemon, log) ) if not leakfound: diff --git a/tests/topotests/mgmt_tests/test_yang_mgmt.py b/tests/topotests/mgmt_tests/test_yang_mgmt.py index 06c18d7dfa..921b4e622c 100644 --- a/tests/topotests/mgmt_tests/test_yang_mgmt.py +++ b/tests/topotests/mgmt_tests/test_yang_mgmt.py @@ -217,7 +217,9 @@ def test_mgmt_commit_check(request): ] } } - result = verify_rib(tgen, "ipv4", dut, input_dict_4, protocol=protocol) + result = verify_rib( + tgen, "ipv4", dut, input_dict_4, protocol=protocol, expected=False + ) assert ( result is not True ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) @@ -319,7 +321,9 @@ def test_mgmt_commit_abort(request): ] } } - result = verify_rib(tgen, "ipv4", dut, input_dict_4, protocol=protocol) + result = verify_rib( + tgen, "ipv4", dut, input_dict_4, protocol=protocol, expected=False + ) assert ( result is not True ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) @@ -372,7 +376,7 @@ def test_mgmt_delete_config(request): assert ( result is True ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) - + step("Mgmt delete config") raw_config = { "r1": { @@ -387,7 +391,9 @@ def test_mgmt_delete_config(request): assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) step("Verify that the route is deleted from RIB") - result = verify_rib(tgen, "ipv4", dut, input_dict_4, protocol=protocol) + result = verify_rib( + tgen, "ipv4", dut, input_dict_4, protocol=protocol, expected=False + ) assert ( result is not True ), "Testcase {} : Failed" "Error: Routes is still present in RIB".format(tc_name) @@ -461,7 +467,9 @@ def test_mgmt_chaos_stop_start_frr(request): dut = "r1" protocol = "static" input_dict_4 = {"r1": {"static_routes": [{"network": "192.1.11.200/32"}]}} - result = verify_rib(tgen, "ipv4", dut, input_dict_4, protocol=protocol) + result = verify_rib( + tgen, "ipv4", dut, input_dict_4, protocol=protocol, expected=False + ) assert ( result is not True ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) diff --git a/tests/topotests/multicast_mld_join_topo1/multicast_mld_local_join.json b/tests/topotests/multicast_mld_join_topo1/multicast_mld_local_join.json new file mode 100644 index 0000000000..d7053bf6dd --- /dev/null +++ b/tests/topotests/multicast_mld_join_topo1/multicast_mld_local_join.json @@ -0,0 +1,249 @@ +{ + "address_types": ["ipv6"], + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv6": "2001:db8:f::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "r4": {"ipv6": "auto", "pim6": "enable"}, + "r2": {"ipv6": "auto", "pim6": "enable"}, + "r3": {"ipv6": "auto", "pim6": "enable"}, + "i1": {"ipv6": "auto", "pim6": "enable"}, + "i2": {"ipv6": "auto", "pim6": "enable"} + }, + + "bgp": { + "local_as": "100", + "router_id": "192.168.1.1", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r1": {} + } + }, + "r2": { + "dest_link": { + "r1": {} + } + }, + "r3": { + "dest_link": { + "r1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"} + ], + "neighbor": { + "r4": { + "dest_link": { + "r1": {} + } + }, + "r2": { + "dest_link": { + "r1": {} + } + }, + "r3": { + "dest_link": { + "r1": {} + } + } + } + } + } + } + } + }, + "r2": { + "links": { + "lo": {"ipv6": "auto", "type": "loopback", "pim6": "enable"}, + "r1": {"ipv6": "auto", "pim6": "enable"}, + "r4": {"ipv6": "auto", "pim6": "enable"} + }, + "bgp": { + "local_as": "100", + "router_id": "192.168.1.2", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r4": { + "dest_link": { + "r2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"} + ], + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r4": { + "dest_link": { + "r2": {} + } + } + } + } + } + } + } + }, + "r3": { + "links": { + "r1": {"ipv6": "auto", "pim6": "enable"}, + "r4": {"ipv6": "auto", "pim6": "enable"} + }, + "bgp": { + "local_as": "100", + "router_id": "192.168.1.3", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {} + } + }, + "r4": { + "dest_link": { + "r3": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"} + ], + "neighbor": { + "r1": { + "dest_link": { + "r3": {} + } + }, + "r4": { + "dest_link": { + "r3": {} + } + } + } + } + } + } + } + }, + "r4": { + "links": { + "r2": {"ipv6": "auto", "pim6": "enable"}, + "r3": {"ipv6": "auto", "pim6": "enable"}, + "i4": {"ipv6": "auto", "pim6": "enable"}, + "r1": {"ipv6": "auto", "pim6": "enable"} + }, + "bgp": { + "local_as": "100", + "router_id": "192.168.1.4", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r4": {} + } + }, + "r2": { + "dest_link": { + "r4": {} + } + }, + "r3": { + "dest_link": { + "r4": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"} + ], + "neighbor": { + "r1": { + "dest_link": { + "r4": {} + } + }, + "r2": { + "dest_link": { + "r4": {} + } + }, + "r3": { + "dest_link": { + "r4": {} + } + } + } + } + } + } + } + }, + "i1": { + "links": { + "r1": {"ipv6": "auto"} + } + }, + "i2": { + "links": { + "r1": {"ipv6": "auto"} + } + }, + "i4": { + "links": { + "r4": {"ipv6": "auto"} + } + } + } +}
\ No newline at end of file diff --git a/tests/topotests/multicast_mld_join_topo1/test_multicast_mld_local_join.py b/tests/topotests/multicast_mld_join_topo1/test_multicast_mld_local_join.py new file mode 100644 index 0000000000..2c4fb4e998 --- /dev/null +++ b/tests/topotests/multicast_mld_join_topo1/test_multicast_mld_local_join.py @@ -0,0 +1,915 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2023 by VMware, Inc. ("VMware") +# + +""" +Following tests are covered to test_multicast_pim_mld_local_tier_1: + +Test steps +- Create topology (setup module) +- Bring up topology + +Following tests are covered: + +1. Verify static MLD group populated when static "ip mld join <grp>" in configured +2. Verify mroute and upstream populated with correct OIL/IIF with static imld join +3. Verify local MLD join not allowed for non multicast group +4. Verify static MLD group removed from DUT while removing "ip mld join" CLI +5. Verify static MLD groups after removing and adding MLD config +""" + +import os +import sys +import time +import pytest + +# 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/")) + +# Required to instantiate the topology builder class. + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from re import search as re_search +from re import findall as findall + +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + step, + kill_router_daemons, + start_router_daemons, + reset_config_on_routers, + do_countdown, + apply_raw_config, + socat_send_pim6_traffic, +) + +from lib.pim import ( + create_pim_config, + verify_mroutes, + verify_upstream_iif, + verify_mld_groups, + clear_pim6_mroute, + McastTesterHelper, + verify_pim_neighbors, + create_mld_config, + verify_mld_groups, + verify_local_mld_groups, + verify_pim_rp_info, +) +from lib.topolog import logger +from lib.topojson import build_config_from_json + +r1_r2_links = [] +r1_r3_links = [] +r2_r1_links = [] +r2_r4_links = [] +r3_r1_links = [] +r3_r4_links = [] +r4_r2_links = [] +r4_r3_links = [] + +pytestmark = [pytest.mark.pim6d, pytest.mark.staticd] + +TOPOLOGY = """ + +-------------------+ + | | + i1--- R1-------R2----------R4---i2 + | | + +-------R3----------+ + + + Description: + i1, i2, i3. i4, i5, i6, i7, i8 - FRR running iperf to send MLD + join and traffic + R1 - DUT (LHR) + R2 - RP + R3 - Transit + R4 - (FHR) + +""" +# Global variables + +GROUP_RANGE = "ffaa::/16" +RP_RANGE = "ff00::/8" +GROUP_RANGE_1 = [ + "ffaa::1/128", + "ffaa::2/128", + "ffaa::3/128", + "ffaa::4/128", + "ffaa::5/128", +] +MLD_JOIN_RANGE_1 = ["ffaa::1", "ffaa::2", "ffaa::3", "ffaa::4", "ffaa::5"] +MLD_JOIN_RANGE_2 = [ + "ff02::1:ff00:0", + "ff02::d", + "fe80::250:56ff:feb7:d8d5", + "2001::4", + "2002::5", +] + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + logger.info("Master Topology: \n {}".format(TOPOLOGY)) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/multicast_mld_local_join.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + # Verify PIM neighbors + result = verify_pim_neighbors(tgen, topo) + assert result is True, " Verify PIM neighbor: Failed Error: {}".format(result) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Testcases +# +##################################################### + + +def test_mld_local_joins_p0(request): + """ + Verify static MLD group populated when static + "ipv6 mld join <grp>" in configured + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + + step("configure BGP on R1, R2, R3, R4 and enable redistribute static/connected") + step("Enable the MLD on R11 interfac of R1 and configure local mld groups") + intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"] + intf_r1_i2 = topo["routers"]["r1"]["links"]["i2"]["interface"] + input_dict = { + "r1": { + "mld": { + "interfaces": { + intf_r1_i1: {"mld": {"version": "1", "join": MLD_JOIN_RANGE_1}}, + intf_r1_i2: {"mld": {"version": "1", "join": MLD_JOIN_RANGE_1}}, + } + } + } + } + + result = create_mld_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure static RP for (ffaa::1-5) as R2") + + input_dict = { + "r2": { + "pim6": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv6"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE, + } + ] + } + } + } + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("verify static mld join using show ipv6 mld join") + dut = "r1" + interfaces = [intf_r1_i1, intf_r1_i2] + for interface in interfaces: + result = verify_local_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("verify mld groups using show ipv6 mld groups") + interfaces = [intf_r1_i1, intf_r1_i2] + for interface in interfaces: + result = verify_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_mroute_with_mld_local_joins_p0(request): + """ + Verify mroute and upstream populated with correct OIL/IIF with + static mld join + """ + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + + step("Enable the PIM on all the interfaces of R1, R2, R3, R4") + step("configure BGP on R1, R2, R3, R4 and enable redistribute static/connected") + step("Enable the MLD on R11 interfac of R1 and configure local mld groups") + + intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"] + intf_r1_i2 = topo["routers"]["r1"]["links"]["i2"]["interface"] + input_dict = { + "r1": { + "mld": { + "interfaces": { + intf_r1_i1: {"mld": {"version": "1", "join": MLD_JOIN_RANGE_1}}, + intf_r1_i2: {"mld": {"version": "1", "join": MLD_JOIN_RANGE_1}}, + } + } + } + } + + result = create_mld_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure static RP for (ffaa::1-5) as R2") + + input_dict = { + "r2": { + "pim6": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv6"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE, + } + ] + } + } + } + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("verify static mld join using show ipv6 mld join") + dut = "r1" + interfaces = [intf_r1_i1, intf_r1_i2] + for interface in interfaces: + result = verify_local_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("verify mld groups using show ipv6 mld groups") + interfaces = [intf_r1_i1, intf_r1_i2] + for interface in interfaces: + result = verify_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("verify RP-info populated in DUT") + dut = "r1" + rp_address = topo["routers"]["r2"]["links"]["lo"]["ipv6"].split("/")[0] + SOURCE = "Static" + oif = topo["routers"]["r1"]["links"]["r2"]["interface"] + result = verify_pim_rp_info(tgen, topo, dut, GROUP_RANGE_1, oif, rp_address, SOURCE) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Send traffic from R4 to all the groups ( ffaa::1 to ffaa::5)") + intf_ip = topo["routers"]["i4"]["links"]["r4"]["ipv6"].split("/")[0] + intf = topo["routers"]["i4"]["links"]["r4"]["interface"] + result = socat_send_pim6_traffic(tgen, "i4", "UDP6-SEND", MLD_JOIN_RANGE_1, intf) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step( + "'show ipv6 mroute' showing correct RPF and OIF interface for (*,G)" + " and (S,G) entries on all the nodes" + ) + source_i6 = topo["routers"]["i4"]["links"]["r4"]["ipv6"].split("/")[0] + + intf_r1_r2 = topo["routers"]["r1"]["links"]["r2"]["interface"] + + input_dict_starg = [ + { + "dut": "r1", + "src_address": "*", + "iif": intf_r1_r2, + "oil": topo["routers"]["r1"]["links"]["i1"]["interface"], + }, + { + "dut": "r1", + "src_address": "*", + "iif": intf_r1_r2, + "oil": topo["routers"]["r1"]["links"]["i2"]["interface"], + }, + ] + + input_dict_sg = [ + { + "dut": "r1", + "src_address": source_i6, + "iif": topo["routers"]["r1"]["links"]["r4"]["interface"], + "oil": topo["routers"]["r1"]["links"]["i1"]["interface"], + }, + { + "dut": "r1", + "src_address": source_i6, + "iif": topo["routers"]["r1"]["links"]["r4"]["interface"], + "oil": topo["routers"]["r1"]["links"]["i2"]["interface"], + }, + ] + + step("Verify mroutes and iff upstream for local mld groups") + for input_dict in [input_dict_starg, input_dict_sg]: + for data in input_dict: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + MLD_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], MLD_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + + step("Verify mroutes not created with local interface ip ") + input_dict_local_sg = [ + { + "dut": "r1", + "src_address": intf_r1_i1, + "iif": topo["routers"]["r1"]["links"]["r4"]["interface"], + "oil": topo["routers"]["r1"]["links"]["i1"]["interface"], + }, + { + "dut": "r1", + "src_address": intf_r1_i2, + "iif": topo["routers"]["r1"]["links"]["r4"]["interface"], + "oil": topo["routers"]["r1"]["links"]["i2"]["interface"], + }, + ] + + for data in input_dict_local_sg: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + MLD_JOIN_RANGE_1, + data["iif"], + data["oil"], + expected=False, + ) + assert result is not True, ( + "Testcase {} : Failed Error: {}" + "sg created with local interface ip".format(tc_name, result) + ) + + result = verify_upstream_iif( + tgen, + data["dut"], + data["iif"], + data["src_address"], + MLD_JOIN_RANGE_1, + expected=False, + ) + assert result is not True, ( + "Testcase {} : Failed Error: {}" + "upstream created with local interface ip".format(tc_name, result) + ) + + write_test_footer(tc_name) + + +def test_remove_add_mld_local_joins_p1(request): + """ + Verify static MLD group removed from DUT while + removing "ip mld join" CLI + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + + step("Enable the PIM on all the interfaces of R1, R2, R3, R4") + step("configure BGP on R1, R2, R3, R4 and enable redistribute static/connected") + step("Enable the MLD on R11 interfac of R1 and configure local mld groups") + + intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"] + + input_dict = { + "r1": { + "mld": { + "interfaces": { + intf_r1_i1: {"mld": {"version": "1", "join": MLD_JOIN_RANGE_1}} + } + } + } + } + + result = create_mld_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure static RP for (ffaa::1-5) as R2") + + input_dict = { + "r2": { + "pim6": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv6"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE, + } + ] + } + } + } + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("verify static mld join using show ipv6 mld join") + dut = "r1" + interface = intf_r1_i1 + result = verify_local_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("verify mld groups using show ipv6 mld groups") + + interface = intf_r1_i1 + result = verify_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("verify RP-info populated in DUT") + dut = "r1" + rp_address = topo["routers"]["r2"]["links"]["lo"]["ipv6"].split("/")[0] + SOURCE = "Static" + oif = topo["routers"]["r1"]["links"]["r2"]["interface"] + result = verify_pim_rp_info(tgen, topo, dut, GROUP_RANGE_1, oif, rp_address, SOURCE) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Send traffic from R4 to all the groups ( ffaa::1 to ffaa::5)") + intf_ip = topo["routers"]["i4"]["links"]["r4"]["ipv6"].split("/")[0] + intf = topo["routers"]["i4"]["links"]["r4"]["interface"] + result = socat_send_pim6_traffic(tgen, "i4", "UDP6-SEND", MLD_JOIN_RANGE_1, intf) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step( + "'show ipv6 mroute' showing correct RPF and OIF interface for (*,G)" + " and (S,G) entries on all the nodes" + ) + source_i6 = topo["routers"]["i4"]["links"]["r4"]["ipv6"].split("/")[0] + + intf_r1_r2 = topo["routers"]["r1"]["links"]["r2"]["interface"] + input_dict_starg = [ + { + "dut": "r1", + "src_address": "*", + "iif": intf_r1_r2, + "oil": topo["routers"]["r1"]["links"]["i1"]["interface"], + } + ] + + input_dict_sg = [ + { + "dut": "r1", + "src_address": source_i6, + "iif": topo["routers"]["r1"]["links"]["r4"]["interface"], + "oil": topo["routers"]["r1"]["links"]["i1"]["interface"], + } + ] + + step("Verify mroutes and iff upstream for local mld groups") + for input_dict in [input_dict_starg, input_dict_sg]: + for data in input_dict: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + MLD_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], MLD_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + + step("Remove MLD join from DUT") + input_dict = { + "r1": { + "mld": { + "interfaces": { + intf_r1_i1: { + "mld": { + "join": MLD_JOIN_RANGE_1, + "delete_attr": True, + } + } + } + } + } + } + result = create_mld_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("verify static mld join removed using show ipv6 mld join") + dut = "r1" + interface = intf_r1_i1 + result = verify_local_mld_groups( + tgen, dut, interface, MLD_JOIN_RANGE_1, expected=False + ) + assert ( + result is not True + ), "Testcase {} :Failed \n Error: {}" "MLD join still present".format( + tc_name, result + ) + + step("verify mld groups removed using show ipv6 mld groups") + interface = intf_r1_i1 + result = verify_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1, expected=False) + assert ( + result is not True + ), "Testcase {} :Failed \n Error: {}" "MLD groups still present".format( + tc_name, result + ) + + step("Verify mroutes and iff upstream for local mld groups") + for input_dict in [input_dict_starg, input_dict_sg]: + for data in input_dict: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + MLD_JOIN_RANGE_1, + data["iif"], + data["oil"], + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed Error: {}" "mroutes still present".format( + tc_name, result + ) + + result = verify_upstream_iif( + tgen, + data["dut"], + data["iif"], + data["src_address"], + MLD_JOIN_RANGE_1, + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed Error: {}" "mroutes still present".format( + tc_name, result + ) + + step("Add MLD join on DUT again") + input_dict = { + "r1": { + "mld": { + "interfaces": { + intf_r1_i1: { + "mld": { + "join": MLD_JOIN_RANGE_1, + } + } + } + } + } + } + result = create_mld_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("verify static mld join using show ipv6 mld join") + dut = "r1" + interface = intf_r1_i1 + result = verify_local_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("verify mld groups using show ipv6 mld groups") + + interface = intf_r1_i1 + result = verify_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify mroutes and iff upstream for local mld groups") + for input_dict in [input_dict_starg, input_dict_sg]: + for data in input_dict: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + MLD_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], MLD_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_remove_add_mld_config_with_local_joins_p1(request): + """ + Verify static MLD groups after removing + and adding MLD config + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + + step("Enable the PIM on all the interfaces of R1, R2, R3, R4") + step("configure BGP on R1, R2, R3, R4 and enable redistribute static/connected") + step("Enable the MLD on R11 interfac of R1 and configure local mld groups") + + intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"] + input_dict = { + "r1": { + "mld": { + "interfaces": { + intf_r1_i1: {"mld": {"version": "1", "join": MLD_JOIN_RANGE_1}} + } + } + } + } + + result = create_mld_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure static RP for (ffaa::1-5) as R2") + + input_dict = { + "r2": { + "pim6": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv6"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE, + } + ] + } + } + } + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("verify static mld join using show ipv6 mld join") + dut = "r1" + interface = intf_r1_i1 + result = verify_local_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("verify mld groups using show ipv6 mld groups") + interface = intf_r1_i1 + result = verify_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Send traffic from R4 to all the groups ( ffaa::1 to ffaa::5)") + intf_ip = topo["routers"]["i4"]["links"]["r4"]["ipv6"].split("/")[0] + intf = topo["routers"]["i4"]["links"]["r4"]["interface"] + result = socat_send_pim6_traffic(tgen, "i4", "UDP6-SEND", MLD_JOIN_RANGE_1, intf) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step( + "'show ipv6 mroute' showing correct RPF and OIF interface for (*,G)" + " and (S,G) entries on all the nodes" + ) + source_i6 = topo["routers"]["i4"]["links"]["r4"]["ipv6"].split("/")[0] + + intf_r1_r2 = topo["routers"]["r1"]["links"]["r2"]["interface"] + input_dict_starg = [ + { + "dut": "r1", + "src_address": "*", + "iif": intf_r1_r2, + "oil": topo["routers"]["r1"]["links"]["i1"]["interface"], + } + ] + + input_dict_sg = [ + { + "dut": "r1", + "src_address": source_i6, + "iif": topo["routers"]["r1"]["links"]["r4"]["interface"], + "oil": topo["routers"]["r1"]["links"]["i1"]["interface"], + } + ] + + step("Verify mroutes and iff upstream for local mld groups") + for input_dict in [input_dict_starg, input_dict_sg]: + for data in input_dict: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + MLD_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], MLD_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + + step("Remove mld and mld version 2 from DUT interface") + input_dict = { + "r1": { + "mld": { + "interfaces": {intf_r1_i1: {"mld": {"version": "1", "delete": True}}} + } + } + } + + result = create_mld_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("verify static mld join using show ipv6 mld join") + dut = "r1" + interface = intf_r1_i1 + result = verify_local_mld_groups( + tgen, dut, interface, MLD_JOIN_RANGE_1, expected=False + ) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("verify mld groups using show ipv6 mld groups") + interface = intf_r1_i1 + result = verify_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1, expected=False) + assert ( + result is not True + ), "Testcase {} :Failed \n Error: {}" "MLD grsp still present".format( + tc_name, result + ) + + step("Verify mroutes and iff upstream for local mld groups") + for input_dict in [input_dict_starg, input_dict_sg]: + for data in input_dict: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + MLD_JOIN_RANGE_1, + data["iif"], + data["oil"], + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed Error: {}" "mroutes still present".format( + tc_name, result + ) + + step("Add mld and mld version 2 from DUT interface") + input_dict = { + "r1": { + "mld": { + "interfaces": { + intf_r1_i1: {"mld": {"version": "1", "join": MLD_JOIN_RANGE_1}} + } + } + } + } + + result = create_mld_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("verify static mld join using show ipv6 mld join") + dut = "r1" + interface = intf_r1_i1 + result = verify_local_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("verify mld groups using show ipv6 mld groups") + interface = intf_r1_i1 + result = verify_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify mroutes and iff upstream for local mld groups") + for input_dict in [input_dict_starg, input_dict_sg]: + for data in input_dict: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + MLD_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], MLD_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed 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/multicast_pim6_sm_topo1/test_multicast_pim6_sm1.py b/tests/topotests/multicast_pim6_sm_topo1/test_multicast_pim6_sm1.py index a76ff2dd9c..87b04b41be 100644 --- a/tests/topotests/multicast_pim6_sm_topo1/test_multicast_pim6_sm1.py +++ b/tests/topotests/multicast_pim6_sm_topo1/test_multicast_pim6_sm1.py @@ -62,6 +62,7 @@ from lib.common_config import ( socat_send_mld_join, socat_send_pim6_traffic, get_frr_ipv6_linklocal, + kill_socat, ) from lib.bgp import create_router_bgp from lib.pim import ( @@ -158,10 +159,6 @@ def setup_module(mod): # Creating configuration from JSON build_config_from_json(tgen, tgen.json_topo) - # XXX Replace this using "with McastTesterHelper()... " in each test if possible. - global app_helper - app_helper = McastTesterHelper(tgen) - logger.info("Running setup_module() done") @@ -172,7 +169,8 @@ def teardown_module(): tgen = get_topogen() - app_helper.cleanup() + # Clean up socat + kill_socat(tgen) # Stop toplogy and Remove tmp files tgen.stop_topology() diff --git a/tests/topotests/multicast_pim6_sm_topo1/test_multicast_pim6_sm2.py b/tests/topotests/multicast_pim6_sm_topo1/test_multicast_pim6_sm2.py index ceef68fece..788a839918 100644 --- a/tests/topotests/multicast_pim6_sm_topo1/test_multicast_pim6_sm2.py +++ b/tests/topotests/multicast_pim6_sm_topo1/test_multicast_pim6_sm2.py @@ -53,6 +53,7 @@ from lib.common_config import ( socat_send_mld_join, socat_send_pim6_traffic, get_frr_ipv6_linklocal, + kill_socat, ) from lib.bgp import create_router_bgp from lib.pim import ( @@ -149,10 +150,6 @@ def setup_module(mod): # Creating configuration from JSON build_config_from_json(tgen, tgen.json_topo) - # XXX Replace this using "with McastTesterHelper()... " in each test if possible. - global app_helper - app_helper = McastTesterHelper(tgen) - logger.info("Running setup_module() done") @@ -163,7 +160,8 @@ def teardown_module(): tgen = get_topogen() - app_helper.cleanup() + # Clean up socat + kill_socat(tgen) # Stop toplogy and Remove tmp files tgen.stop_topology() diff --git a/tests/topotests/multicast_pim6_static_rp_topo1/test_multicast_pim6_static_rp1.py b/tests/topotests/multicast_pim6_static_rp_topo1/test_multicast_pim6_static_rp1.py index 95b4004e14..977cd477c8 100755 --- a/tests/topotests/multicast_pim6_static_rp_topo1/test_multicast_pim6_static_rp1.py +++ b/tests/topotests/multicast_pim6_static_rp_topo1/test_multicast_pim6_static_rp1.py @@ -172,6 +172,9 @@ def teardown_module(): logger.info("Running teardown_module to delete topology") tgen = get_topogen() + # Clean up socat + kill_socat(tgen) + # Stop toplogy and Remove tmp files tgen.stop_topology() diff --git a/tests/topotests/multicast_pim6_static_rp_topo1/test_multicast_pim6_static_rp2.py b/tests/topotests/multicast_pim6_static_rp_topo1/test_multicast_pim6_static_rp2.py index 2fedb6e517..a61164baa2 100755 --- a/tests/topotests/multicast_pim6_static_rp_topo1/test_multicast_pim6_static_rp2.py +++ b/tests/topotests/multicast_pim6_static_rp_topo1/test_multicast_pim6_static_rp2.py @@ -176,6 +176,9 @@ def teardown_module(): logger.info("Running teardown_module to delete topology") tgen = get_topogen() + # Clean up socat + kill_socat(tgen) + # Stop toplogy and Remove tmp files tgen.stop_topology() diff --git a/tests/topotests/multicast_pim_sm_topo3/igmp_group_all_detail.json b/tests/topotests/multicast_pim_sm_topo3/igmp_group_all_detail.json new file mode 100644 index 0000000000..715aa1de72 --- /dev/null +++ b/tests/topotests/multicast_pim_sm_topo3/igmp_group_all_detail.json @@ -0,0 +1 @@ +{"totalGroups":5,"watermarkLimit":0,"l1-i1-eth1":{"name":"l1-i1-eth1","state":"up","address":"10.0.8.2","index":"*","flagMulticast":true,"flagBroadcast":true,"lanDelayEnabled":true,"groups":[{"group":"225.1.1.2","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.1","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.3","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.5","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.4","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]}]}} diff --git a/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_group_all_brief.json b/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_group_all_brief.json new file mode 100644 index 0000000000..3bbcce1370 --- /dev/null +++ b/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_group_all_brief.json @@ -0,0 +1,51 @@ +{ + "totalGroups":5, + "watermarkLimit":0, + "l1-i1-eth1":{ + "name":"l1-i1-eth1", + "state":"up", + "address":"10.0.8.2", + "index":"*", + "flagMulticast":true, + "flagBroadcast":true, + "lanDelayEnabled":true, + "groups":[ + { + "group":"225.1.1.1", + "timer":"*", + "sourcesCount":1, + "version":2, + "uptime":"*" + }, + { + "group":"225.1.1.2", + "timer":"*", + "sourcesCount":1, + "version":2, + "uptime":"*" + }, + { + "group":"225.1.1.3", + "timer":"*", + "sourcesCount":1, + "version":2, + "uptime":"*" + }, + { + "group":"225.1.1.4", + "timer":"*", + "sourcesCount":1, + "version":2, + "uptime":"*" + }, + { + "group":"225.1.1.5", + "timer":"*", + "sourcesCount":1, + "version":2, + "uptime":"*" + } + ] + } +} + diff --git a/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_group_all_detail.json b/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_group_all_detail.json new file mode 100644 index 0000000000..876befa1b8 --- /dev/null +++ b/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_group_all_detail.json @@ -0,0 +1 @@ +{"totalGroups":5,"watermarkLimit":0,"l1-i1-eth1":{"name":"l1-i1-eth1","state":"up","address":"10.0.8.2","index":"*","flagMulticast":true,"flagBroadcast":true,"lanDelayEnabled":true,"groups":[{"group":"225.1.1.1","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.2","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.3","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.4","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.5","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]}]}} diff --git a/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_single_group_brief.json b/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_single_group_brief.json new file mode 100644 index 0000000000..a3fb496d25 --- /dev/null +++ b/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_single_group_brief.json @@ -0,0 +1,22 @@ +{ + "totalGroups":5, + "watermarkLimit":0, + "l1-i1-eth1":{ + "name":"l1-i1-eth1", + "state":"up", + "address":"10.0.8.2", + "index":"*", + "flagMulticast":true, + "flagBroadcast":true, + "lanDelayEnabled":true, + "groups":[ + { + "group":"225.1.1.5", + "timer":"*", + "sourcesCount":1, + "version":2, + "uptime":"*" + } + ] + } +} diff --git a/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_single_group_detail.json b/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_single_group_detail.json new file mode 100644 index 0000000000..11ac5a01e7 --- /dev/null +++ b/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_single_group_detail.json @@ -0,0 +1 @@ +{"totalGroups":5,"watermarkLimit":0,"l1-i1-eth1":{"name":"l1-i1-eth1","state":"up","address":"10.0.8.2","index":"*","flagMulticast":true,"flagBroadcast":true,"lanDelayEnabled":true,"groups":[{"group":"225.1.1.5","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]}]}} diff --git a/tests/topotests/multicast_pim_sm_topo3/igmp_source_single_if_group_all.json b/tests/topotests/multicast_pim_sm_topo3/igmp_source_single_if_group_all.json new file mode 100644 index 0000000000..10ae1afc90 --- /dev/null +++ b/tests/topotests/multicast_pim_sm_topo3/igmp_source_single_if_group_all.json @@ -0,0 +1,61 @@ +{ + "l1-i1-eth1":{ + "name":"l1-i1-eth1", + "225.1.1.1":{ + "group":"225.1.1.1", + "sources":[ + { + "source":"*", + "timer":"*", + "forwarded":true, + "uptime":"*" + } + ] + }, + "225.1.1.2":{ + "group":"225.1.1.2", + "sources":[ + { + "source":"*", + "timer":"*", + "forwarded":true, + "uptime":"*" + } + ] + }, + "225.1.1.3":{ + "group":"225.1.1.3", + "sources":[ + { + "source":"*", + "timer":"*", + "forwarded":true, + "uptime":"*" + } + ] + }, + "225.1.1.4":{ + "group":"225.1.1.4", + "sources":[ + { + "source":"*", + "timer":"*", + "forwarded":true, + "uptime":"*" + } + ] + }, + "225.1.1.5":{ + "group":"225.1.1.5", + "sources":[ + { + "source":"*", + "timer":"*", + "forwarded":true, + "uptime":"*" + } + ] + } + } +} + diff --git a/tests/topotests/multicast_pim_sm_topo3/igmp_source_single_if_single_group.json b/tests/topotests/multicast_pim_sm_topo3/igmp_source_single_if_single_group.json new file mode 100644 index 0000000000..7a19975bee --- /dev/null +++ b/tests/topotests/multicast_pim_sm_topo3/igmp_source_single_if_single_group.json @@ -0,0 +1,16 @@ +{ + "l1-i1-eth1":{ + "name":"l1-i1-eth1", + "225.1.1.4":{ + "group":"225.1.1.4", + "sources":[ + { + "source":"*", + "timer":"*", + "forwarded":true, + "uptime":"*" + } + ] + } + } +} diff --git a/tests/topotests/multicast_pim_sm_topo3/test_multicast_pim_sm_topo3.py b/tests/topotests/multicast_pim_sm_topo3/test_multicast_pim_sm_topo3.py index 2ffd3a3ac0..2c1241c0cc 100755 --- a/tests/topotests/multicast_pim_sm_topo3/test_multicast_pim_sm_topo3.py +++ b/tests/topotests/multicast_pim_sm_topo3/test_multicast_pim_sm_topo3.py @@ -42,6 +42,8 @@ import time import datetime import pytest from time import sleep +import json +import functools pytestmark = pytest.mark.pimd @@ -54,8 +56,8 @@ sys.path.append(os.path.join(CWD, "../lib/")) # pylint: disable=C0413 # Import topogen and topotest helpers -from lib.topogen import Topogen, get_topogen - +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen from lib.common_config import ( start_topology, write_test_header, @@ -1510,6 +1512,108 @@ def test_verify_remove_add_igmp_config_to_receiver_interface_p0(request): ) assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + # IGMP JSON verification + step("Verify IGMP group and source JSON for single interface and group") + router = tgen.gears["l1"] + + reffile = os.path.join(CWD, "igmp_group_all_detail.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, + router, + "show ip igmp vrf default groups detail json", + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=60, wait=2) + assertmsg = "IGMP group detailed output on l1 for all interfaces and all groups is not as expected. Expected: {}".format( + expected + ) + assert res is None, assertmsg + + reffile = os.path.join(CWD, "igmp_single_if_group_all_brief.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, + router, + "show ip igmp vrf default groups l1-i1-eth1 json", + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=60, wait=2) + assertmsg = "IGMP group output on l1 for all groups in interface l1-i1-eth1 is not as expected. Expected: {}".format( + expected + ) + assert res is None, assertmsg + + reffile = os.path.join(CWD, "igmp_single_if_group_all_detail.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, + router, + "show ip igmp vrf default groups l1-i1-eth1 detail json", + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=60, wait=2) + assertmsg = "IGMP group detailed output on l1 for all groups in interface l1-i1-eth1 is not as expected. Expected: {}".format( + expected + ) + assert res is None, assertmsg + + reffile = os.path.join(CWD, "igmp_single_if_single_group_brief.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, + router, + "show ip igmp vrf default groups l1-i1-eth1 225.1.1.5 json", + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=60, wait=2) + assertmsg = "IGMP group output on l1 for interface l1-i1-eth1 and group 225.1.1.5 is not as expected. Expected: {}".format( + expected + ) + assert res is None, assertmsg + + reffile = os.path.join(CWD, "igmp_single_if_single_group_detail.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, + router, + "show ip igmp vrf default groups l1-i1-eth1 225.1.1.5 detail json", + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=60, wait=2) + assertmsg = "IGMP group detailed output on l1 for interface l1-i1-eth1 and group 225.1.1.5 is not as expected. Expected: {}".format( + expected + ) + assert res is None, assertmsg + + reffile = os.path.join(CWD, "igmp_source_single_if_group_all.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, + router, + "show ip igmp sources l1-i1-eth1 json", + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=60, wait=2) + assertmsg = "IGMP source output on l1 for interface l1-i1-eth1 is not as expected. Expected: {}".format( + expected + ) + assert res is None, assertmsg + + reffile = os.path.join(CWD, "igmp_source_single_if_single_group.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, + router, + "show ip igmp sources l1-i1-eth1 225.1.1.4 json", + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=60, wait=2) + assertmsg = "IGMP source output on l1 for interface l1-i1-eth1 and group 225.1.1.4 is not as expected. Expected: {}".format( + expected + ) + assert res is None, assertmsg + step( "Remove igmp 'no ip igmp' and 'no ip igmp version 2' from" " receiver interface of FRR1" diff --git a/tests/topotests/munet/__init__.py b/tests/topotests/munet/__init__.py new file mode 100644 index 0000000000..e1f18e51e6 --- /dev/null +++ b/tests/topotests/munet/__init__.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# September 30 2021, Christian Hopps <chopps@labn.net> +# +# Copyright 2021, LabN Consulting, L.L.C. +# +"""A module to import various objects to root namespace.""" +from .base import BaseMunet +from .base import Bridge +from .base import Commander +from .base import LinuxNamespace +from .base import SharedNamespace +from .base import cmd_error +from .base import comm_error +from .base import get_exec_path +from .base import proc_error +from .native import L3Bridge +from .native import L3NamespaceNode +from .native import Munet +from .native import to_thread + + +__all__ = [ + "BaseMunet", + "Bridge", + "Commander", + "L3Bridge", + "L3NamespaceNode", + "LinuxNamespace", + "Munet", + "SharedNamespace", + "cmd_error", + "comm_error", + "get_exec_path", + "proc_error", + "to_thread", +] diff --git a/tests/topotests/munet/__main__.py b/tests/topotests/munet/__main__.py new file mode 100644 index 0000000000..4419ab94a2 --- /dev/null +++ b/tests/topotests/munet/__main__.py @@ -0,0 +1,236 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# September 2 2021, Christian Hopps <chopps@labn.net> +# +# Copyright 2021, LabN Consulting, L.L.C. +# +"""The main function for standalone operation.""" +import argparse +import asyncio +import logging +import logging.config +import os +import subprocess +import sys + +from . import cli +from . import parser +from .base import get_event_loop +from .cleanup import cleanup_previous +from .compat import PytestConfig + + +logger = None + + +async def forever(): + while True: + await asyncio.sleep(3600) + + +async def run_and_wait(args, unet): + tasks = [] + + if not args.topology_only: + # add the cmd.wait()s returned from unet.run() + tasks += await unet.run() + + if sys.stdin.isatty() and not args.no_cli: + # Run an interactive CLI + task = cli.async_cli(unet) + else: + if args.no_wait: + logger.info("Waiting for all node cmd to complete") + task = asyncio.gather(*tasks, return_exceptions=True) + else: + logger.info("Waiting on signal to exit") + task = asyncio.create_task(forever()) + task = asyncio.gather(task, *tasks, return_exceptions=True) + + try: + await task + finally: + # Basically we are canceling tasks from unet.run() which are just async calls to + # node.cmd_p.wait() so we've stopped waiting for them to complete, but not + # actually canceld/killed the cmd_p process. + for task in tasks: + task.cancel() + + +async def async_main(args, config): + status = 3 + + # Setup the namespaces and network addressing. + + unet = await parser.async_build_topology( + config, rundir=args.rundir, args=args, pytestconfig=PytestConfig(args) + ) + logger.info("Topology up: rundir: %s", unet.rundir) + + try: + status = await run_and_wait(args, unet) + except KeyboardInterrupt: + logger.info("Exiting, received KeyboardInterrupt in async_main") + except asyncio.CancelledError as ex: + logger.info("task canceled error: %s cleaning up", ex) + except Exception as error: + logger.info("Exiting, unexpected exception %s", error, exc_info=True) + else: + logger.info("Exiting normally") + + logger.debug("main: async deleting") + try: + await unet.async_delete() + except KeyboardInterrupt: + status = 2 + logger.warning("Received KeyboardInterrupt while cleaning up.") + except Exception as error: + status = 2 + logger.info("Deleting, unexpected exception %s", error, exc_info=True) + return status + + +def main(*args): + ap = argparse.ArgumentParser(args) + cap = ap.add_argument_group(title="Config", description="config related options") + + cap.add_argument("-c", "--config", help="config file (yaml, toml, json, ...)") + cap.add_argument( + "-d", "--rundir", help="runtime directory for tempfiles, logs, etc" + ) + cap.add_argument( + "--kinds-config", + help="kinds config file, overrides default search (yaml, toml, json, ...)", + ) + cap.add_argument( + "--project-root", help="directory to stop searching for kinds config at" + ) + rap = ap.add_argument_group(title="Runtime", description="runtime related options") + rap.add_argument( + "-C", + "--cleanup", + action="store_true", + help="Remove the entire rundir (not just node subdirs) prior to running.", + ) + rap.add_argument( + "--gdb", metavar="NODE-LIST", help="comma-sep list of hosts to run gdb on" + ) + rap.add_argument( + "--gdb-breakpoints", + metavar="BREAKPOINT-LIST", + help="comma-sep list of breakpoints to set", + ) + rap.add_argument( + "--host", + action="store_true", + help="no isolation for top namespace, bridges exposed to default namespace", + ) + rap.add_argument( + "--pcap", + metavar="TARGET-LIST", + help="comma-sep list of capture targets (NETWORK or NODE:IFNAME)", + ) + rap.add_argument( + "--shell", metavar="NODE-LIST", help="comma-sep list of nodes to open shells on" + ) + rap.add_argument( + "--stderr", + metavar="NODE-LIST", + help="comma-sep list of nodes to open windows viewing stderr", + ) + rap.add_argument( + "--stdout", + metavar="NODE-LIST", + help="comma-sep list of nodes to open windows viewing stdout", + ) + rap.add_argument( + "--topology-only", + action="store_true", + help="Do not run any node commands", + ) + rap.add_argument("--unshare-inline", action="store_true", help=argparse.SUPPRESS) + rap.add_argument( + "--validate-only", + action="store_true", + help="Validate the config against the schema definition", + ) + rap.add_argument("-v", "--verbose", action="store_true", help="be verbose") + rap.add_argument( + "-V", "--version", action="store_true", help="print the verison number and exit" + ) + eap = ap.add_argument_group(title="Uncommon", description="uncommonly used options") + eap.add_argument("--log-config", help="logging config file (yaml, toml, json, ...)") + eap.add_argument( + "--no-kill", + action="store_true", + help="Do not kill previous running processes", + ) + eap.add_argument( + "--no-cli", action="store_true", help="Do not run the interactive CLI" + ) + eap.add_argument("--no-wait", action="store_true", help="Exit after commands") + + args = ap.parse_args() + + if args.version: + from importlib import metadata # pylint: disable=C0415 + + print(metadata.version("munet")) + sys.exit(0) + + rundir = args.rundir if args.rundir else "/tmp/munet" + args.rundir = rundir + + if args.cleanup: + if os.path.exists(rundir): + if not os.path.exists(f"{rundir}/config.json"): + logging.critical( + 'unsafe: won\'t clean up rundir "%s" as ' + "previous config.json not present", + rundir, + ) + sys.exit(1) + else: + subprocess.run(["/usr/bin/rm", "-rf", rundir], check=True) + + subprocess.run(f"mkdir -p {rundir} && chmod 755 {rundir}", check=True, shell=True) + os.environ["MUNET_RUNDIR"] = rundir + + parser.setup_logging(args) + + global logger # pylint: disable=W0603 + logger = logging.getLogger("munet") + + config = parser.get_config(args.config) + logger.info("Loaded config from %s", config["config_pathname"]) + if not config["topology"]["nodes"]: + logger.critical("No nodes defined in config file") + return 1 + + if not args.no_kill: + cleanup_previous() + + loop = None + status = 4 + try: + parser.validate_config(config, logger, args) + if args.validate_only: + return 0 + # Executes the cmd for each node. + loop = get_event_loop() + status = loop.run_until_complete(async_main(args, config)) + except KeyboardInterrupt: + logger.info("Exiting, received KeyboardInterrupt in main") + except Exception as error: + logger.info("Exiting, unexpected exception %s", error, exc_info=True) + finally: + if loop: + loop.close() + + return status + + +if __name__ == "__main__": + exit_status = main() + sys.exit(exit_status) diff --git a/tests/topotests/munet/base.py b/tests/topotests/munet/base.py new file mode 100644 index 0000000000..9238736c3e --- /dev/null +++ b/tests/topotests/munet/base.py @@ -0,0 +1,3047 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# July 9 2021, Christian Hopps <chopps@labn.net> +# +# Copyright 2021, LabN Consulting, L.L.C. +# +"""A module that implements core functionality for library or standalone use.""" +import asyncio +import datetime +import errno +import ipaddress +import logging +import os +import platform +import re +import readline +import shlex +import signal +import subprocess +import sys +import tempfile +import time as time_mod + +from collections import defaultdict +from pathlib import Path +from typing import Union + +from . import linux + + +try: + import pexpect + + from pexpect.fdpexpect import fdspawn + from pexpect.popen_spawn import PopenSpawn + + have_pexpect = True +except ImportError: + have_pexpect = False + +PEXPECT_PROMPT = "PEXPECT_PROMPT>" +PEXPECT_CONTINUATION_PROMPT = "PEXPECT_PROMPT+" + +root_hostname = subprocess.check_output("hostname") +our_pid = os.getpid() + + +class MunetError(Exception): + """A generic munet error.""" + + +class CalledProcessError(subprocess.CalledProcessError): + """Improved logging subclass of subprocess.CalledProcessError.""" + + def __str__(self): + o = self.output.strip() if self.output else "" + e = self.stderr.strip() if self.stderr else "" + s = f"returncode: {self.returncode} command: {self.cmd}" + o = "\n\tstdout: " + o if o else "" + e = "\n\tstderr: " + e if e else "" + return s + o + e + + def __repr__(self): + o = self.output.strip() if self.output else "" + e = self.stderr.strip() if self.stderr else "" + return f"munet.base.CalledProcessError({self.returncode}, {self.cmd}, {o}, {e})" + + +class Timeout: + """An object to passively monitor for timeouts.""" + + def __init__(self, delta): + self.delta = datetime.timedelta(seconds=delta) + self.started_on = datetime.datetime.now() + self.expires_on = self.started_on + self.delta + + def elapsed(self): + elapsed = datetime.datetime.now() - self.started_on + return elapsed.total_seconds() + + def is_expired(self): + return datetime.datetime.now() > self.expires_on + + def remaining(self): + remaining = self.expires_on - datetime.datetime.now() + return remaining.total_seconds() + + def __iter__(self): + return self + + def __next__(self): + remaining = self.remaining() + if remaining <= 0: + raise StopIteration() + return remaining + + +def fsafe_name(name): + return "".join(x if x.isalnum() else "_" for x in name) + + +def indent(s): + return "\t" + s.replace("\n", "\n\t") + + +def shell_quote(command): + """Return command wrapped in single quotes.""" + if sys.version_info[0] >= 3: + return shlex.quote(command) + return "'" + command.replace("'", "'\"'\"'") + "'" + + +def cmd_error(rc, o, e): + s = f"rc {rc}" + o = "\n\tstdout: " + o.strip() if o and o.strip() else "" + e = "\n\tstderr: " + e.strip() if e and e.strip() else "" + return s + o + e + + +def proc_str(p): + if hasattr(p, "args"): + args = p.args if isinstance(p.args, str) else " ".join(p.args) + else: + args = "" + return f"proc pid: {p.pid} args: {args}" + + +def proc_error(p, o, e): + if hasattr(p, "args"): + args = p.args if isinstance(p.args, str) else " ".join(p.args) + else: + args = "" + + s = f"rc {p.returncode} pid {p.pid}" + a = "\n\targs: " + args if args else "" + o = "\n\tstdout: " + (o.strip() if o and o.strip() else "*empty*") + e = "\n\tstderr: " + (e.strip() if e and e.strip() else "*empty*") + return s + a + o + e + + +def comm_error(p): + rc = p.poll() + assert rc is not None + if not hasattr(p, "saved_output"): + p.saved_output = p.communicate() + return proc_error(p, *p.saved_output) + + +async def acomm_error(p): + rc = p.returncode + assert rc is not None + if not hasattr(p, "saved_output"): + p.saved_output = await p.communicate() + return proc_error(p, *p.saved_output) + + +def get_kernel_version(): + kvs = ( + subprocess.check_output("uname -r", shell=True, text=True).strip().split("-", 1) + ) + kv = kvs[0].split(".") + kv = [int(x) for x in kv] + return kv + + +def convert_number(value) -> int: + """Convert a number value with a possible suffix to an integer. + + >>> convert_number("100k") == 100 * 1024 + True + >>> convert_number("100M") == 100 * 1000 * 1000 + True + >>> convert_number("100Gi") == 100 * 1024 * 1024 * 1024 + True + >>> convert_number("55") == 55 + True + """ + if value is None: + raise ValueError("Invalid value None for convert_number") + rate = str(value) + base = 1000 + if rate[-1] == "i": + base = 1024 + rate = rate[:-1] + suffix = "KMGTPEZY" + index = suffix.find(rate[-1]) + if index == -1: + base = 1024 + index = suffix.lower().find(rate[-1]) + if index != -1: + rate = rate[:-1] + return int(rate) * base ** (index + 1) + + +def is_file_like(fo): + return isinstance(fo, int) or hasattr(fo, "fileno") + + +def get_tc_bits_value(user_value): + value = convert_number(user_value) / 1000 + return f"{value:03f}kbit" + + +def get_tc_bytes_value(user_value): + # Raw numbers are bytes in tc + return convert_number(user_value) + + +def get_tmp_dir(uniq): + return os.path.join(tempfile.mkdtemp(), uniq) + + +async def _async_get_exec_path(binary, cmdf, cache): + if isinstance(binary, str): + bins = [binary] + else: + bins = binary + for b in bins: + if b in cache: + return cache[b] + + rc, output, _ = await cmdf("which " + b, warn=False) + if not rc: + cache[b] = os.path.abspath(output.strip()) + return cache[b] + return None + + +def _get_exec_path(binary, cmdf, cache): + if isinstance(binary, str): + bins = [binary] + else: + bins = binary + for b in bins: + if b in cache: + return cache[b] + + rc, output, _ = cmdf("which " + b, warn=False) + if not rc: + cache[b] = os.path.abspath(output.strip()) + return cache[b] + return None + + +def get_event_loop(): + """Configure and return our non-thread using event loop. + + This function configures a new child watcher to not use threads. + Threads cannot be used when we inline unshare a PID namespace. + """ + policy = asyncio.get_event_loop_policy() + loop = policy.get_event_loop() + owatcher = policy.get_child_watcher() + logging.debug( + "event_loop_fixture: global policy %s, current loop %s, current watcher %s", + policy, + loop, + owatcher, + ) + + policy.set_child_watcher(None) + owatcher.close() + + try: + watcher = asyncio.PidfdChildWatcher() # pylint: disable=no-member + except Exception: + watcher = asyncio.SafeChildWatcher() + loop = policy.get_event_loop() + + logging.debug( + "event_loop_fixture: attaching new watcher %s to loop and setting in policy", + watcher, + ) + watcher.attach_loop(loop) + policy.set_child_watcher(watcher) + policy.set_event_loop(loop) + assert asyncio.get_event_loop_policy().get_child_watcher() is watcher + + return loop + + +class Commander: # pylint: disable=R0904 + """An object that can execute commands.""" + + tmux_wait_gen = 0 + + def __init__(self, name, logger=None, unet=None, **kwargs): + """Create a Commander. + + Args: + name: name of the commander object + logger: logger to use for logging commands a defualt is supplied if this + is None + unet: unet that owns this object, only used by Commander in run_in_window, + otherwise can be None. + """ + # del kwargs # deal with lint warning + # logging.warning("Commander: name %s kwargs %s", name, kwargs) + + self.name = name + self.unet = unet + self.deleting = False + self.last = None + self.exec_paths = {} + + if not logger: + logname = f"munet.{self.__class__.__name__.lower()}.{name}" + self.logger = logging.getLogger(logname) + self.logger.setLevel(logging.DEBUG) + else: + self.logger = logger + + super().__init__(**kwargs) + + @property + def is_vm(self): + return False + + @property + def is_container(self): + return False + + def set_logger(self, logfile): + self.logger = logging.getLogger(__name__ + ".commander." + self.name) + self.logger.setLevel(logging.DEBUG) + if isinstance(logfile, str): + handler = logging.FileHandler(logfile, mode="w") + else: + handler = logging.StreamHandler(logfile) + + fmtstr = "%(asctime)s.%(msecs)03d %(levelname)s: {}({}): %(message)s".format( + self.__class__.__name__, self.name + ) + handler.setFormatter(logging.Formatter(fmt=fmtstr)) + self.logger.addHandler(handler) + + def _get_pre_cmd(self, use_str, use_pty, **kwargs): + """Get the pre-user-command values. + + The values returned here should be what is required to cause the user's command + to execute in the correct context (e.g., namespace, container, sshremote). + """ + del kwargs + del use_pty + return "" if use_str else [] + + def __str__(self): + return f"{self.__class__.__name__}({self.name})" + + async def async_get_exec_path(self, binary): + """Return the full path to the binary executable. + + `binary` :: binary name or list of binary names + """ + return await _async_get_exec_path( + binary, self.async_cmd_status_nsonly, self.exec_paths + ) + + def get_exec_path(self, binary): + """Return the full path to the binary executable. + + `binary` :: binary name or list of binary names + """ + return _get_exec_path(binary, self.cmd_status_nsonly, self.exec_paths) + + def get_exec_path_host(self, binary): + """Return the full path to the binary executable. + + If the object is actually a derived class (e.g., a container) this method will + return the exec path for the native namespace rather than the container. The + path is the one which the other xxx_host methods will use. + + `binary` :: binary name or list of binary names + """ + return get_exec_path_host(binary) + + def test(self, flags, arg): + """Run test binary, with flags and arg.""" + test_path = self.get_exec_path(["test"]) + rc, _, _ = self.cmd_status([test_path, flags, arg], warn=False) + return not rc + + def test_nsonly(self, flags, arg): + """Run test binary, with flags and arg.""" + test_path = self.get_exec_path(["test"]) + rc, _, _ = self.cmd_status_nsonly([test_path, flags, arg], warn=False) + return not rc + + def path_exists(self, path): + """Check if path exists.""" + return self.test("-e", path) + + async def cleanup_pid(self, pid, kill_pid=None): + """Signal a pid to exit with escalating forcefulness.""" + if kill_pid is None: + kill_pid = pid + + for sn in (signal.SIGHUP, signal.SIGKILL): + self.logger.debug( + "%s: %s %s (wait %s)", self, signal.Signals(sn).name, kill_pid, pid + ) + + os.kill(kill_pid, sn) + + # No need to wait after this. + if sn == signal.SIGKILL: + return + + # try each signal, waiting 15 seconds for exit before advancing + wait_sec = 30 + self.logger.debug("%s: waiting %ss for pid to exit", self, wait_sec) + for _ in Timeout(wait_sec): + try: + status = os.waitpid(pid, os.WNOHANG) + if status == (0, 0): + await asyncio.sleep(0.1) + else: + self.logger.debug("pid %s exited status %s", pid, status) + return + except OSError as error: + if error.errno == errno.ECHILD: + self.logger.debug("%s: pid %s was reaped", self, pid) + else: + self.logger.warning( + "%s: error waiting on pid %s: %s", self, pid, error + ) + return + self.logger.debug("%s: timeout waiting on pid %s to exit", self, pid) + + def _get_sub_args(self, cmd_list, defaults, use_pty=False, ns_only=False, **kwargs): + """Returns pre-command, cmd, and default keyword args.""" + assert not isinstance(cmd_list, str) + + defaults["shell"] = False + pre_cmd_list = self._get_pre_cmd(False, use_pty, ns_only=ns_only, **kwargs) + cmd_list = [str(x) for x in cmd_list] + + # os_env = {k: v for k, v in os.environ.items() if k.startswith("MUNET")} + # env = {**os_env, **(kwargs["env"] if "env" in kwargs else {})} + env = {**(kwargs["env"] if "env" in kwargs else os.environ)} + if "MUNET_NODENAME" not in env: + env["MUNET_NODENAME"] = self.name + kwargs["env"] = env + + defaults.update(kwargs) + + return pre_cmd_list, cmd_list, defaults + + def _common_prologue(self, async_exec, method, cmd, skip_pre_cmd=False, **kwargs): + cmd_list = self._get_cmd_as_list(cmd) + if method == "_spawn": + defaults = { + "encoding": "utf-8", + "codec_errors": "ignore", + } + else: + defaults = { + "stdout": subprocess.PIPE, + "stderr": subprocess.PIPE, + } + if not async_exec: + defaults["encoding"] = "utf-8" + + pre_cmd_list, cmd_list, defaults = self._get_sub_args( + cmd_list, defaults, **kwargs + ) + + use_pty = kwargs.get("use_pty", False) + if method == "_spawn": + # spawn doesn't take "shell" keyword arg + if "shell" in defaults: + del defaults["shell"] + # this is required to avoid receiving a STOPPED signal on expect! + if not use_pty: + defaults["preexec_fn"] = os.setsid + defaults["env"]["PS1"] = "$ " + + self.logger.debug( + '%s: %s %s("%s", pre_cmd: "%s" use_pty: %s kwargs: %.120s)', + self, + "XXX" if method == "_spawn" else "", + method, + cmd_list, + pre_cmd_list if not skip_pre_cmd else "", + use_pty, + defaults, + ) + + actual_cmd_list = cmd_list if skip_pre_cmd else pre_cmd_list + cmd_list + return actual_cmd_list, defaults + + async def _async_popen(self, method, cmd, **kwargs): + """Create a new asynchronous subprocess.""" + acmd, kwargs = self._common_prologue(True, method, cmd, **kwargs) + p = await asyncio.create_subprocess_exec(*acmd, **kwargs) + return p, acmd + + def _popen(self, method, cmd, **kwargs): + """Create a subprocess.""" + acmd, kwargs = self._common_prologue(False, method, cmd, **kwargs) + p = subprocess.Popen(acmd, **kwargs) + return p, acmd + + def _fdspawn(self, fo, **kwargs): + defaults = {} + defaults.update(kwargs) + + if "echo" in defaults: + del defaults["echo"] + + if "encoding" not in defaults: + defaults["encoding"] = "utf-8" + if "codec_errors" not in defaults: + defaults["codec_errors"] = "ignore" + encoding = defaults["encoding"] + + self.logger.debug("%s: _fdspawn(%s, kwargs: %s)", self, fo, defaults) + + p = fdspawn(fo, **defaults) + + # We don't have TTY like conversions of LF to CRLF + p.crlf = os.linesep.encode(encoding) + + # we own the socket now detach the file descriptor to keep it from closing + if hasattr(fo, "detach"): + fo.detach() + + return p + + def _spawn(self, cmd, skip_pre_cmd=False, use_pty=False, echo=False, **kwargs): + logging.debug( + '%s: XXX _spawn: cmd "%s" skip_pre_cmd %s use_pty %s echo %s kwargs %s', + self, + cmd, + skip_pre_cmd, + use_pty, + echo, + kwargs, + ) + actual_cmd, defaults = self._common_prologue( + False, "_spawn", cmd, skip_pre_cmd=skip_pre_cmd, use_pty=use_pty, **kwargs + ) + + self.logger.debug( + '%s: XXX %s("%s", use_pty %s echo %s defaults: %s)', + self, + "PopenSpawn" if not use_pty else "pexpect.spawn", + actual_cmd, + use_pty, + echo, + defaults, + ) + + # We don't specify a timeout it defaults to 30s is that OK? + if not use_pty: + p = PopenSpawn(actual_cmd, **defaults) + else: + p = pexpect.spawn(actual_cmd[0], actual_cmd[1:], echo=echo, **defaults) + return p, actual_cmd + + def spawn( + self, + cmd, + spawned_re, + expects=(), + sends=(), + use_pty=False, + logfile=None, + logfile_read=None, + logfile_send=None, + trace=None, + **kwargs, + ): + """Create a spawned send/expect process. + + Args: + cmd: list of args to exec/popen with, or an already open socket + spawned_re: what to look for to know when done, `spawn` returns when seen + expects: a list of regex other than `spawned_re` to look for. Commonly, + "ogin:" or "[Pp]assword:"r. + sends: what to send when an element of `expects` matches. So e.g., the + username or password if thats what corresponding expect matched. Can + be the empty string to send nothing. + use_pty: true for pty based expect, otherwise uses popen (pipes/files) + trace: if true then log send/expects + **kwargs - kwargs passed on the _spawn. + + Returns: + A pexpect process. + + Raises: + pexpect.TIMEOUT, pexpect.EOF as documented in `pexpect` + CalledProcessError if EOF is seen and `cmd` exited then + raises a CalledProcessError to indicate the failure. + """ + if is_file_like(cmd): + assert not use_pty + ac = "*socket*" + p = self._fdspawn(cmd, **kwargs) + else: + p, ac = self._spawn(cmd, use_pty=use_pty, **kwargs) + + if logfile: + p.logfile = logfile + if logfile_read: + p.logfile_read = logfile_read + if logfile_send: + p.logfile_send = logfile_send + + # for spawned shells (i.e., a direct command an not a console) + # this is wrong and will cause 2 prompts + if not use_pty: + # This isn't very nice looking + p.echo = False + if not is_file_like(cmd): + p.isalive = lambda: p.proc.poll() is None + if not hasattr(p, "close"): + p.close = p.wait + + # Do a quick check to see if we got the prompt right away, otherwise we may be + # at a console so we send a \n to re-issue the prompt + index = p.expect([spawned_re, pexpect.TIMEOUT, pexpect.EOF], timeout=0.1) + if index == 0: + assert p.match is not None + self.logger.debug( + "%s: got spawned_re quick: '%s' matching '%s'", + self, + p.match.group(0), + spawned_re, + ) + return p + + # Now send a CRLF to cause the prompt (or whatever else) to re-issue + p.send("\n") + try: + patterns = [spawned_re, *expects] + + self.logger.debug("%s: expecting: %s", self, patterns) + + while index := p.expect(patterns): + if trace: + assert p.match is not None + self.logger.debug( + "%s: got expect: '%s' matching %d '%s', sending '%s'", + self, + p.match.group(0), + index, + patterns[index], + sends[index - 1], + ) + if sends[index - 1]: + p.send(sends[index - 1]) + + self.logger.debug("%s: expecting again: %s", self, patterns) + self.logger.debug( + "%s: got spawned_re: '%s' matching '%s'", + self, + p.match.group(0), + spawned_re, + ) + return p + except pexpect.TIMEOUT: + self.logger.error( + "%s: TIMEOUT looking for spawned_re '%s' expect buffer so far:\n%s", + self, + spawned_re, + indent(p.buffer), + ) + raise + except pexpect.EOF as eoferr: + if p.isalive(): + raise + rc = p.status + before = indent(p.before) + error = CalledProcessError(rc, ac, output=before) + self.logger.error( + "%s: EOF looking for spawned_re '%s' before EOF:\n%s", + self, + spawned_re, + before, + ) + p.close() + raise error from eoferr + + async def shell_spawn( + self, + cmd, + prompt, + expects=(), + sends=(), + use_pty=False, + will_echo=False, + is_bourne=True, + init_newline=False, + **kwargs, + ): + """Create a shell REPL (read-eval-print-loop). + + Args: + cmd: shell and list of args to popen with, or an already open socket + prompt: the REPL prompt to look for, the function returns when seen + expects: a list of regex other than `spawned_re` to look for. Commonly, + "ogin:" or "[Pp]assword:"r. + sends: what to send when an element of `expects` matches. So e.g., the + username or password if thats what corresponding expect matched. Can + be the empty string to send nothing. + is_bourne: if False then do not modify shell prompt for internal + parser friently format, and do not expect continuation prompts. + init_newline: send an initial newline for non-bourne shell spawns, otherwise + expect the prompt simply from running the command + use_pty: true for pty based expect, otherwise uses popen (pipes/files) + will_echo: bash is buggy in that it echo's to non-tty unlike any other + sh/ksh, set this value to true if running back + **kwargs - kwargs passed on the _spawn. + """ + combined_prompt = r"({}|{})".format(re.escape(PEXPECT_PROMPT), prompt) + + assert not is_file_like(cmd) or not use_pty + p = self.spawn( + cmd, + combined_prompt, + expects=expects, + sends=sends, + use_pty=use_pty, + echo=False, + **kwargs, + ) + assert not p.echo + + if not is_bourne: + if init_newline: + p.send("\n") + return ShellWrapper(p, prompt, will_echo=will_echo) + + ps1 = PEXPECT_PROMPT + ps2 = PEXPECT_CONTINUATION_PROMPT + + # Avoid problems when =/usr/bin/env= prints the values + ps1p = ps1[:5] + "${UNSET_V}" + ps1[5:] + ps2p = ps2[:5] + "${UNSET_V}" + ps2[5:] + + ps1 = re.escape(ps1) + ps2 = re.escape(ps2) + + extra = "PAGER=cat; export PAGER; TERM=dumb; unset HISTFILE; set +o emacs +o vi" + pchg = "PS1='{0}' PS2='{1}' PROMPT_COMMAND=''\n".format(ps1p, ps2p) + p.send(pchg) + return ShellWrapper(p, ps1, ps2, extra_init_cmd=extra, will_echo=will_echo) + + def popen(self, cmd, **kwargs): + """Creates a pipe with the given `command`. + + Args: + cmd: `str` or `list` of command to open a pipe with. + **kwargs: kwargs is eventually passed on to Popen. If `command` is a string + then will be invoked with `bash -c`, otherwise `command` is a list and + will be invoked without a shell. + + Returns: + a subprocess.Popen object. + """ + return self._popen("popen", cmd, **kwargs)[0] + + def popen_nsonly(self, cmd, **kwargs): + """Creates a pipe with the given `command`. + + Args: + cmd: `str` or `list` of command to open a pipe with. + **kwargs: kwargs is eventually passed on to Popen. If `command` is a string + then will be invoked with `bash -c`, otherwise `command` is a list and + will be invoked without a shell. + + Returns: + a subprocess.Popen object. + """ + return self._popen("popen_nsonly", cmd, ns_only=True, **kwargs)[0] + + async def async_popen(self, cmd, **kwargs): + """Creates a pipe with the given `command`. + + Args: + cmd: `str` or `list` of command to open a pipe with. + **kwargs: kwargs is eventually passed on to create_subprocess_exec. If + `command` is a string then will be invoked with `bash -c`, otherwise + `command` is a list and will be invoked without a shell. + + Returns: + a asyncio.subprocess.Process object. + """ + p, _ = await self._async_popen("async_popen", cmd, **kwargs) + return p + + async def async_popen_nsonly(self, cmd, **kwargs): + """Creates a pipe with the given `command`. + + Args: + cmd: `str` or `list` of command to open a pipe with. + **kwargs: kwargs is eventually passed on to create_subprocess_exec. If + `command` is a string then will be invoked with `bash -c`, otherwise + `command` is a list and will be invoked without a shell. + + Returns: + a asyncio.subprocess.Process object. + """ + p, _ = await self._async_popen( + "async_popen_nsonly", cmd, ns_only=True, **kwargs + ) + return p + + async def async_cleanup_proc(self, p, pid=None): + """Terminate a process started with a popen call. + + Args: + p: return value from :py:`async_popen`, :py:`popen`, et al. + pid: pid to signal instead of p.pid, typically a child of + cmd_p == nsenter. + + Returns: + None on success, the ``p`` if multiple timeouts occur even + after a SIGKILL sent. + """ + if not p: + return None + + if p.returncode is not None: + if isinstance(p, subprocess.Popen): + o, e = p.communicate() + else: + o, e = await p.communicate() + self.logger.debug( + "%s: cmd_p already exited status: %s", self, proc_error(p, o, e) + ) + return None + + if pid is None: + pid = p.pid + + self.logger.debug("%s: terminate process: %s (pid %s)", self, proc_str(p), pid) + try: + # This will SIGHUP and wait a while then SIGKILL and return immediately + await self.cleanup_pid(p.pid, pid) + + # Wait another 2 seconds after the possible SIGKILL above for the + # parent nsenter to cleanup and exit + wait_secs = 2 + if isinstance(p, subprocess.Popen): + o, e = p.communicate(timeout=wait_secs) + else: + o, e = await asyncio.wait_for(p.communicate(), timeout=wait_secs) + self.logger.debug( + "%s: cmd_p exited after kill, status: %s", self, proc_error(p, o, e) + ) + except (asyncio.TimeoutError, subprocess.TimeoutExpired): + self.logger.warning("%s: SIGKILL timeout", self) + return p + except Exception as error: + self.logger.warning( + "%s: kill unexpected exception: %s", self, error, exc_info=True + ) + return p + return None + + @staticmethod + def _cmd_status_input(stdin): + pinput = None + if isinstance(stdin, (bytes, str)): + pinput = stdin + stdin = subprocess.PIPE + return pinput, stdin + + def _cmd_status_finish(self, p, c, ac, o, e, raises, warn): + rc = p.returncode + self.last = (rc, ac, c, o, e) + if rc: + if warn: + self.logger.warning("%s: proc failed: %s", self, proc_error(p, o, e)) + if raises: + # error = Exception("stderr: {}".format(stderr)) + # This annoyingly doesnt' show stderr when printed normally + raise CalledProcessError(rc, ac, o, e) + return rc, o, e + + def _cmd_status(self, cmds, raises=False, warn=True, stdin=None, **kwargs): + """Execute a command.""" + pinput, stdin = Commander._cmd_status_input(stdin) + p, actual_cmd = self._popen("cmd_status", cmds, stdin=stdin, **kwargs) + o, e = p.communicate(pinput) + return self._cmd_status_finish(p, cmds, actual_cmd, o, e, raises, warn) + + async def _async_cmd_status( + self, cmds, raises=False, warn=True, stdin=None, text=None, **kwargs + ): + """Execute a command.""" + pinput, stdin = Commander._cmd_status_input(stdin) + p, actual_cmd = await self._async_popen( + "async_cmd_status", cmds, stdin=stdin, **kwargs + ) + + if text is False: + encoding = None + else: + encoding = kwargs.get("encoding", "utf-8") + + if encoding is not None and isinstance(pinput, str): + pinput = pinput.encode(encoding) + o, e = await p.communicate(pinput) + if encoding is not None: + o = o.decode(encoding) if o is not None else o + e = e.decode(encoding) if e is not None else e + return self._cmd_status_finish(p, cmds, actual_cmd, o, e, raises, warn) + + def _get_cmd_as_list(self, cmd): + """Given a list or string return a list form for execution. + + If `cmd` is a string then the returned list uses bash and looks + like this: ["/bin/bash", "-c", cmd]. Some node types override + this function if they utilize a different shell as to return + a different list of values. + + Args: + cmd: list or string representing the command to execute. + + Returns: + list of commands to execute. + """ + if not isinstance(cmd, str): + cmds = cmd + else: + # Make sure the code doesn't think `cd` will work. + assert not re.match(r"cd(\s*|\s+(\S+))$", cmd) + cmds = ["/bin/bash", "-c", cmd] + return cmds + + def cmd_nostatus(self, cmd, **kwargs): + """Run given command returning output[s]. + + Args: + cmd: `str` or `list` of the command to execute. If a string is given + it is run using a shell, otherwise the list is executed directly + as the binary and arguments. + **kwargs: kwargs is eventually passed on to Popen. If `command` is a string + then will be invoked with `bash -c`, otherwise `command` is a list and + will be invoked without a shell. + + Returns: + if "stderr" is in kwargs and not equal to subprocess.STDOUT, then + both stdout and stderr are returned, otherwise stderr is combined + with stdout and only stdout is returned. + """ + # + # This method serves as the basis for all derived sync cmd variations, so to + # override sync cmd behavior simply override this function and *not* the other + # variations, unless you are changing only that variation's behavior + # + + # XXX change this back to _cmd_status instead of cmd_status when we + # consolidate and cleanup the container overrides of *cmd_* functions + + cmds = cmd + if "stderr" in kwargs and kwargs["stderr"] != subprocess.STDOUT: + _, o, e = self.cmd_status(cmds, **kwargs) + return o, e + if "stderr" in kwargs: + del kwargs["stderr"] + _, o, _ = self.cmd_status(cmds, stderr=subprocess.STDOUT, **kwargs) + return o + + def cmd_status(self, cmd, **kwargs): + """Run given command returning status and outputs. + + Args: + cmd: `str` or `list` of the command to execute. If a string is given + it is run using a shell, otherwise the list is executed directly + as the binary and arguments. + **kwargs: kwargs is eventually passed on to Popen. If `command` is a string + then will be invoked with `bash -c`, otherwise `command` is a list and + will be invoked without a shell. + + Returns: + (status, output, error) are returned + status: the returncode of the command. + output: stdout as a string from the command. + error: stderr as a string from the command. + """ + # + # This method serves as the basis for all derived sync cmd variations, so to + # override sync cmd behavior simply override this function and *not* the other + # variations, unless you are changing only that variation's behavior + # + return self._cmd_status(cmd, **kwargs) + + def cmd_raises(self, cmd, **kwargs): + """Execute a command. Raise an exception on errors. + + Args: + cmd: `str` or `list` of the command to execute. If a string is given + it is run using a shell, otherwise the list is executed directly + as the binary and arguments. + **kwargs: kwargs is eventually passed on to Popen. If `command` is a string + then will be invoked with `bash -c`, otherwise `command` is a list and + will be invoked without a shell. + + Returns: + output: stdout as a string from the command. + + Raises: + CalledProcessError: on non-zero exit status + """ + _, stdout, _ = self._cmd_status(cmd, raises=True, **kwargs) + return stdout + + def cmd_nostatus_nsonly(self, cmd, **kwargs): + # Make sure the command runs on the host and not in any container. + return self.cmd_nostatus(cmd, ns_only=True, **kwargs) + + def cmd_status_nsonly(self, cmd, **kwargs): + # Make sure the command runs on the host and not in any container. + return self._cmd_status(cmd, ns_only=True, **kwargs) + + def cmd_raises_nsonly(self, cmd, **kwargs): + # Make sure the command runs on the host and not in any container. + _, stdout, _ = self._cmd_status(cmd, raises=True, ns_only=True, **kwargs) + return stdout + + async def async_cmd_status(self, cmd, **kwargs): + """Run given command returning status and outputs. + + Args: + cmd: `str` or `list` of the command to execute. If a string is given + it is run using a shell, otherwise the list is executed directly + as the binary and arguments. + **kwargs: kwargs is eventually passed on to create_subprocess_exec. If + `cmd` is a string then will be invoked with `bash -c`, otherwise + `cmd` is a list and will be invoked without a shell. + + Returns: + (status, output, error) are returned + status: the returncode of the command. + output: stdout as a string from the command. + error: stderr as a string from the command. + """ + # + # This method serves as the basis for all derived async cmd variations, so to + # override async cmd behavior simply override this function and *not* the other + # variations, unless you are changing only that variation's behavior + # + return await self._async_cmd_status(cmd, **kwargs) + + async def async_cmd_nostatus(self, cmd, **kwargs): + """Run given command returning output[s]. + + Args: + cmd: `str` or `list` of the command to execute. If a string is given + it is run using a shell, otherwise the list is executed directly + as the binary and arguments. + **kwargs: kwargs is eventually passed on to create_subprocess_exec. If + `cmd` is a string then will be invoked with `bash -c`, otherwise + `cmd` is a list and will be invoked without a shell. + + Returns: + if "stderr" is in kwargs and not equal to subprocess.STDOUT, then + both stdout and stderr are returned, otherwise stderr is combined + with stdout and only stdout is returned. + + """ + # XXX change this back to _async_cmd_status instead of cmd_status when we + # consolidate and cleanup the container overrides of *cmd_* functions + + cmds = cmd + if "stderr" in kwargs and kwargs["stderr"] != subprocess.STDOUT: + _, o, e = await self._async_cmd_status(cmds, **kwargs) + return o, e + if "stderr" in kwargs: + del kwargs["stderr"] + _, o, _ = await self._async_cmd_status(cmds, stderr=subprocess.STDOUT, **kwargs) + return o + + async def async_cmd_raises(self, cmd, **kwargs): + """Execute a command. Raise an exception on errors. + + Args: + cmd: `str` or `list` of the command to execute. If a string is given + it is run using a shell, otherwise the list is executed directly + as the binary and arguments. + **kwargs: kwargs is eventually passed on to create_subprocess_exec. If + `cmd` is a string then will be invoked with `bash -c`, otherwise + `cmd` is a list and will be invoked without a shell. + + Returns: + output: stdout as a string from the command. + + Raises: + CalledProcessError: on non-zero exit status + """ + _, stdout, _ = await self._async_cmd_status(cmd, raises=True, **kwargs) + return stdout + + async def async_cmd_status_nsonly(self, cmd, **kwargs): + # Make sure the command runs on the host and not in any container. + return await self._async_cmd_status(cmd, ns_only=True, **kwargs) + + async def async_cmd_raises_nsonly(self, cmd, **kwargs): + # Make sure the command runs on the host and not in any container. + _, stdout, _ = await self._async_cmd_status( + cmd, raises=True, ns_only=True, **kwargs + ) + return stdout + + def cmd_legacy(self, cmd, **kwargs): + """Execute a command with stdout and stderr joined, *IGNORES ERROR*.""" + defaults = {"stderr": subprocess.STDOUT} + defaults.update(kwargs) + _, stdout, _ = self._cmd_status(cmd, raises=False, **defaults) + return stdout + + # Run a command in a new window (gnome-terminal, screen, tmux, xterm) + def run_in_window( + self, + cmd, + wait_for=False, + background=False, + name=None, + title=None, + forcex=False, + new_window=False, + tmux_target=None, + ns_only=False, + ): + """Run a command in a new window (TMUX, Screen or XTerm). + + Args: + cmd: string to execute. + wait_for: True to wait for exit from command or `str` as channel neme to + signal on exit, otherwise False + background: Do not change focus to new window. + title: Title for new pane (tmux) or window (xterm). + name: Name of the new window (tmux) + forcex: Force use of X11. + new_window: Open new window (instead of pane) in TMUX + tmux_target: Target for tmux pane. + + Returns: + the pane/window identifier from TMUX (depends on `new_window`) + """ + channel = None + if isinstance(wait_for, str): + channel = wait_for + elif wait_for is True: + channel = "{}-wait-{}".format(our_pid, Commander.tmux_wait_gen) + Commander.tmux_wait_gen += 1 + + if forcex or ("TMUX" not in os.environ and "STY" not in os.environ): + root_level = False + else: + root_level = True + + # SUDO: The important thing to note is that with all these methods we are + # executing on the users windowing system, so even though we are normally + # running as root, we will not be when the command is dispatched. Also + # in the case of SCREEN and X11 we need to sudo *back* to the user as well + # This is also done by SSHRemote by defualt so we should *not* sudo back + # if we are SSHRemote. + + # XXX need to test ssh in screen + # XXX need to test ssh in Xterm + sudo_path = get_exec_path_host(["sudo"]) + # This first test case seems same as last but using list instead of string? + if self.is_vm and self.use_ssh: # pylint: disable=E1101 + if isinstance(cmd, str): + cmd = shlex.split(cmd) + cmd = ["/usr/bin/env", f"MUNET_NODENAME={self.name}"] + cmd + + # get the ssh cmd + cmd = self._get_pre_cmd(False, True, ns_only=ns_only) + [shlex.join(cmd)] + unet = self.unet # pylint: disable=E1101 + uns_cmd = unet._get_pre_cmd( # pylint: disable=W0212 + False, True, ns_only=True, root_level=root_level + ) + # get the nsenter for munet + nscmd = [ + sudo_path, + *uns_cmd, + *cmd, + ] + else: + # This is the command to execute to be inside the namespace. + # We are getting into trouble with quoting. + # Why aren't we passing in MUNET_RUNDIR? + cmd = f"/usr/bin/env MUNET_NODENAME={self.name} {cmd}" + # We need sudo b/c we are executing as the user inside the window system. + sudo_path = get_exec_path_host(["sudo"]) + nscmd = ( + sudo_path + + " " + + self._get_pre_cmd(True, True, ns_only=ns_only, root_level=root_level) + + " " + + cmd + ) + + if "TMUX" in os.environ and not forcex: + cmd = [get_exec_path_host("tmux")] + if new_window: + cmd.append("new-window") + cmd.append("-P") + if name: + cmd.append("-n") + cmd.append(name) + if tmux_target: + cmd.append("-t") + cmd.append(tmux_target) + else: + cmd.append("split-window") + cmd.append("-P") + cmd.append("-h") + if not tmux_target: + tmux_target = os.getenv("TMUX_PANE", "") + if background: + cmd.append("-d") + if tmux_target: + cmd.append("-t") + cmd.append(tmux_target) + + # nscmd is always added as single string argument + if not isinstance(nscmd, str): + nscmd = shlex.join(nscmd) + if title: + nscmd = f"printf '\033]2;{title}\033\\'; {nscmd}" + if channel: + nscmd = f'trap "tmux wait -S {channel}; exit 0" EXIT; {nscmd}' + cmd.append(nscmd) + + elif "STY" in os.environ and not forcex: + # wait for not supported in screen for now + channel = None + cmd = [get_exec_path_host("screen")] + if not os.path.exists( + "/run/screen/S-{}/{}".format(os.environ["USER"], os.environ["STY"]) + ): + # XXX not appropriate for ssh + cmd = ["sudo", "-Eu", os.environ["SUDO_USER"]] + cmd + + if not isinstance(nscmd, str): + nscmd = shlex.join(nscmd) + cmd.append(nscmd) + elif "DISPLAY" in os.environ: + cmd = [get_exec_path_host("xterm")] + if "SUDO_USER" in os.environ: + # Do this b/c making things work as root with xauth seems hard + cmd = [ + get_exec_path_host("sudo"), + "-Eu", + os.environ["SUDO_USER"], + ] + cmd + if title: + cmd.append("-T") + cmd.append(title) + + cmd.append("-e") + if isinstance(nscmd, str): + cmd.extend(shlex.split(nscmd)) + else: + cmd.extend(nscmd) + + # if channel: + # return self.cmd_raises(cmd, skip_pre_cmd=True) + # else: + p = commander.popen( + cmd, + # skip_pre_cmd=True, + stdin=None, + shell=False, + ) + # We should reap the child and report the error then. + time_mod.sleep(2) + if p.poll() is not None: + self.logger.error("%s: Failed to launch xterm: %s", self, comm_error(p)) + return p + else: + self.logger.error( + "DISPLAY, STY, and TMUX not in environment, can't open window" + ) + raise Exception("Window requestd but TMUX, Screen and X11 not available") + + # pane_info = self.cmd_raises(cmd, skip_pre_cmd=True, ns_only=True).strip() + # We are prepending the nsenter command, so use unet.rootcmd + pane_info = commander.cmd_raises(cmd).strip() + + # Re-adjust the layout + if "TMUX" in os.environ: + cmd = [ + get_exec_path_host("tmux"), + "select-layout", + "-t", + pane_info if not tmux_target else tmux_target, + "tiled", + ] + commander.cmd_status(cmd) + + # Wait here if we weren't handed the channel to wait for + if channel and wait_for is True: + cmd = [get_exec_path_host("tmux"), "wait", channel] + # commander.cmd_status(cmd, skip_pre_cmd=True) + commander.cmd_status(cmd) + + return pane_info + + def delete(self): + """Calls self.async_delete within an exec loop.""" + asyncio.run(self.async_delete()) + + async def _async_delete(self): + """Delete this objects resources. + + This is the actual implementation of the resource cleanup, each class + should cleanup it's own resources, generally catching and reporting, + but not reraising any exceptions for it's own cleanup, then it should + invoke `super()._async_delete() without catching any exceptions raised + therein. See other examples in `base.py` or `native.py` + """ + self.logger.info("%s: deleted", self) + + async def async_delete(self): + """Delete the Commander (or derived object). + + The actual implementation for any class should be in `_async_delete` + new derived classes should look at the documentation for that function. + """ + try: + self.deleting = True + await self._async_delete() + except Exception as error: + self.logger.error("%s: error while deleting: %s", self, error) + + +class InterfaceMixin: + """A mixin class to support interface functionality.""" + + def __init__(self, *args, **kwargs): + # del kwargs # get rid of lint + # logging.warning("InterfaceMixin: args: %s kwargs: %s", args, kwargs) + + self._intf_addrs = defaultdict(lambda: [None, None]) + self.net_intfs = {} + self.next_intf_index = 0 + self.basename = "eth" + # self.basename = name + "-eth" + super().__init__(*args, **kwargs) + + @property + def intfs(self): + return sorted(self._intf_addrs.keys()) + + @property + def networks(self): + return sorted(self.net_intfs.keys()) + + def get_intf_addr(self, ifname, ipv6=False): + if ifname not in self._intf_addrs: + return None + return self._intf_addrs[ifname][bool(ipv6)] + + def set_intf_addr(self, ifname, ifaddr): + ifaddr = ipaddress.ip_interface(ifaddr) + self._intf_addrs[ifname][ifaddr.version == 6] = ifaddr + + def net_addr(self, netname, ipv6=False): + if netname not in self.net_intfs: + return None + return self.get_intf_addr(self.net_intfs[netname], ipv6=ipv6) + + def set_intf_basename(self, basename): + self.basename = basename + + def get_next_intf_name(self): + while True: + ifname = self.basename + str(self.next_intf_index) + self.next_intf_index += 1 + if ifname not in self._intf_addrs: + break + return ifname + + def get_ns_ifname(self, ifname): + """Return a namespace unique interface name. + + This function is primarily overriden by L3QemuVM, IOW by any class + that doesn't create it's own network namespace and will share that + with the root (unet) namespace. + + Args: + ifname: the interface name. + + Returns: + A name unique to the namespace of this object. By defualt the assumption + is the ifname is namespace unique. + """ + return ifname + + def register_interface(self, ifname): + if ifname not in self._intf_addrs: + self._intf_addrs[ifname] = [None, None] + + def register_network(self, netname, ifname): + if netname in self.net_intfs: + assert self.net_intfs[netname] == ifname + else: + self.net_intfs[netname] = ifname + + def get_linux_tc_args(self, ifname, config): + """Get interface constraints (jitter, delay, rate) for linux TC. + + The keys and their values are as follows: + + delay (int): number of microseconds + jitter (int): number of microseconds + jitter-correlation (float): % correlation to previous (default 10%) + loss (float): % of loss + loss-correlation (float): % correlation to previous (default 0%) + rate (int or str): bits per second, string allows for use of + {KMGTKiMiGiTi} prefixes "i" means K == 1024 otherwise K == 1000 + """ + del ifname # unused + + netem_args = "" + + def get_number(c, v, d=None): + if v not in c or c[v] is None: + return d + return convert_number(c[v]) + + delay = get_number(config, "delay") + if delay is not None: + netem_args += f" delay {delay}usec" + + jitter = get_number(config, "jitter") + if jitter is not None: + if not delay: + raise ValueError("jitter but no delay specified") + jitter_correlation = get_number(config, "jitter-correlation", 10) + netem_args += f" {jitter}usec {jitter_correlation}%" + + loss = get_number(config, "loss") + if loss is not None: + loss_correlation = get_number(config, "loss-correlation", 0) + if loss_correlation: + netem_args += f" loss {loss}% {loss_correlation}%" + else: + netem_args += f" loss {loss}%" + + if (o_rate := config.get("rate")) is None: + return netem_args, "" + + # + # This comment is not correct, but is trying to talk through/learn the + # machinery. + # + # tokens arrive at `rate` into token buffer. + # limit - number of bytes that can be queued waiting for tokens + # -or- + # latency - maximum amount of time a packet may sit in TBF queue + # + # So this just allows receiving faster than rate for latency amount of + # time, before dropping. + # + # latency = sizeofbucket(limit) / rate (peakrate?) + # + # 32kbit + # -------- = latency = 320ms + # 100kbps + # + # -but then- + # burst ([token] buffer) the largest number of instantaneous + # tokens available (i.e, bucket size). + + tbf_args = "" + DEFLIMIT = 1518 * 1 + DEFBURST = 1518 * 2 + try: + tc_rate = o_rate["rate"] + tc_rate = convert_number(tc_rate) + limit = convert_number(o_rate.get("limit", DEFLIMIT)) + burst = convert_number(o_rate.get("burst", DEFBURST)) + except (KeyError, TypeError): + tc_rate = convert_number(o_rate) + limit = convert_number(DEFLIMIT) + burst = convert_number(DEFBURST) + tbf_args += f" rate {tc_rate/1000}kbit" + if delay: + # give an extra 1/10 of buffer space to handle delay + tbf_args += f" limit {limit} burst {burst}" + else: + tbf_args += f" limit {limit} burst {burst}" + + return netem_args, tbf_args + + def set_intf_constraints(self, ifname, **constraints): + """Set interface outbound constraints. + + Set outbound constraints (jitter, delay, rate) for an interface. All arguments + may also be passed as a string and will be converted to numerical format. All + arguments are also optional. If not specified then that existing constraint will + be cleared. + + Args: + ifname: the name of the interface + delay (int): number of microseconds. + jitter (int): number of microseconds. + jitter-correlation (float): Percent correlation to previous (default 10%). + loss (float): Percent of loss. + loss-correlation (float): Percent correlation to previous (default 25%). + rate (int): bits per second, string allows for use of + {KMGTKiMiGiTi} prefixes "i" means K == 1024 otherwise K == 1000. + """ + nsifname = self.get_ns_ifname(ifname) + netem_args, tbf_args = self.get_linux_tc_args(nsifname, constraints) + count = 1 + selector = f"root handle {count}:" + if netem_args: + self.cmd_raises( + f"tc qdisc add dev {nsifname} {selector} netem {netem_args}" + ) + count += 1 + selector = f"parent {count-1}: handle {count}" + # Place rate limit after delay otherwise limit/burst too complex + if tbf_args: + self.cmd_raises(f"tc qdisc add dev {nsifname} {selector} tbf {tbf_args}") + + self.cmd_raises(f"tc qdisc show dev {nsifname}") + + +class LinuxNamespace(Commander, InterfaceMixin): + """A linux Namespace. + + An object that creates and executes commands in a linux namespace + """ + + def __init__( + self, + name, + net=True, + mount=True, + uts=True, + cgroup=False, + ipc=False, + pid=False, + time=False, + user=False, + unshare_inline=False, + set_hostname=True, + private_mounts=None, + **kwargs, + ): + """Create a new linux namespace. + + Args: + name: Internal name for the namespace. + net: Create network namespace. + mount: Create network namespace. + uts: Create UTS (hostname) namespace. + cgroup: Create cgroup namespace. + ipc: Create IPC namespace. + pid: Create PID namespace, also mounts new /proc. + time: Create time namespace. + user: Create user namespace, also keeps capabilities. + set_hostname: Set the hostname to `name`, uts must also be True. + private_mounts: List of strings of the form + "[/external/path:]/internal/path. If no external path is specified a + tmpfs is mounted on the internal path. Any paths specified are first + passed to `mkdir -p`. + unshare_inline: Unshare the process itself rather than using a proxy. + logger: Passed to superclass. + """ + # logging.warning("LinuxNamespace: name %s kwargs %s", name, kwargs) + + super().__init__(name, **kwargs) + + unet = self.unet + + self.logger.debug("%s: creating", self) + + self.cwd = os.path.abspath(os.getcwd()) + + self.nsflags = [] + self.ifnetns = {} + self.uflags = 0 + self.p_ns_fds = None + self.p_ns_fnames = None + self.pid_ns = False + self.init_pid = None + self.unshare_inline = unshare_inline + self.nsenter_fork = True + + # + # Collect the namespaces to unshare + # + if hasattr(self, "proc_path") and self.proc_path: # pylint: disable=no-member + pp = Path(self.proc_path) # pylint: disable=no-member + else: + pp = unet.proc_path if unet else Path("/proc") + pp = pp.joinpath("%P%", "ns") + + flags = "" + uflags = 0 + nslist = [] + nsflags = [] + if cgroup: + nselm = "cgroup" + nslist.append(nselm) + nsflags.append(f"--{nselm}={pp / nselm}") + flags += "C" + uflags |= linux.CLONE_NEWCGROUP + if ipc: + nselm = "ipc" + nslist.append(nselm) + nsflags.append(f"--{nselm}={pp / nselm}") + flags += "i" + uflags |= linux.CLONE_NEWIPC + if mount or pid: + # We need a new mount namespace for pid + nselm = "mnt" + nslist.append(nselm) + nsflags.append(f"--mount={pp / nselm}") + mount = True + flags += "m" + uflags |= linux.CLONE_NEWNS + if net: + nselm = "net" + nslist.append(nselm) + nsflags.append(f"--{nselm}={pp / nselm}") + # if pid: + # os.system(f"touch /tmp/netns-{name}") + # cmd.append(f"--net=/tmp/netns-{name}") + # else: + flags += "n" + uflags |= linux.CLONE_NEWNET + if pid: + self.pid_ns = True + # We look for this b/c the unshare pid will share with /sibn/init + nselm = "pid_for_children" + nslist.append(nselm) + nsflags.append(f"--pid={pp / nselm}") + flags += "p" + uflags |= linux.CLONE_NEWPID + if time: + nselm = "time" + # XXX time_for_children? + nslist.append(nselm) + nsflags.append(f"--{nselm}={pp / nselm}") + flags += "T" + uflags |= linux.CLONE_NEWTIME + if user: + nselm = "user" + nslist.append(nselm) + nsflags.append(f"--{nselm}={pp / nselm}") + flags += "U" + uflags |= linux.CLONE_NEWUSER + if uts: + nselm = "uts" + nslist.append(nselm) + nsflags.append(f"--{nselm}={pp / nselm}") + flags += "u" + uflags |= linux.CLONE_NEWUTS + + assert flags, "LinuxNamespace with no namespaces requested" + + # Should look path up using resources maybe... + mutini_path = get_our_script_path("mutini") + if not mutini_path: + mutini_path = get_our_script_path("mutini.py") + assert mutini_path + cmd = [mutini_path, f"--unshare-flags={flags}", "-v"] + fname = fsafe_name(self.name) + "-mutini.log" + fname = (unet or self).rundir.joinpath(fname) + stdout = open(fname, "w", encoding="utf-8") + stderr = subprocess.STDOUT + + # + # Save the current namespace info to compare against later + # + + if not unet: + nsdict = {x: os.readlink(f"/proc/self/ns/{x}") for x in nslist} + else: + nsdict = { + x: os.readlink(f"{unet.proc_path}/{unet.pid}/ns/{x}") for x in nslist + } + + # + # (A) Basically we need to save the pid of the unshare call for nsenter. + # + # For `unet is not None` (node case) the level this exists at is based on wether + # unet is using a forking nsenter or not. So if unet.nsenter_fork == True then + # we need the child pid of the p.pid (child of pid returned to us), otherwise + # unet.nsenter_fork == False and we just use p.pid as it will be unshare after + # nsenter exec's it. + # + # For the `unet is None` (unet case) the unshare is at the top level or + # non-existent so we always save the returned p.pid. If we are unshare_inline we + # won't have a __pre_cmd but we can save our child_pid to kill later, otherwise + # we set unet.pid to None b/c there's literally nothing to do. + # + # --------------------------------------------------------------------------- + # Breakdown for nested (non-unet) namespace creation, and what PID + # to use for __pre_cmd nsenter use. + # --------------------------------------------------------------------------- + # + # tl;dr + # - for non-inline unshare: Use BBB with pid_for_children, unless none/none + # #then (AAA) returned + # - for inline unshare: use returned pid (AAA) with pid_for_children + # + # All commands use unet.popen to launch the unshare of mutini or cat. + # mutini for PID unshare, otherwise cat. AAA is the returned pid BBB is the + # child of the returned. + # + # Unshare Variant + # --------------- + # + # Here we are running mutini if we are creating new pid namespace workspace, + # cat otherwise. + # + # [PID+PID] pid tree looks like this: + # + # PID NSPID PPID PGID + # uuu - N/A uuu main unet process + # AAA - uuu AAA nsenter (forking, from unet) (in unet namespaces -pid) + # BBB - AAA AAA unshare --fork --kill-child (forking) + # CCC 1 BBB CCC mutini (non-forking since it is pid 1 in new namespace) + # + # Use BBB if we use pid_for_children, CCC for all + # + # [PID+none] For non-pid workspace creation (but unet pid) we use cat and pid + # tree looks like this: + # + # PID PPID PGID + # uuu N/A uuu main unet process + # AAA uuu AAA nsenter (forking) (in unet namespaces -pid) + # BBB AAA AAA unshare -> cat (from unshare non-forking) + # + # Use BBB for all + # + # [none+PID] For pid workspace creation (but NOT unet pid) we use mutini and pid + # tree looks like this: + # + # PID NSPID PPID PGID + # uuu - N/A uuu main unet process + # AAA - uuu AAA nsenter -> unshare --fork --kill-child + # BBB 1 AAA AAA mutini (non-forking since it is pid 1 in new namespace) + # + # Use AAA if we use pid_for_children, BBB for all + # + # [none+none] For non-pid workspace and non-pid unet we use cat and pid tree + # looks like this: + # + # PID PPID PGID + # uuu N/A uuu main unet process + # AAA uuu AAA nsenter -> unshare -> cat + # + # Use AAA for all, there's no BBB + # + # Inline-Unshare Variant + # ---------------------- + # + # For unshare_inline and new PID namespace we have unshared all but our PID + # namespace, but our children end up in the new namespace so the fork popen + # does is good enough. + # + # [PID+PID] pid tree looks like this: + # + # PID NSPID PPID PGID + # uuu - N/A uuu main unet process + # AAA - uuu AAA unshare --fork --kill-child (forking) + # BBB 1 AAA BBB mutini + # + # Use AAA if we use pid_for_children, BBB for all + # + # [PID+none] For non-pid workspace creation (but unet pid) we use cat and pid + # tree looks like this: + # + # PID PPID PGID + # uuu N/A uuu main unet process + # AAA uuu AAA unshare -> cat + # + # Use AAA for all + # + # [none+PID] For pid workspace creation (but NOT unet pid) we use mutini and pid + # tree looks like this: + # + # PID NSPID PPID PGID + # uuu - N/A uuu main unet process + # AAA - uuu AAA unshare --fork --kill-child + # BBB 1 AAA BBB mutini + # + # Use AAA if we use pid_for_children, BBB for all + # + # [none+none] For non-pid workspace and non-pid unet we use cat and pid tree + # looks like this: + # + # PID PPID PGID + # uuu N/A uuu main unet process + # AAA uuu AAA unshare -> cat + # + # Use AAA for all. + # + # + # --------------------------------------------------------------------------- + # Breakdown for unet namespace creation, and what PID to use for __pre_cmd + # --------------------------------------------------------------------------- + # + # tl;dr: save returned PID or nothing. + # - for non-inline unshare: Use AAA with pid_for_children (returned pid) + # - for inline unshare: no __precmd as the fork in popen is enough. + # + # Use commander to launch the unshare mutini/cat (for PID/none + # workspace PID) for non-inline case. AAA is the returned pid BBB is the child + # of the returned. + # + # Unshare Variant + # --------------- + # + # Here we are running mutini if we are creating new pid namespace workspace, + # cat otherwise. + # + # [PID] for unet pid creation pid tree looks like this: + # + # PID NSPID PPID PGID + # uuu - N/A uuu main unet process + # AAA - uuu AAA unshare --fork --kill-child (forking) + # BBB 1 AAA BBB mutini + # + # Use AAA if we use pid_for_children, BBB for all + # + # [none] for unet non-pid, pid tree looks like this: + # + # PID PPID PGID + # uuu N/A uuu main unet process + # AAA uuu AAA unshare -> cat + # + # Use AAA for all + # + # Inline-Unshare Variant + # ----------------------- + # + # For unshare_inline and new PID namespace we have unshared all but our PID + # namespace, but our children end up in the new namespace so the fork in popen + # does is good enough. + # + # [PID] for unet pid creation pid tree looks like this: + # + # PID NSPID PPID PGID + # uuu - N/A uuu main unet process + # AAA 1 uuu AAA mutini + # + # Save p / p.pid, but don't configure any nsenter, uneeded. + # + # Use nothing as the fork when doing a popen is enough to be in all the right + # namepsaces. + # + # [none] for unet non-pid, pid tree looks like this: + # + # PID PPID PGID + # uuu N/A uuu main unet process + # + # Nothing, no __pre_cmd. + # + # + + self.ppid = os.getppid() + self.unshare_inline = unshare_inline + if unshare_inline: + assert unet is None + self.uflags = uflags + # + # Open file descriptors for current namespaces for later resotration. + # + try: + kversion = [int(x) for x in platform.release().split("-")[0].split(".")] + kvok = kversion[0] > 5 or (kversion[0] == 5 and kversion[1] >= 8) + except ValueError: + kvok = False + if ( + not kvok + or sys.version_info[0] < 3 + or (sys.version_info[0] == 3 and sys.version_info[1] < 9) + ): + # get list of namespace file descriptors before we unshare + self.p_ns_fds = [] + self.p_ns_fnames = [] + tmpflags = uflags + for i in range(0, 64): + v = 1 << i + if (tmpflags & v) == 0: + continue + tmpflags &= ~v + if v in linux.namespace_files: + path = os.path.join("/proc/self", linux.namespace_files[v]) + if os.path.exists(path): + self.p_ns_fds.append(os.open(path, 0)) + self.p_ns_fnames.append(f"{path} -> {os.readlink(path)}") + self.logger.debug( + "%s: saving old namespace fd %s (%s)", + self, + self.p_ns_fnames[-1], + self.p_ns_fds[-1], + ) + if not tmpflags: + break + else: + self.p_ns_fds = None + self.p_ns_fnames = None + self.ppid_fd = linux.pidfd_open(self.ppid) + + self.logger.debug( + "%s: unshare to new namespaces %s", + self, + linux.clone_flag_string(uflags), + ) + + linux.unshare(uflags) + + if not pid: + p = None + self.pid = None + self.nsenter_fork = False + else: + # Need to fork to create the PID namespace, but we need to continue + # running from the parent so that things like pytest work. We'll execute + # a mutini process to manage the child init 1 duties. + # + # We (the parent pid) can no longer create threads, due to that being + # restricted by the kernel. See EINVAL in clone(2). + # + p = commander.popen( + [mutini_path, "-v"], + stdin=subprocess.PIPE, + stdout=stdout, + stderr=stderr, + text=True, + # new session/pgid so signals don't propagate + start_new_session=True, + shell=False, + ) + self.pid = p.pid + self.nsenter_fork = False + else: + # Using cat and a stdin PIPE is nice as it will exit when we do. However, + # we also detach it from the pgid so that signals do not propagate to it. + # This is b/c it would exit early (e.g., ^C) then, at least the main munet + # proc which has no other processes like frr daemons running, will take the + # main network namespace with it, which will remove the bridges and the + # veth pair (because the bridge side veth is deleted). + self.logger.debug("%s: creating namespace process: %s", self, cmd) + + # Use the parent unet process if we have one this will cause us to inherit + # the namespaces correctly even in the non-inline case. + parent = self.unet if self.unet else commander + + p = parent.popen( + cmd, + stdin=subprocess.PIPE, + stdout=stdout, + stderr=stderr, + text=True, + start_new_session=not unet, + shell=False, + ) + + # The pid number returned is in the global pid namespace. For unshare_inline + # this can be unfortunate b/c our /proc has been remounted in our new pid + # namespace and won't contain global pid namespace pids. To solve for this + # we get all the pid values for the process below. + + # See (A) above for when we need the child pid. + self.logger.debug("%s: namespace process: %s", self, proc_str(p)) + self.pid = p.pid + if unet and unet.nsenter_fork: + assert not unet.unshare_inline + # Need child pid of p.pid + pgrep = roothost.get_exec_path("pgrep") + # a sing fork was done + child_pid = roothost.cmd_raises([pgrep, "-o", "-P", str(p.pid)]) + self.pid = int(child_pid.strip()) + self.logger.debug("%s: child of namespace process: %s", self, pid) + + self.p = p + + # Let's always have a valid value. + if self.pid is None: + self.pid = our_pid + + # + # Let's find all our pids in the nested PID namespaces + # + if unet: + proc_path = unet.proc_path + else: + proc_path = self.proc_path if hasattr(self, "proc_path") else "/proc" + proc_path = f"{proc_path}/{self.pid}" + + pid_status = open(f"{proc_path}/status", "r", encoding="ascii").read() + m = re.search(r"\nNSpid:((?:\t[0-9]+)+)\n", pid_status) + self.pids = [int(x) for x in m.group(1).strip().split("\t")] + assert self.pids[0] == self.pid + + self.logger.debug("%s: namespace scoped pids: %s", self, self.pids) + + # ----------------------------------------------- + # Now let's wait until unshare completes it's job + # ----------------------------------------------- + timeout = Timeout(30) + if self.pid is not None and self.pid != our_pid: + while (not p or not p.poll()) and not timeout.is_expired(): + # check new namespace values against old (nsdict), unshare + # can actually take a bit to complete. + for fname in tuple(nslist): + # self.pid will be the global pid b/c we didn't unshare_inline + nspath = f"{proc_path}/ns/{fname}" + try: + nsf = os.readlink(nspath) + except OSError as error: + self.logger.debug( + "unswitched: error (ok) checking %s: %s", nspath, error + ) + continue + if nsdict[fname] != nsf: + self.logger.debug( + "switched: original %s current %s", nsdict[fname], nsf + ) + nslist.remove(fname) + elif unshare_inline: + logging.warning( + "unshare_inline not unshared %s == %s", nsdict[fname], nsf + ) + else: + self.logger.debug( + "unswitched: current %s elapsed: %s", nsf, timeout.elapsed() + ) + if not nslist: + self.logger.debug( + "all done waiting for unshare after %s", timeout.elapsed() + ) + break + + elapsed = int(timeout.elapsed()) + if elapsed <= 3: + time_mod.sleep(0.1) + else: + self.logger.info( + "%s: unshare taking more than %ss: %s", self, elapsed, nslist + ) + time_mod.sleep(1) + + if p is not None and p.poll(): + self.logger.error("%s: namespace process failed: %s", self, comm_error(p)) + assert p.poll() is None, "unshare failed" + + # + # Setup the pre-command to enter the target namespace from the running munet + # process using self.pid + # + + if pid: + nsenter_fork = True + elif unet and unet.nsenter_fork: + # if unet created a pid namespace we need to enter it since we aren't + # entering a child pid namespace we created for the node. Otherwise + # we have a /proc remounted under unet, but our process is running in + # the root pid namepsace + nselm = "pid_for_children" + nsflags.append(f"--pid={pp / nselm}") + nsenter_fork = True + else: + # We dont need a fork. + nsflags.append("-F") + nsenter_fork = False + + # Save nsenter values if running from root namespace + # we need this for the unshare_inline case when run externally (e.g., from + # within tmux server). + root_nsflags = [x.replace("%P%", str(self.pid)) for x in nsflags] + self.__root_base_pre_cmd = ["/usr/bin/nsenter", *root_nsflags] + self.__root_pre_cmd = list(self.__root_base_pre_cmd) + + if unshare_inline: + assert unet is None + # We have nothing to do here since our process is now in the correct + # namespaces and children will inherit from us, even the PID namespace will + # be corrent b/c commands are run by first forking. + self.nsenter_fork = False + self.nsflags = [] + self.__base_pre_cmd = [] + else: + # We will use nsenter + self.nsenter_fork = nsenter_fork + self.nsflags = nsflags + self.__base_pre_cmd = list(self.__root_base_pre_cmd) + + self.__pre_cmd = list(self.__base_pre_cmd) + + # Always mark new mount namespaces as recursive private + if mount: + # if self.p is None and not pid: + self.cmd_raises_nsonly("mount --make-rprivate /") + + # We need to remount the procfs for the new PID namespace, since we aren't using + # unshare(1) which does that for us. + if pid and unshare_inline: + assert mount + self.cmd_raises_nsonly("mount -t proc proc /proc") + + # We do not want cmd_status in child classes (e.g., container) for + # the remaining setup calls in this __init__ function. + + if net: + # Remount /sys to pickup any changes in the network, but keep root + # /sys/fs/cgroup. This pattern could be made generic and supported for any + # overlapping mounts + if mount: + tmpmnt = f"/tmp/cgm-{self.pid}" + self.cmd_status_nsonly( + f"mkdir {tmpmnt} && mount --rbind /sys/fs/cgroup {tmpmnt}" + ) + rc = o = e = None + for i in range(0, 10): + rc, o, e = self.cmd_status_nsonly( + "mount -t sysfs sysfs /sys", warn=False + ) + if not rc: + break + self.logger.debug( + "got error mounting new sysfs will retry: %s", + cmd_error(rc, o, e), + ) + time_mod.sleep(1) + else: + raise Exception(cmd_error(rc, o, e)) + + self.cmd_status_nsonly( + f"mount --move {tmpmnt} /sys/fs/cgroup && rmdir {tmpmnt}" + ) + + # Original micronet code + # self.cmd_raises_nsonly("mount -t sysfs sysfs /sys") + # self.cmd_raises_nsonly( + # "mount -o rw,nosuid,nodev,noexec,relatime " + # "-t cgroup2 cgroup /sys/fs/cgroup" + # ) + + # Set the hostname to the namespace name + if uts and set_hostname: + self.cmd_status_nsonly("hostname " + self.name) + nroot = subprocess.check_output("hostname") + if unshare_inline or (unet and unet.unshare_inline): + assert ( + root_hostname != nroot + ), f'hostname unchanged from "{nroot}" wanted "{self.name}"' + else: + # Assert that we didn't just change the host hostname + assert ( + root_hostname == nroot + ), f'root hostname "{root_hostname}" changed to "{nroot}"!' + + if private_mounts: + if isinstance(private_mounts, str): + private_mounts = [private_mounts] + for m in private_mounts: + s = m.split(":", 1) + if len(s) == 1: + self.tmpfs_mount(s[0]) + else: + self.bind_mount(s[0], s[1]) + + # this will fail if running inside the namespace with PID + if pid: + o = self.cmd_status_nsonly("ls -l /proc/1/ns") + else: + o = self.cmd_nostatus_nsonly(cmd=shlex.split("/usr/bin/ls -l /proc/self")) + o = self.cmd_nostatus_nsonly(cmd=shlex.split("ls -l /proc/self/ns")) + + self.logger.debug("namespaces:\n %s", o) + + # will cache the path, which is important in delete to avoid running a shell + # which can hang during cleanup + self.ip_path = get_exec_path_host("ip") + if net: + self.cmd_status_nsonly([self.ip_path, "link", "set", "lo", "up"]) + + self.logger.info("%s: created", self) + + def _get_pre_cmd(self, use_str, use_pty, ns_only=False, root_level=False, **kwargs): + """Get the pre-user-command values. + + The values returned here should be what is required to cause the user's command + to execute in the correct context (e.g., namespace, container, sshremote). + """ + del kwargs + del ns_only + del use_pty + pre_cmd = self.__root_pre_cmd if root_level else self.__pre_cmd + return shlex.join(pre_cmd) if use_str else list(pre_cmd) + + def tmpfs_mount(self, inner): + self.logger.debug("Mounting tmpfs on %s", inner) + self.cmd_raises("mkdir -p " + inner) + self.cmd_raises("mount -n -t tmpfs tmpfs " + inner) + + def bind_mount(self, outer, inner): + self.logger.debug("Bind mounting %s on %s", outer, inner) + if commander.test("-f", outer): + self.cmd_raises(f"mkdir -p {os.path.dirname(inner)} && touch {inner}") + else: + if not commander.test("-e", outer): + commander.cmd_raises_nsonly(f"mkdir -p {outer}") + self.cmd_raises(f"mkdir -p {inner}") + self.cmd_raises("mount --rbind {} {} ".format(outer, inner)) + + def add_netns(self, ns): + self.logger.debug("Adding network namespace %s", ns) + + if os.path.exists("/run/netns/{}".format(ns)): + self.logger.warning("%s: Removing existing nsspace %s", self, ns) + try: + self.delete_netns(ns) + except Exception as ex: + self.logger.warning( + "%s: Couldn't remove existing nsspace %s: %s", + self, + ns, + str(ex), + exc_info=True, + ) + self.cmd_raises_nsonly([self.ip_path, "netns", "add", ns]) + + def delete_netns(self, ns): + self.logger.debug("Deleting network namespace %s", ns) + self.cmd_raises_nsonly([self.ip_path, "netns", "delete", ns]) + + def set_intf_netns(self, intf, ns, up=False): + # In case a user hard-codes 1 thinking it "resets" + ns = str(ns) + if ns == "1": + ns = str(self.pid) + + self.logger.debug("Moving interface %s to namespace %s", intf, ns) + + cmd = [self.ip_path, "link", "set", intf, "netns", ns] + if up: + cmd.append("up") + self.intf_ip_cmd(intf, cmd) + if ns == str(self.pid): + # If we are returning then remove from dict + if intf in self.ifnetns: + del self.ifnetns[intf] + else: + self.ifnetns[intf] = ns + + def reset_intf_netns(self, intf): + self.logger.debug("Moving interface %s to default namespace", intf) + self.set_intf_netns(intf, str(self.pid)) + + def intf_ip_cmd(self, intf, cmd): + """Run an ip command, considering an interface's possible namespace.""" + if intf in self.ifnetns: + if isinstance(cmd, list): + assert cmd[0].endswith("ip") + cmd[1:1] = ["-n", self.ifnetns[intf]] + else: + assert cmd.startswith("ip ") + cmd = "ip -n " + self.ifnetns[intf] + cmd[2:] + self.cmd_raises_nsonly(cmd) + + def intf_tc_cmd(self, intf, cmd): + """Run a tc command, considering an interface's possible namespace.""" + if intf in self.ifnetns: + if isinstance(cmd, list): + assert cmd[0].endswith("tc") + cmd[1:1] = ["-n", self.ifnetns[intf]] + else: + assert cmd.startswith("tc ") + cmd = "tc -n " + self.ifnetns[intf] + cmd[2:] + self.cmd_raises_nsonly(cmd) + + def set_ns_cwd(self, cwd: Union[str, Path]): + """Common code for changing pre_cmd and pre_nscmd.""" + self.logger.debug("%s: new CWD %s", self, cwd) + self.__root_pre_cmd = self.__root_base_pre_cmd + ["--wd=" + str(cwd)] + if self.__pre_cmd: + self.__pre_cmd = self.__base_pre_cmd + ["--wd=" + str(cwd)] + elif self.unshare_inline: + os.chdir(cwd) + + async def _async_delete(self): + if type(self) == LinuxNamespace: # pylint: disable=C0123 + self.logger.info("%s: deleting", self) + else: + self.logger.debug("%s: LinuxNamespace sub-class deleting", self) + + # Signal pid namespace proc to exit + if ( + (self.p is None or self.p.pid != self.pid) + and self.pid + and self.pid != our_pid + ): + self.logger.debug( + "cleanup pid on separate pid %s from proc pid %s", + self.pid, + self.p.pid if self.p else None, + ) + await self.cleanup_pid(self.pid) + + if self.p is not None: + self.logger.debug("cleanup proc pid %s", self.p.pid) + await self.async_cleanup_proc(self.p) + + # return to the previous namespace, need to do this in case anothe munet + # is being created, especially when it plans to inherit the parent's (host) + # namespace. + if self.uflags: + logging.info("restoring from inline unshare: cwd: %s", os.getcwd()) + # This only works in linux>=5.8 + if self.p_ns_fds is None: + self.logger.debug( + "%s: restoring namespaces %s", + self, + linux.clone_flag_string(self.uflags), + ) + # fd = linux.pidfd_open(self.ppid) + fd = self.ppid_fd + retry = 3 + for i in range(0, retry): + try: + linux.setns(fd, self.uflags) + except OSError as error: + self.logger.warning( + "%s: could not reset to old namespace fd %s: %s", + self, + fd, + error, + ) + if i == retry - 1: + raise + time_mod.sleep(1) + os.close(fd) + else: + while self.p_ns_fds: + fd = self.p_ns_fds.pop() + fname = self.p_ns_fnames.pop() + self.logger.debug( + "%s: restoring namespace from fd %s (%s)", self, fname, fd + ) + retry = 3 + for i in range(0, retry): + try: + linux.setns(fd, 0) + break + except OSError as error: + self.logger.warning( + "%s: could not reset to old namespace fd %s (%s): %s", + self, + fname, + fd, + error, + ) + if i == retry - 1: + raise + time_mod.sleep(1) + os.close(fd) + self.p_ns_fds = None + self.p_ns_fnames = None + logging.info("restored from unshare: cwd: %s", os.getcwd()) + + self.__root_base_pre_cmd = ["/bin/false"] + self.__base_pre_cmd = ["/bin/false"] + self.__root_pre_cmd = ["/bin/false"] + self.__pre_cmd = ["/bin/false"] + + await super()._async_delete() + + +class SharedNamespace(Commander): + """Share another namespace. + + An object that executes commands in an existing pid's linux namespace + """ + + def __init__(self, name, pid=None, nsflags=None, **kwargs): + """Share a linux namespace. + + Args: + name: Internal name for the namespace. + pid: PID of the process to share with. + nsflags: nsenter flags to pass to inherit namespaces from + """ + super().__init__(name, **kwargs) + + self.logger.debug("%s: Creating", self) + + self.cwd = os.path.abspath(os.getcwd()) + self.pid = pid if pid is not None else our_pid + + nsflags = (x.replace("%P%", str(self.pid)) for x in nsflags) if nsflags else [] + self.__base_pre_cmd = ["/usr/bin/nsenter", *nsflags] if nsflags else [] + self.__pre_cmd = self.__base_pre_cmd + self.ip_path = self.get_exec_path("ip") + + def _get_pre_cmd(self, use_str, use_pty, ns_only=False, root_level=False, **kwargs): + """Get the pre-user-command values. + + The values returned here should be what is required to cause the user's command + to execute in the correct context (e.g., namespace, container, sshremote). + """ + del kwargs + del ns_only + del use_pty + assert not root_level + return shlex.join(self.__pre_cmd) if use_str else list(self.__pre_cmd) + + def set_ns_cwd(self, cwd: Union[str, Path]): + """Common code for changing pre_cmd and pre_nscmd.""" + self.logger.debug("%s: new CWD %s", self, cwd) + self.__pre_cmd = self.__base_pre_cmd + ["--wd=" + str(cwd)] + + +class Bridge(SharedNamespace, InterfaceMixin): + """A linux bridge.""" + + next_ord = 1 + + @classmethod + def _get_next_id(cls): + # Do not use `cls` here b/c that makes the variable class specific + n = Bridge.next_ord + Bridge.next_ord = n + 1 + return n + + def __init__(self, name=None, mtu=None, unet=None, **kwargs): + """Create a linux Bridge.""" + self.id = self._get_next_id() + if not name: + name = "br{}".format(self.id) + + unet_pid = our_pid if unet.pid is None else unet.pid + + super().__init__(name, pid=unet_pid, nsflags=unet.nsflags, unet=unet, **kwargs) + + self.set_intf_basename(self.name + "-e") + + self.mtu = mtu + + self.logger.debug("Bridge: Creating") + + assert len(self.name) <= 16 # Make sure fits in IFNAMSIZE + self.cmd_raises(f"ip link delete {name} || true") + self.cmd_raises(f"ip link add {name} type bridge") + if self.mtu: + self.cmd_raises(f"ip link set {name} mtu {self.mtu}") + self.cmd_raises(f"ip link set {name} up") + + self.logger.debug("%s: Created, Running", self) + + def get_ifname(self, netname): + return self.net_intfs[netname] if netname in self.net_intfs else None + + async def _async_delete(self): + """Stop the bridge (i.e., delete the linux resources).""" + if type(self) == Bridge: # pylint: disable=C0123 + self.logger.info("%s: deleting", self) + else: + self.logger.debug("%s: Bridge sub-class deleting", self) + + rc, o, e = await self.async_cmd_status( + [self.ip_path, "link", "show", self.name], + stdin=subprocess.DEVNULL, + start_new_session=True, + warn=False, + ) + if not rc: + rc, o, e = await self.async_cmd_status( + [self.ip_path, "link", "delete", self.name], + stdin=subprocess.DEVNULL, + start_new_session=True, + warn=False, + ) + if rc: + self.logger.error( + "%s: error deleting bridge %s: %s", + self, + self.name, + cmd_error(rc, o, e), + ) + await super()._async_delete() + + +class BaseMunet(LinuxNamespace): + """Munet.""" + + def __init__(self, name="munet", isolated=True, pid=True, rundir=None, **kwargs): + """Create a Munet.""" + # logging.warning("BaseMunet: %s", name) + + self.hosts = {} + self.switches = {} + self.links = {} + self.macs = {} + self.rmacs = {} + self.isolated = isolated + + self.cli_server = None + self.cli_sockpath = None + self.cli_histfile = None + self.cli_in_window_cmds = {} + self.cli_run_cmds = {} + + # + # We need a directory for various files + # + if not rundir: + rundir = "/tmp/munet" + self.rundir = Path(rundir) + + # + # Always having a global /proc is required to keep things from exploding + # complexity with nested new pid namespaces.. + # + if pid: + self.proc_path = Path(tempfile.mkdtemp(suffix="-proc", prefix="mu-")) + logging.debug("%s: mounting /proc on proc_path %s", name, self.proc_path) + linux.mount("proc", str(self.proc_path), "proc") + else: + self.proc_path = Path("/proc") + + # + # Now create a root level commander that works regardless of whether we inline + # unshare or not. Save it in the global variable as well + # + + if not self.isolated: + self.rootcmd = commander + else: + # XXX user + nsflags = ( + f"--pid={self.proc_path / '1/ns/pid_for_children'}", + f"--mount={self.proc_path / '1/ns/mnt'}", + f"--net={self.proc_path / '1/ns/net'}", + f"--uts={self.proc_path / '1/ns/uts'}", + # f"--ipc={self.proc_path / '1/ns/ipc'}", + # f"--time={self.proc_path / '1/ns/time'}", + # f"--cgroup={self.proc_path / '1/ns/cgroup'}", + ) + self.rootcmd = SharedNamespace("root", pid=1, nsflags=nsflags) + global roothost # pylint: disable=global-statement + + roothost = self.rootcmd + + super().__init__( + name, mount=True, net=isolated, uts=isolated, pid=pid, unet=None, **kwargs + ) + + # This allows us to cleanup any leftover running munet's + if "MUNET_PID" in os.environ: + if os.environ["MUNET_PID"] != str(our_pid): + logging.error( + "Found env MUNET_PID != our pid %s, instead its %s, changing", + our_pid, + os.environ["MUNET_PID"], + ) + os.environ["MUNET_PID"] = str(our_pid) + + # this is for testing purposes do not use + if not BaseMunet.g_unet: + BaseMunet.g_unet = self + + self.logger.debug("%s: Creating", self) + + def __getitem__(self, key): + if key in self.switches: + return self.switches[key] + return self.hosts[key] + + def add_host(self, name, cls=LinuxNamespace, **kwargs): + """Add a host to munet.""" + self.logger.debug("%s: add_host %s(%s)", self, cls.__name__, name) + + self.hosts[name] = cls(name, unet=self, **kwargs) + + # Create a new mounted FS for tracking nested network namespaces creatd by the + # user with `ip netns add` + + # XXX why is this failing with podman??? + # self.hosts[name].tmpfs_mount("/run/netns") + + return self.hosts[name] + + def add_link(self, node1, node2, if1, if2, mtu=None, **intf_constraints): + """Add a link between switch and node or 2 nodes. + + If constraints are given they are applied to each endpoint. See + `InterfaceMixin::set_intf_constraints()` for more info. + """ + isp2p = False + + try: + name1 = node1.name + except AttributeError: + if node1 in self.switches: + node1 = self.switches[node1] + else: + node1 = self.hosts[node1] + name1 = node1.name + + try: + name2 = node2.name + except AttributeError: + if node2 in self.switches: + node2 = self.switches[node2] + else: + node2 = self.hosts[node2] + name2 = node2.name + + if name1 in self.switches: + assert name2 in self.hosts + elif name2 in self.switches: + assert name1 in self.hosts + name1, name2 = name2, name1 + if1, if2 = if2, if1 + else: + # p2p link + assert name1 in self.hosts + assert name2 in self.hosts + isp2p = True + + lname = "{}:{}-{}:{}".format(name1, if1, name2, if2) + self.logger.debug("%s: add_link %s%s", self, lname, " p2p" if isp2p else "") + self.links[lname] = (name1, if1, name2, if2) + + # And create the veth now. + if isp2p: + lhost, rhost = self.hosts[name1], self.hosts[name2] + lifname = "i1{:x}".format(lhost.pid) + + # Done at root level + nsif1 = lhost.get_ns_ifname(if1) + nsif2 = rhost.get_ns_ifname(if2) + + # Use pids[-1] to get the unet scoped pid for hosts + self.cmd_raises_nsonly( + f"ip link add {lifname} type veth peer name {nsif2}" + f" netns {rhost.pids[-1]}" + ) + self.cmd_raises_nsonly(f"ip link set {lifname} netns {lhost.pids[-1]}") + + lhost.cmd_raises_nsonly("ip link set {} name {}".format(lifname, nsif1)) + if mtu: + lhost.cmd_raises_nsonly("ip link set {} mtu {}".format(nsif1, mtu)) + lhost.cmd_raises_nsonly("ip link set {} up".format(nsif1)) + lhost.register_interface(if1) + + if mtu: + rhost.cmd_raises_nsonly("ip link set {} mtu {}".format(nsif2, mtu)) + rhost.cmd_raises_nsonly("ip link set {} up".format(nsif2)) + rhost.register_interface(if2) + else: + switch = self.switches[name1] + rhost = self.hosts[name2] + + nsif1 = switch.get_ns_ifname(if1) + nsif2 = rhost.get_ns_ifname(if2) + + if mtu is None: + mtu = switch.mtu + + if len(nsif1) > 16: + self.logger.error('"%s" len %s > 16', nsif1, len(nsif1)) + elif len(nsif2) > 16: + self.logger.error('"%s" len %s > 16', nsif2, len(nsif2)) + assert len(nsif1) <= 16 and len(nsif2) <= 16 # Make sure fits in IFNAMSIZE + + self.logger.debug("%s: Creating veth pair for link %s", self, lname) + + # Use pids[-1] to get the unet scoped pid for hosts + # switch is already in our namespace so nothing to convert. + self.cmd_raises_nsonly( + f"ip link add {nsif1} type veth peer name {nsif2}" + f" netns {rhost.pids[-1]}" + ) + + if mtu: + # if switch.mtu: + # # the switch interface should match the switch config + # switch.cmd_raises_nsonly( + # "ip link set {} mtu {}".format(if1, switch.mtu) + # ) + switch.cmd_raises_nsonly("ip link set {} mtu {}".format(nsif1, mtu)) + rhost.cmd_raises_nsonly("ip link set {} mtu {}".format(nsif2, mtu)) + + switch.register_interface(if1) + rhost.register_interface(if2) + rhost.register_network(switch.name, if2) + + switch.cmd_raises_nsonly(f"ip link set {nsif1} master {switch.name}") + + switch.cmd_raises_nsonly(f"ip link set {nsif1} up") + rhost.cmd_raises_nsonly(f"ip link set {nsif2} up") + + # Cache the MAC values, and reverse mapping + self.get_mac(name1, nsif1) + self.get_mac(name2, nsif2) + + # Setup interface constraints if provided + if intf_constraints: + node1.set_intf_constraints(if1, **intf_constraints) + node2.set_intf_constraints(if2, **intf_constraints) + + def add_switch(self, name, cls=Bridge, **kwargs): + """Add a switch to munet.""" + self.logger.debug("%s: add_switch %s(%s)", self, cls.__name__, name) + self.switches[name] = cls(name, unet=self, **kwargs) + return self.switches[name] + + def get_mac(self, name, ifname): + if name in self.hosts: + dev = self.hosts[name] + else: + dev = self.switches[name] + + nsifname = self.get_ns_ifname(ifname) + + if (name, ifname) not in self.macs: + _, output, _ = dev.cmd_status_nsonly("ip -o link show " + nsifname) + m = re.match(".*link/(loopback|ether) ([0-9a-fA-F:]+) .*", output) + mac = m.group(2) + self.macs[(name, ifname)] = mac + self.rmacs[mac] = (name, ifname) + + return self.macs[(name, ifname)] + + async def _delete_link(self, lname): + rname, rif = self.links[lname][2:4] + host = self.hosts[rname] + nsrif = host.get_ns_ifname(rif) + + self.logger.debug("%s: Deleting veth pair for link %s", self, lname) + rc, o, e = await host.async_cmd_status_nsonly( + [self.ip_path, "link", "delete", nsrif], + stdin=subprocess.DEVNULL, + start_new_session=True, + warn=False, + ) + if rc: + self.logger.error("Err del veth pair %s: %s", lname, cmd_error(rc, o, e)) + + async def _delete_links(self): + # for x in self.links: + # await self._delete_link(x) + return await asyncio.gather(*[self._delete_link(x) for x in self.links]) + + async def _async_delete(self): + """Delete the munet topology.""" + # logger = self.logger if False else logging + logger = self.logger + if type(self) == BaseMunet: # pylint: disable=C0123 + logger.info("%s: deleting.", self) + else: + logger.debug("%s: BaseMunet sub-class deleting.", self) + + logger.debug("Deleting links") + try: + await self._delete_links() + except Exception as error: + logger.error("%s: error deleting links: %s", self, error, exc_info=True) + + logger.debug("Deleting hosts and bridges") + try: + # Delete hosts and switches, wait for them all to complete + # even if there is an exception. + htask = [x.async_delete() for x in self.hosts.values()] + stask = [x.async_delete() for x in self.switches.values()] + await asyncio.gather(*htask, *stask, return_exceptions=True) + except Exception as error: + logger.error( + "%s: error deleting hosts and switches: %s", self, error, exc_info=True + ) + + self.links = {} + self.hosts = {} + self.switches = {} + + try: + if self.cli_server: + self.cli_server.cancel() + self.cli_server = None + if self.cli_sockpath: + await self.async_cmd_status( + "rm -rf " + os.path.dirname(self.cli_sockpath) + ) + self.cli_sockpath = None + except Exception as error: + logger.error( + "%s: error cli server or sockpaths: %s", self, error, exc_info=True + ) + + try: + if self.cli_histfile: + readline.write_history_file(self.cli_histfile) + self.cli_histfile = None + except Exception as error: + logger.error( + "%s: error saving history file: %s", self, error, exc_info=True + ) + + # XXX for some reason setns during the delete is changing our dir to /. + cwd = os.getcwd() + + try: + await super()._async_delete() + except Exception as error: + logger.error( + "%s: error deleting parent classes: %s", self, error, exc_info=True + ) + os.chdir(cwd) + + try: + if self.proc_path and str(self.proc_path) != "/proc": + logger.debug("%s: umount, remove proc_path %s", self, self.proc_path) + linux.umount(str(self.proc_path), 0) + os.rmdir(self.proc_path) + except Exception as error: + logger.warning( + "%s: error umount and removing proc_path %s: %s", + self, + self.proc_path, + error, + exc_info=True, + ) + try: + linux.umount(str(self.proc_path), linux.MNT_DETACH) + except Exception as error2: + logger.error( + "%s: error umount with detach proc_path %s: %s", + self, + self.proc_path, + error2, + exc_info=True, + ) + + if BaseMunet.g_unet == self: + BaseMunet.g_unet = None + + +BaseMunet.g_unet = None + +if True: # pylint: disable=using-constant-test + + class ShellWrapper: + """A Read-Execute-Print-Loop (REPL) interface. + + A newline or prompt changing command should be sent to the + spawned child prior to creation as the `prompt` will be `expect`ed + """ + + def __init__( + self, + spawn, + prompt, + continuation_prompt=None, + extra_init_cmd=None, + will_echo=False, + escape_ansi=False, + ): + self.echo = will_echo + self.escape = ( + re.compile(r"(\x9B|\x1B\[)[0-?]*[ -\/]*[@-~]") if escape_ansi else None + ) + + logging.debug( + 'ShellWraper: XXX prompt "%s" will_echo %s child.echo %s', + prompt, + will_echo, + spawn.echo, + ) + + self.child = spawn + if self.child.echo: + logging.info("Setting child to echo") + self.child.setecho(False) + self.child.waitnoecho() + assert not self.child.echo + + self.prompt = prompt + self.cont_prompt = continuation_prompt + + # Use expect_exact if we can as it should be faster + self.expects = [prompt] + if re.escape(prompt) == prompt and hasattr(self.child, "expect_exact"): + self._expectf = self.child.expect_exact + else: + self._expectf = self.child.expect + if continuation_prompt: + self.expects.append(continuation_prompt) + if re.escape(continuation_prompt) != continuation_prompt: + self._expectf = self.child.expect + + if extra_init_cmd: + self.expect_prompt() + self.child.sendline(extra_init_cmd) + self.expect_prompt() + + def expect_prompt(self, timeout=-1): + return self._expectf(self.expects, timeout=timeout) + + def run_command(self, command, timeout=-1): + """Pexpect REPLWrapper compatible run_command. + + This will split `command` into lines and feed each one to the shell. + + Args: + command: string of commands separated by newlines, a trailing + newline will cause and empty line to be sent. + timeout: pexpect timeout value. + """ + lines = command.splitlines() + if command[-1] == "\n": + lines.append("") + output = "" + index = 0 + for line in lines: + self.child.sendline(line) + index = self.expect_prompt(timeout=timeout) + output += self.child.before + + if index: + if hasattr(self.child, "kill"): + self.child.kill(signal.SIGINT) + else: + self.child.send("\x03") + self.expect_prompt(timeout=30 if self.child.timeout is None else -1) + raise ValueError("Continuation prompt found at end of commands") + + if self.escape: + output = self.escape.sub("", output) + + return output + + def cmd_nostatus(self, cmd, timeout=-1): + r"""Execute a shell command. + + Returns: + (strip/cleaned \r) output + """ + output = self.run_command(cmd, timeout) + output = output.replace("\r\n", "\n") + if self.echo: + # remove the command + idx = output.find(cmd) + if idx == -1: + logging.warning( + "Didn't find command ('%s') in expected output ('%s')", + cmd, + output, + ) + else: + # Remove up to and including the command from the output stream + output = output[idx + len(cmd) :] + + return output.replace("\r", "").strip() + + def cmd_status(self, cmd, timeout=-1): + r"""Execute a shell command. + + Returns: + status and (strip/cleaned \r) output + """ + # Run the command getting the output + output = self.cmd_nostatus(cmd, timeout) + + # Now get the status + scmd = "echo $?" + rcstr = self.run_command(scmd) + rcstr = rcstr.replace("\r\n", "\n") + if self.echo: + # remove the command + idx = rcstr.find(scmd) + if idx == -1: + if self.echo: + logging.warning( + "Didn't find status ('%s') in expected output ('%s')", + scmd, + rcstr, + ) + try: + rc = int(rcstr) + except Exception: + rc = 255 + else: + rcstr = rcstr[idx + len(scmd) :].strip() + try: + rc = int(rcstr) + except ValueError as error: + logging.error( + "%s: error with expected status output: %s: %s", + self, + error, + rcstr, + exc_info=True, + ) + rc = 255 + return rc, output + + def cmd_raises(self, cmd, timeout=-1): + r"""Execute a shell command. + + Returns: + (strip/cleaned \r) ouptut + + Raises: + CalledProcessError: on non-zero exit status + """ + rc, output = self.cmd_status(cmd, timeout) + if rc: + raise CalledProcessError(rc, cmd, output) + return output + + +# --------------------------- +# Root level utility function +# --------------------------- + + +def get_exec_path(binary): + return commander.get_exec_path(binary) + + +def get_exec_path_host(binary): + return commander.get_exec_path(binary) + + +def get_our_script_path(script): + # would be nice to find this w/o using a path lookup + sdir = os.path.dirname(os.path.abspath(__file__)) + spath = os.path.join(sdir, script) + if os.path.exists(spath): + return spath + return get_exec_path(script) + + +commander = Commander("munet") +roothost = None diff --git a/tests/topotests/munet/cleanup.py b/tests/topotests/munet/cleanup.py new file mode 100644 index 0000000000..c641cda685 --- /dev/null +++ b/tests/topotests/munet/cleanup.py @@ -0,0 +1,114 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# September 30 2021, Christian Hopps <chopps@labn.net> +# +# Copyright 2021, LabN Consulting, L.L.C. +# +"""Provides functionality to cleanup processes on posix systems.""" +import glob +import logging +import os +import signal + + +def get_pids_with_env(has_var, has_val=None): + result = {} + for pidenv in glob.iglob("/proc/*/environ"): + pid = pidenv.split("/")[2] + try: + with open(pidenv, "rb") as rfb: + envlist = [ + x.decode("utf-8").split("=", 1) for x in rfb.read().split(b"\0") + ] + envlist = [[x[0], ""] if len(x) == 1 else x for x in envlist] + envdict = dict(envlist) + if has_var not in envdict: + continue + if has_val is None: + result[pid] = envdict + elif envdict[has_var] == str(has_val): + result[pid] = envdict + except Exception: + # E.g., process exited and files are gone + pass + return result + + +def _kill_piddict(pids_by_upid, sig): + ourpid = str(os.getpid()) + for upid, pids in pids_by_upid: + logging.info("Sending %s to (%s) of munet pid %s", sig, ", ".join(pids), upid) + for pid in pids: + try: + if pid != ourpid: + cmdline = open(f"/proc/{pid}/cmdline", "r", encoding="ascii").read() + cmdline = cmdline.replace("\x00", " ") + logging.info("killing proc %s (%s)", pid, cmdline) + os.kill(int(pid), sig) + except Exception: + pass + + +def _get_our_pids(): + ourpid = str(os.getpid()) + piddict = get_pids_with_env("MUNET_PID", ourpid) + pids = [x for x in piddict if x != ourpid] + if pids: + return {ourpid: pids} + return {} + + +def _get_other_pids(): + piddict = get_pids_with_env("MUNET_PID") + unet_pids = {d["MUNET_PID"] for d in piddict.values()} + pids_by_upid = {p: set() for p in unet_pids} + for pid, envdict in piddict.items(): + unet_pid = envdict["MUNET_PID"] + pids_by_upid[unet_pid].add(pid) + # Filter out any child pid sets whos munet pid is still running + return {x: y for x, y in pids_by_upid.items() if x not in y} + + +def _get_pids_by_upid(ours): + if ours: + return _get_our_pids() + return _get_other_pids() + + +def _cleanup_pids(ours): + pids_by_upid = _get_pids_by_upid(ours).items() + if not pids_by_upid: + return + + t = "current" if ours else "previous" + logging.info("Reaping %s munet processes", t) + + # _kill_piddict(pids_by_upid, signal.SIGTERM) + + # # Give them 5 second to exit cleanly + # logging.info("Waiting up to 5s to allow for clean exit of abandon'd pids") + # for _ in range(0, 5): + # pids_by_upid = _get_pids_by_upid(ours).items() + # if not pids_by_upid: + # return + # time.sleep(1) + + pids_by_upid = _get_pids_by_upid(ours).items() + _kill_piddict(pids_by_upid, signal.SIGKILL) + + +def cleanup_current(): + """Attempt to cleanup preview runs. + + Currently this only scans for old processes. + """ + _cleanup_pids(True) + + +def cleanup_previous(): + """Attempt to cleanup preview runs. + + Currently this only scans for old processes. + """ + _cleanup_pids(False) diff --git a/tests/topotests/munet/cli.py b/tests/topotests/munet/cli.py new file mode 100644 index 0000000000..45db3990c7 --- /dev/null +++ b/tests/topotests/munet/cli.py @@ -0,0 +1,939 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# July 24 2021, Christian Hopps <chopps@labn.net> +# +# Copyright 2021, LabN Consulting, L.L.C. +# +"""A module that implements a CLI.""" +import argparse +import asyncio +import functools +import logging +import multiprocessing +import os +import pty +import re +import readline +import select +import shlex +import socket +import subprocess +import sys +import tempfile +import termios +import tty + +from . import linux +from .config import list_to_dict_with_key + + +ENDMARKER = b"\x00END\x00" + +logger = logging.getLogger(__name__) + + +def lineiter(sock): + s = "" + while True: + sb = sock.recv(256) + if not sb: + return + + s += sb.decode("utf-8") + i = s.find("\n") + if i != -1: + yield s[:i] + s = s[i + 1 :] + + +# Would be nice to convert to async, but really not needed as used +def spawn(unet, host, cmd, iow, ns_only): + if sys.stdin.isatty(): + old_tty = termios.tcgetattr(sys.stdin) + tty.setraw(sys.stdin.fileno()) + + try: + master_fd, slave_fd = pty.openpty() + + ns = unet.hosts[host] if host and host != unet else unet + popenf = ns.popen_nsonly if ns_only else ns.popen + + # use os.setsid() make it run in a new process group, or bash job + # control will not be enabled + p = popenf( + cmd, + # _common_prologue, later in call chain, only does this for use_pty == False + preexec_fn=os.setsid, + stdin=slave_fd, + stdout=slave_fd, + stderr=slave_fd, + universal_newlines=True, + use_pty=True, + # XXX this is actually implementing "run on host" for real + # skip_pre_cmd=ns_only, + ) + iow.write("\r") + iow.flush() + + while p.poll() is None: + r, _, _ = select.select([sys.stdin, master_fd], [], [], 0.25) + if sys.stdin in r: + d = os.read(sys.stdin.fileno(), 10240) + os.write(master_fd, d) + elif master_fd in r: + o = os.read(master_fd, 10240) + if o: + iow.write(o.decode("utf-8")) + iow.flush() + finally: + # restore tty settings back + if sys.stdin.isatty(): + termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_tty) + + +def is_host_regex(restr): + return len(restr) > 2 and restr[0] == "/" and restr[-1] == "/" + + +def get_host_regex(restr): + if len(restr) < 3 or restr[0] != "/" or restr[-1] != "/": + return None + return re.compile(restr[1:-1]) + + +def host_in(restr, names): + """Determine if matcher is a regex that matches one of names.""" + if not (regexp := get_host_regex(restr)): + return restr in names + for name in names: + if regexp.fullmatch(name): + return True + return False + + +def expand_host(restr, names): + """Expand name or regexp into list of hosts.""" + hosts = [] + regexp = get_host_regex(restr) + if not regexp: + assert restr in names + hosts.append(restr) + else: + for name in names: + if regexp.fullmatch(name): + hosts.append(name) + return sorted(hosts) + + +def expand_hosts(restrs, names): + """Expand list of host names or regex into list of hosts.""" + hosts = [] + for restr in restrs: + hosts += expand_host(restr, names) + return sorted(hosts) + + +def host_cmd_split(unet, line, toplevel): + all_hosts = set(unet.hosts) + csplit = line.split() + i = 0 + banner = False + for i, e in enumerate(csplit): + if is_re := is_host_regex(e): + banner = True + if not host_in(e, all_hosts): + if not is_re: + break + else: + i += 1 + + if i == 0 and csplit and csplit[0] == "*": + hosts = sorted(all_hosts) + csplit = csplit[1:] + banner = True + elif i == 0 and csplit and csplit[0] == ".": + hosts = [unet] + csplit = csplit[1:] + else: + hosts = expand_hosts(csplit[:i], all_hosts) + csplit = csplit[i:] + + if not hosts and not csplit[:i]: + if toplevel: + hosts = [unet] + else: + hosts = sorted(all_hosts) + banner = True + + if not csplit: + return hosts, "", "", True + + i = line.index(csplit[0]) + i += len(csplit[0]) + return hosts, csplit[0], line[i:].strip(), banner + + +def win_cmd_host_split(unet, cmd, kinds, defall): + if kinds: + all_hosts = { + x for x in unet.hosts if unet.hosts[x].config.get("kind", "") in kinds + } + else: + all_hosts = set(unet.hosts) + + csplit = cmd.split() + i = 0 + for i, e in enumerate(csplit): + if not host_in(e, all_hosts): + if not is_host_regex(e): + break + else: + i += 1 + + if i == 0 and csplit and csplit[0] == "*": + hosts = sorted(all_hosts) + csplit = csplit[1:] + elif i == 0 and csplit and csplit[0] == ".": + hosts = [unet] + csplit = csplit[1:] + else: + hosts = expand_hosts(csplit[:i], all_hosts) + + if not hosts and defall and not csplit[:i]: + hosts = sorted(all_hosts) + + # Filter hosts based on cmd + cmd = " ".join(csplit[i:]) + return hosts, cmd + + +def proc_readline(fd, prompt, histfile): + """Read a line of input from user while running in a sub-process.""" + # How do we change the command though, that's what's displayed in ps normally + linux.set_process_name("Munet CLI") + try: + # For some reason sys.stdin is fileno == 16 and useless + sys.stdin = os.fdopen(0) + histfile = init_history(None, histfile) + line = input(prompt) + readline.write_history_file(histfile) + if line is None: + os.write(fd, b"\n") + os.write(fd, bytes(f":{str(line)}\n", encoding="utf-8")) + except EOFError: + os.write(fd, b"\n") + except KeyboardInterrupt: + os.write(fd, b"I\n") + except Exception as error: + os.write(fd, bytes(f"E{str(error)}\n", encoding="utf-8")) + + +async def async_input_reader(rfd): + """Read a line of input from the user input sub-process pipe.""" + rpipe = os.fdopen(rfd, mode="r") + reader = asyncio.StreamReader() + + def protocol_factory(): + return asyncio.StreamReaderProtocol(reader) + + loop = asyncio.get_event_loop() + transport, _ = await loop.connect_read_pipe(protocol_factory, rpipe) + o = await reader.readline() + transport.close() + + o = o.decode("utf-8").strip() + if not o: + return None + if o[0] == "I": + raise KeyboardInterrupt() + if o[0] == "E": + raise Exception(o[1:]) + assert o[0] == ":" + return o[1:] + + +# +# A lot of work to add async `input` handling without creating a thread. We cannot use +# threads when unshare_inline is used with pid namespace per kernel clone(2) +# restriction. +# +async def async_input(prompt, histfile): + """Asynchronously read a line from the user.""" + rfd, wfd = os.pipe() + p = multiprocessing.Process(target=proc_readline, args=(wfd, prompt, histfile)) + p.start() + logging.debug("started async_input input process: %s", p) + try: + return await async_input_reader(rfd) + finally: + logging.debug("joining async_input input process") + p.join() + + +def make_help_str(unet): + + w = sorted([x if x else "" for x in unet.cli_in_window_cmds]) + ww = unet.cli_in_window_cmds + u = sorted([x if x else "" for x in unet.cli_run_cmds]) + uu = unet.cli_run_cmds + + s = ( + """ +Basic Commands: + cli :: open a secondary CLI window + help :: this help + hosts :: list hosts + quit :: quit the cli + + HOST can be a host or one of the following: + - '*' for all hosts + - '.' for the parent munet + - a regex specified between '/' (e.g., '/rtr.*/') + +New Window Commands:\n""" + + "\n".join([f" {ww[v][0]}\t:: {ww[v][1]}" for v in w]) + + """\nInline Commands:\n""" + + "\n".join([f" {uu[v][0]}\t:: {uu[v][1]}" for v in u]) + + "\n" + ) + return s + + +def get_shcmd(unet, host, kinds, execfmt, ucmd): + if host is None: + h = None + kind = None + elif host is unet or host == "": + h = unet + kind = "" + else: + h = unet.hosts[host] + kind = h.config.get("kind", "") + if kinds and kind not in kinds: + return "" + if not isinstance(execfmt, str): + execfmt = execfmt.get(kind, {}).get("exec", "") + if not execfmt: + return "" + + # Do substitutions for {} in string + numfmt = len(re.findall(r"{\d*}", execfmt)) + if numfmt > 1: + ucmd = execfmt.format(*shlex.split(ucmd)) + elif numfmt: + ucmd = execfmt.format(ucmd) + elif len(re.findall(r"{[a-zA-Z_][0-9a-zA-Z_\.]*}", execfmt)): + if execfmt.endswith('"'): + fstring = "f'''" + execfmt + "'''" + else: + fstring = 'f"""' + execfmt + '"""' + ucmd = eval( # pylint: disable=W0123 + fstring, + globals(), + {"host": h, "unet": unet, "user_input": ucmd}, + ) + else: + # No variable or usercmd substitution at all. + ucmd = execfmt + + # Do substitution for munet variables + ucmd = ucmd.replace("%CONFIGDIR%", str(unet.config_dirname)) + if host is None or host is unet: + ucmd = ucmd.replace("%RUNDIR%", str(unet.rundir)) + return ucmd.replace("%NAME%", ".") + ucmd = ucmd.replace("%RUNDIR%", str(os.path.join(unet.rundir, host))) + if h.mgmt_ip: + ucmd = ucmd.replace("%IPADDR%", str(h.mgmt_ip)) + elif h.mgmt_ip6: + ucmd = ucmd.replace("%IPADDR%", str(h.mgmt_ip6)) + if h.mgmt_ip6: + ucmd = ucmd.replace("%IP6ADDR%", str(h.mgmt_ip6)) + return ucmd.replace("%NAME%", str(host)) + + +async def run_command( + unet, + outf, + line, + execfmt, + banner, + hosts, + toplevel, + kinds, + ns_only=False, + interactive=False, +): + """Runs a command on a set of hosts. + + Runs `execfmt`. Prior to executing the string the following transformations are + performed on it. + + `execfmt` may also be a dictionary of dicitonaries keyed on kind with `exec` holding + the kind's execfmt string. + + - if `{}` is present then `str.format` is called to replace `{}` with any extra + input values after the command and hosts are removed from the input. + - else if any `{digits}` are present then `str.format` is called to replace + `{digits}` with positional args obtained from the addittional user input + first passed to `shlex.split`. + - else f-string style interpolation is performed on the string with + the local variables `host` (the current node object or None), + `unet` (the Munet object), and `user_input` (the additional command input) + defined. + + The output is sent to `outf`. If `ns_only` is True then the `execfmt` is + run using `Commander.cmd_status_nsonly` otherwise it is run with + `Commander.cmd_status`. + """ + if kinds: + logging.info("Filtering hosts to kinds: %s", kinds) + hosts = [x for x in hosts if unet.hosts[x].config.get("kind", "") in kinds] + logging.info("Filtered hosts: %s", hosts) + + if not hosts: + if not toplevel: + return + hosts = [unet] + + # if unknowns := [x for x in hosts if x not in unet.hosts]: + # outf.write("%% Unknown host[s]: %s\n" % ", ".join(unknowns)) + # return + + # if sys.stdin.isatty() and interactive: + if interactive: + for host in hosts: + shcmd = get_shcmd(unet, host, kinds, execfmt, line) + if not shcmd: + continue + if len(hosts) > 1 or banner: + outf.write(f"------ Host: {host} ------\n") + spawn(unet, host if not toplevel else unet, shcmd, outf, ns_only) + if len(hosts) > 1 or banner: + outf.write(f"------- End: {host} ------\n") + outf.write("\n") + return + + aws = [] + for host in hosts: + shcmd = get_shcmd(unet, host, kinds, execfmt, line) + if not shcmd: + continue + if toplevel: + ns = unet + else: + ns = unet.hosts[host] if host and host != unet else unet + if ns_only: + cmdf = ns.async_cmd_status_nsonly + else: + cmdf = ns.async_cmd_status + aws.append(cmdf(shcmd, warn=False, stderr=subprocess.STDOUT)) + + results = await asyncio.gather(*aws, return_exceptions=True) + for host, result in zip(hosts, results): + if isinstance(result, Exception): + o = str(result) + "\n" + rc = -1 + else: + rc, o, _ = result + if len(hosts) > 1 or banner: + outf.write(f"------ Host: {host} ------\n") + if rc: + outf.write(f"*** non-zero exit status: {rc}\n") + outf.write(o) + if len(hosts) > 1 or banner: + outf.write(f"------- End: {host} ------\n") + + +cli_builtins = ["cli", "help", "hosts", "quit"] + + +class Completer: + """A completer class for the CLI.""" + + def __init__(self, unet): + self.unet = unet + + def complete(self, text, state): + line = readline.get_line_buffer() + tokens = line.split() + # print(f"\nXXX: tokens: {tokens} text: '{text}' state: {state}'\n") + + first_token = not tokens or (text and len(tokens) == 1) + + # If we have already have a builtin command we are done + if tokens and tokens[0] in cli_builtins: + return [None] + + cli_run_cmds = set(self.unet.cli_run_cmds.keys()) + top_run_cmds = {x for x in cli_run_cmds if self.unet.cli_run_cmds[x][3]} + cli_run_cmds -= top_run_cmds + cli_win_cmds = set(self.unet.cli_in_window_cmds.keys()) + hosts = set(self.unet.hosts.keys()) + is_window_cmd = bool(tokens) and tokens[0] in cli_win_cmds + done_set = set() + if bool(tokens): + if text: + done_set = set(tokens[:-1]) + else: + done_set = set(tokens) + + # Determine the domain for completions + if not tokens or first_token: + all_cmds = ( + set(cli_builtins) | hosts | cli_run_cmds | cli_win_cmds | top_run_cmds + ) + elif is_window_cmd: + all_cmds = hosts + elif tokens and tokens[0] in top_run_cmds: + # nothing to complete if a top level command + pass + elif not bool(done_set & cli_run_cmds): + all_cmds = hosts | cli_run_cmds + + if not text: + completes = all_cmds + else: + # print(f"\nXXX: all_cmds: {all_cmds} text: '{text}'\n") + completes = {x + " " for x in all_cmds if x.startswith(text)} + + # print(f"\nXXX: completes: {completes} text: '{text}' state: {state}'\n") + # remove any completions already present + completes -= done_set + completes = sorted(completes) + [None] + return completes[state] + + +async def doline( + unet, line, outf, background=False, notty=False +): # pylint: disable=R0911 + + line = line.strip() + m = re.fullmatch(r"^(\S+)(?:\s+(.*))?$", line) + if not m: + return True + + cmd = m.group(1) + nline = m.group(2) if m.group(2) else "" + + if cmd in ("q", "quit"): + return False + + if cmd == "help": + outf.write(make_help_str(unet)) + return True + if cmd in ("h", "hosts"): + outf.write(f"% Hosts:\t{' '.join(sorted(unet.hosts.keys()))}\n") + return True + if cmd == "cli": + await remote_cli( + unet, + "secondary> ", + "Secondary CLI", + background, + ) + return True + + # + # In window commands + # + + if cmd in unet.cli_in_window_cmds: + execfmt, toplevel, kinds, kwargs = unet.cli_in_window_cmds[cmd][2:] + + # if toplevel: + # ucmd = " ".join(nline.split()) + # else: + hosts, ucmd = win_cmd_host_split(unet, nline, kinds, False) + if not hosts: + if not toplevel: + return True + hosts = [unet] + + if isinstance(execfmt, str): + found_brace = "{}" in execfmt + else: + found_brace = False + for d in execfmt.values(): + if "{}" in d["exec"]: + found_brace = True + break + if not found_brace and ucmd and not toplevel: + # CLI command does not expect user command so treat as hosts of which some + # must be unknown + unknowns = [x for x in ucmd.split() if x not in unet.hosts] + outf.write(f"% Unknown host[s]: {' '.join(unknowns)}\n") + return True + + try: + if not hosts and toplevel: + hosts = [unet] + + for host in hosts: + shcmd = get_shcmd(unet, host, kinds, execfmt, ucmd) + if toplevel or host == unet: + unet.run_in_window(shcmd, **kwargs) + else: + unet.hosts[host].run_in_window(shcmd, **kwargs) + except Exception as error: + outf.write(f"% Error: {error}\n") + return True + + # + # Inline commands + # + + toplevel = unet.cli_run_cmds[cmd][3] if cmd in unet.cli_run_cmds else False + # if toplevel: + # logging.debug("top-level: cmd: '%s' nline: '%s'", cmd, nline) + # hosts = None + # banner = False + # else: + + hosts, cmd, nline, banner = host_cmd_split(unet, line, toplevel) + hoststr = "munet" if hosts == [unet] else f"{hosts}" + logging.debug("hosts: '%s' cmd: '%s' nline: '%s'", hoststr, cmd, nline) + + if cmd in unet.cli_run_cmds: + pass + elif "" in unet.cli_run_cmds: + nline = f"{cmd} {nline}" + cmd = "" + else: + outf.write(f"% Unknown command: {cmd} {nline}\n") + return True + + execfmt, toplevel, kinds, ns_only, interactive = unet.cli_run_cmds[cmd][2:] + if interactive and notty: + outf.write("% Error: interactive command must be run from primary CLI\n") + return True + + await run_command( + unet, outf, nline, execfmt, banner, hosts, toplevel, kinds, ns_only, interactive + ) + + return True + + +async def cli_client(sockpath, prompt="munet> "): + """Implement the user-facing CLI for a remote munet reached by a socket.""" + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + sock.settimeout(10) + sock.connect(sockpath) + + # Go into full non-blocking mode now + sock.settimeout(None) + + print("\n--- Munet CLI Starting ---\n\n") + while True: + line = input(prompt) + if line is None: + return + + # Need to put \n back + line += "\n" + + # Send the CLI command + sock.send(line.encode("utf-8")) + + def bendswith(b, sentinel): + slen = len(sentinel) + return len(b) >= slen and b[-slen:] == sentinel + + # Collect the output + rb = b"" + while not bendswith(rb, ENDMARKER): + lb = sock.recv(4096) + if not lb: + return + rb += lb + + # Remove the marker + rb = rb[: -len(ENDMARKER)] + + # Write the output + sys.stdout.write(rb.decode("utf-8")) + + +async def local_cli(unet, outf, prompt, histfile, background): + """Implement the user-side CLI for local munet.""" + if unet: + completer = Completer(unet) + readline.parse_and_bind("tab: complete") + readline.set_completer(completer.complete) + + print("\n--- Munet CLI Starting ---\n\n") + while True: + try: + line = await async_input(prompt, histfile) + if line is None: + return + + assert unet is not None + + if not await doline(unet, line, outf, background): + return + except KeyboardInterrupt: + outf.write("%% Caught KeyboardInterrupt\nUse ^D or 'quit' to exit") + + +def init_history(unet, histfile): + try: + if histfile is None: + histfile = os.path.expanduser("~/.munet-history.txt") + if not os.path.exists(histfile): + if unet: + unet.cmd("touch " + histfile) + else: + subprocess.run("touch " + histfile, shell=True, check=True) + if histfile: + readline.read_history_file(histfile) + return histfile + except Exception as error: + logging.warning("init_history failed: %s", error) + return None + + +async def cli_client_connected(unet, background, reader, writer): + """Handle CLI commands inside the munet process from a socket.""" + # # Go into full non-blocking mode now + # client.settimeout(None) + logging.debug("cli client connected") + while True: + line = await reader.readline() + if not line: + logging.debug("client closed cli connection") + break + line = line.decode("utf-8").strip() + + # def writef(x): + # writer.write(x.encode("utf-8")) + + if not await doline(unet, line, writer, background, notty=True): + logging.debug("server closing cli connection") + return + + writer.write(ENDMARKER) + await writer.drain() + + +async def remote_cli(unet, prompt, title, background): + """Open a CLI in a new window.""" + try: + if not unet.cli_sockpath: + sockpath = os.path.join(tempfile.mkdtemp("-sockdir", "pty-"), "cli.sock") + ccfunc = functools.partial(cli_client_connected, unet, background) + s = await asyncio.start_unix_server(ccfunc, path=sockpath) + unet.cli_server = asyncio.create_task(s.serve_forever(), name="cli-task") + unet.cli_sockpath = sockpath + logging.info("server created on :\n%s\n", sockpath) + + # Open a new window with a new CLI + python_path = await unet.async_get_exec_path(["python3", "python"]) + us = os.path.realpath(__file__) + cmd = f"{python_path} {us}" + if unet.cli_histfile: + cmd += " --histfile=" + unet.cli_histfile + if prompt: + cmd += f" --prompt='{prompt}'" + cmd += " " + unet.cli_sockpath + unet.run_in_window(cmd, title=title, background=False) + except Exception as error: + logging.error("cli server: unexpected exception: %s", error) + + +def add_cli_in_window_cmd( + unet, name, helpfmt, helptxt, execfmt, toplevel, kinds, **kwargs +): + """Adds a CLI command to the CLI. + + The command `cmd` is added to the commands executable by the user from the CLI. See + `base.Commander.run_in_window` for the arguments that can be passed in `args` and + `kwargs` to this function. + + Args: + unet: unet object + name: command string (no spaces) + helpfmt: format of command to display in help (left side) + helptxt: help string for command (right side) + execfmt: interpreter `cmd` to pass to `host.run_in_window()`, if {} present then + allow for user commands to be entered and inserted. May also be a dict of dict + keyed on kind with sub-key of "exec" providing the `execfmt` string for that + kind. + toplevel: run command in common top-level namespaec not inside hosts + kinds: limit CLI command to nodes which match list of kinds. + **kwargs: keyword args to pass to `host.run_in_window()` + """ + unet.cli_in_window_cmds[name] = (helpfmt, helptxt, execfmt, toplevel, kinds, kwargs) + + +def add_cli_run_cmd( + unet, + name, + helpfmt, + helptxt, + execfmt, + toplevel, + kinds, + ns_only=False, + interactive=False, +): + """Adds a CLI command to the CLI. + + The command `cmd` is added to the commands executable by the user from the CLI. + See `run_command` above in the `doline` function and for the arguments that can + be passed in to this function. + + Args: + unet: unet object + name: command string (no spaces) + helpfmt: format of command to display in help (left side) + helptxt: help string for command (right side) + execfmt: format string to insert user cmds into for execution. May also be a + dict of dict keyed on kind with sub-key of "exec" providing the `execfmt` + string for that kind. + toplevel: run command in common top-level namespaec not inside hosts + kinds: limit CLI command to nodes which match list of kinds. + ns_only: Should execute the command on the host vs in the node namespace. + interactive: Should execute the command inside an allocated pty (interactive) + """ + unet.cli_run_cmds[name] = ( + helpfmt, + helptxt, + execfmt, + toplevel, + kinds, + ns_only, + interactive, + ) + + +def add_cli_config(unet, config): + """Adds CLI commands based on config. + + All exec strings will have %CONFIGDIR%, %NAME% and %RUNDIR% replaced with the + corresponding config directory and the current nodes `name` and `rundir`. + Additionally, the exec string will have f-string style interpolation performed + with the local variables `host` (node object or None), `unet` (Munet object) and + `user_input` (if provided to the CLI command) defined. + + The format of the config dictionary can be seen in the following example. + The first list entry represents the default command because it has no `name` key. + + commands: + - help: "run the given FRR command using vtysh" + format: "[HOST ...] FRR-CLI-COMMAND" + exec: "vtysh -c {}" + ns-only: false # the default + interactive: false # the default + - name: "vtysh" + help: "Open a FRR CLI inside new terminal[s] on the given HOST[s]" + format: "vtysh HOST [HOST ...]" + exec: "vtysh" + new-window: true + - name: "capture" + help: "Capture packets on a given network" + format: "pcap NETWORK" + exec: "tshark -s 9200 -i {0} -w /tmp/capture-{0}.pcap" + new-window: true + top-level: true # run in top-level container namespace, above hosts + + The `new_window` key can also be a dictionary which will be passed as keyward + arguments to the `Commander.run_in_window()` function. + + Args: + unet: unet object + config: dictionary of cli config + """ + for cli_cmd in config.get("commands", []): + name = cli_cmd.get("name", None) + helpfmt = cli_cmd.get("format", "") + helptxt = cli_cmd.get("help", "") + execfmt = list_to_dict_with_key(cli_cmd.get("exec-kind"), "kind") + if not execfmt: + execfmt = cli_cmd.get("exec", "bash -c '{}'") + toplevel = cli_cmd.get("top-level", False) + kinds = cli_cmd.get("kinds", []) + stdargs = (unet, name, helpfmt, helptxt, execfmt, toplevel, kinds) + new_window = cli_cmd.get("new-window", None) + if isinstance(new_window, dict): + add_cli_in_window_cmd(*stdargs, **new_window) + elif bool(new_window): + add_cli_in_window_cmd(*stdargs) + else: + # on-host is deprecated it really implemented "ns-only" + add_cli_run_cmd( + *stdargs, + cli_cmd.get("ns-only", cli_cmd.get("on-host")), + cli_cmd.get("interactive", False), + ) + + +def cli( + unet, + histfile=None, + sockpath=None, + force_window=False, + title=None, + prompt=None, + background=True, +): + asyncio.run( + async_cli(unet, histfile, sockpath, force_window, title, prompt, background) + ) + + +async def async_cli( + unet, + histfile=None, + sockpath=None, + force_window=False, + title=None, + prompt=None, + background=True, +): + if prompt is None: + prompt = "munet> " + + if force_window or not sys.stdin.isatty(): + await remote_cli(unet, prompt, title, background) + + if not unet: + logger.debug("client-cli using sockpath %s", sockpath) + + try: + if sockpath: + await cli_client(sockpath, prompt) + else: + await local_cli(unet, sys.stdout, prompt, histfile, background) + except KeyboardInterrupt: + print("\n...^C exiting CLI") + except EOFError: + pass + except Exception as ex: + logger.critical("cli: got exception: %s", ex, exc_info=True) + raise + + +if __name__ == "__main__": + # logging.basicConfig(level=logging.DEBUG, filename="/tmp/topotests/cli-client.log") + logging.basicConfig(level=logging.DEBUG) + logger = logging.getLogger("cli-client") + logger.info("Start logging cli-client") + + parser = argparse.ArgumentParser() + parser.add_argument("--histfile", help="file to user for history") + parser.add_argument("--prompt", help="prompt string to use") + parser.add_argument("socket", help="path to pair of sockets to communicate over") + cli_args = parser.parse_args() + + cli_prompt = cli_args.prompt if cli_args.prompt else "munet> " + asyncio.run( + async_cli( + None, + cli_args.histfile, + cli_args.socket, + prompt=cli_prompt, + background=False, + ) + ) diff --git a/tests/topotests/munet/compat.py b/tests/topotests/munet/compat.py new file mode 100644 index 0000000000..bf9092e53a --- /dev/null +++ b/tests/topotests/munet/compat.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# November 16 2022, Christian Hopps <chopps@labn.net> +# +# Copyright (c) 2022, LabN Consulting, L.L.C. +# +"""Provide compatible APIs.""" + + +class PytestConfig: + """Pytest config duck-type-compatible object using argprase args.""" + + def __init__(self, args): + self.args = vars(args) + + def getoption(self, name, default=None, skip=False): + assert not skip + if name.startswith("--"): + name = name[2:] + name = name.replace("-", "_") + if name in self.args: + return self.args[name] if self.args[name] is not None else default + return default diff --git a/tests/topotests/munet/config.py b/tests/topotests/munet/config.py new file mode 100644 index 0000000000..b66e4d17da --- /dev/null +++ b/tests/topotests/munet/config.py @@ -0,0 +1,158 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# June 25 2022, Christian Hopps <chopps@gmail.com> +# +# Copyright (c) 2021-2022, LabN Consulting, L.L.C. +# +"""A module that defines common configuration utility functions.""" +import logging + +from collections.abc import Iterable +from copy import deepcopy +from typing import overload + + +def find_with_kv(lst, k, v): + if lst: + for e in lst: + if k in e and e[k] == v: + return e + return {} + + +def find_all_with_kv(lst, k, v): + rv = [] + if lst: + for e in lst: + if k in e and e[k] == v: + rv.append(e) + return rv + + +def find_matching_net_config(name, cconf, oconf): + p = find_all_with_kv(oconf.get("connections", {}), "to", name) + if not p: + return {} + + rname = cconf.get("remote-name", None) + if not rname: + return p[0] + + return find_with_kv(p, "name", rname) + + +def merge_using_key(a, b, k): + # First get a dict of indexes in `a` for the key value of `k` in objects of `a` + m = list(a) + mi = {o[k]: i for i, o in enumerate(m)} + for o in b: + bkv = o[k] + if bkv in mi: + m[mi[bkv]] = o + else: + mi[bkv] = len(m) + m.append(o) + return m + + +def list_to_dict_with_key(lst, k): + """Convert a YANG styl list of objects to dict of objects. + + This function converts a YANG style list of objects (dictionaries) to a plain python + dictionary of objects (dictionaries). The value for the supplied key for each + object is used to store the object in the new diciontary. + + This only works for lists of objects which are keyed on a single contained value. + + Args: + lst: a *list* of python dictionary objects. + k: the key value contained in each dictionary object in the list. + + Returns: + A dictionary of objects (dictionaries). + """ + return {x[k]: x for x in (lst if lst else [])} + + +def config_to_dict_with_key(c, ck, k): + """Convert the config item from a list of objects to dict. + + Use :py:func:`list_to_dict_with_key` to convert the list of objects + at ``c[ck]`` to a dict of the objects using the key ``k``. + + Args: + c: config dictionary + ck: The key identifying the list of objects from ``c``. + k: The key to pass to :py:func:`list_to_dict_with_key`. + + Returns: + A dictionary of objects (dictionaries). + """ + c[ck] = list_to_dict_with_key(c.get(ck, []), k) + return c[ck] + + +@overload +def config_subst(config: str, **kwargs) -> str: + ... + + +@overload +def config_subst(config: Iterable, **kwargs) -> Iterable: + ... + + +def config_subst(config: Iterable, **kwargs) -> Iterable: + if isinstance(config, str): + if "%RUNDIR%/%NAME%" in config: + config = config.replace("%RUNDIR%/%NAME%", "%RUNDIR%") + logging.warning( + "config '%RUNDIR%/%NAME%' should be changed to '%RUNDIR%' only, " + "converting automatically for now." + ) + for name, value in kwargs.items(): + config = config.replace(f"%{name.upper()}%", str(value)) + elif isinstance(config, Iterable): + try: + return {k: config_subst(config[k], **kwargs) for k in config} + except (KeyError, TypeError): + return [config_subst(x, **kwargs) for x in config] + return config + + +def value_merge_deepcopy(s1, s2): + """Merge values using deepcopy. + + Create a deepcopy of the result of merging the values from dicts ``s1`` and ``s2``. + If a key exists in both ``s1`` and ``s2`` the value from ``s2`` is used." + """ + d = {} + for k, v in s1.items(): + if k in s2: + d[k] = deepcopy(s2[k]) + else: + d[k] = deepcopy(v) + return d + + +def merge_kind_config(kconf, config): + mergekeys = kconf.get("merge", []) + config = deepcopy(config) + new = deepcopy(kconf) + for k in new: + if k not in config: + continue + + if k not in mergekeys: + new[k] = config[k] + elif isinstance(new[k], list): + new[k].extend(config[k]) + elif isinstance(new[k], dict): + new[k] = {**new[k], **config[k]} + else: + new[k] = config[k] + for k in config: + if k not in new: + new[k] = config[k] + return new diff --git a/tests/topotests/munet/kinds.yaml b/tests/topotests/munet/kinds.yaml new file mode 100644 index 0000000000..0c278d37c9 --- /dev/null +++ b/tests/topotests/munet/kinds.yaml @@ -0,0 +1,84 @@ +version: 1 +kinds: + - name: frr + cap-add: + # Zebra requires these + - NET_ADMIN + - NET_RAW + - SYS_ADMIN + - AUDIT_WRITE # needed for ssh pty allocation + - name: ceos + init: false + shell: false + merge: ["env"] + # Should we cap-drop some of these in privileged mode? + # ceos kind is special. munet will add args to /sbin/init for each + # environment variable of the form `systemd.setenv=ENVNAME=VALUE` for each + # environment varialbe named ENVNAME with a value of `VALUE`. If cmd: is + # changed to anything but `/sbin/init` munet will not do this. + cmd: /sbin/init + privileged: true + env: + - name: "EOS_PLATFORM" + value: "ceoslab" + - name: "container" + value: "docker" + - name: "ETBA" + value: "4" + - name: "SKIP_ZEROTOUCH_BARRIER_IN_SYSDBINIT" + value: "1" + - name: "INTFTYPE" + value: "eth" + - name: "MAPETH0" + value: "1" + - name: "MGMT_INTF" + value: "eth0" + - name: "CEOS" + value: "1" + + # cap-add: + # # cEOS requires these, except GNMI still doesn't work + # # - NET_ADMIN + # # - NET_RAW + # # - SYS_ADMIN + # # - SYS_RESOURCE # Required for the CLI + + # All Caps + # - AUDIT_CONTROL + # - AUDIT_READ + # - AUDIT_WRITE + # - BLOCK_SUSPEND + # - CHOWN + # - DAC_OVERRIDE + # - DAC_READ_SEARCH + # - FOWNER + # - FSETID + # - IPC_LOCK + # - IPC_OWNER + # - KILL + # - LEASE + # - LINUX_IMMUTABLE + # - MKNOD + # - NET_ADMIN + # - NET_BIND_SERVICE + # - NET_BROADCAST + # - NET_RAW + # - SETFCAP + # - SETGID + # - SETPCAP + # - SETUID + # - SYSLOG + # - SYS_ADMIN + # - SYS_BOOT + # - SYS_CHROOT + # - SYS_MODULE + # - SYS_NICE + # - SYS_PACCT + # - SYS_PTRACE + # - SYS_RAWIO + # - SYS_RESOURCE + # - SYS_TIME + # - SYS_TTY_CONFIG + # - WAKE_ALARM + # - MAC_ADMIN - Smack project? + # - MAC_OVERRIDE - Smack project? diff --git a/tests/topotests/munet/linux.py b/tests/topotests/munet/linux.py new file mode 100644 index 0000000000..417f74566a --- /dev/null +++ b/tests/topotests/munet/linux.py @@ -0,0 +1,267 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# June 10 2022, Christian Hopps <chopps@labn.net> +# +# Copyright (c) 2022, LabN Consulting, L.L.C. +# +"""A module that gives access to linux unshare system call.""" + +import ctypes # pylint: disable=C0415 +import ctypes.util # pylint: disable=C0415 +import errno +import functools +import os + + +libc = None + + +def raise_oserror(enum): + s = errno.errorcode[enum] if enum in errno.errorcode else str(enum) + error = OSError(s) + error.errno = enum + error.strerror = s + raise error + + +def _load_libc(): + global libc # pylint: disable=W0601,W0603 + if libc: + return + lcpath = ctypes.util.find_library("c") + libc = ctypes.CDLL(lcpath, use_errno=True) + + +def pause(): + if not libc: + _load_libc() + libc.pause() + + +MS_RDONLY = 1 +MS_NOSUID = 1 << 1 +MS_NODEV = 1 << 2 +MS_NOEXEC = 1 << 3 +MS_SYNCHRONOUS = 1 << 4 +MS_REMOUNT = 1 << 5 +MS_MANDLOCK = 1 << 6 +MS_DIRSYNC = 1 << 7 +MS_NOSYMFOLLOW = 1 << 8 +MS_NOATIME = 1 << 10 +MS_NODIRATIME = 1 << 11 +MS_BIND = 1 << 12 +MS_MOVE = 1 << 13 +MS_REC = 1 << 14 +MS_SILENT = 1 << 15 +MS_POSIXACL = 1 << 16 +MS_UNBINDABLE = 1 << 17 +MS_PRIVATE = 1 << 18 +MS_SLAVE = 1 << 19 +MS_SHARED = 1 << 20 +MS_RELATIME = 1 << 21 +MS_KERNMOUNT = 1 << 22 +MS_I_VERSION = 1 << 23 +MS_STRICTATIME = 1 << 24 +MS_LAZYTIME = 1 << 25 + + +def mount(source, target, fs, flags=0, options=""): + if not libc: + _load_libc() + libc.mount.argtypes = ( + ctypes.c_char_p, + ctypes.c_char_p, + ctypes.c_char_p, + ctypes.c_ulong, + ctypes.c_char_p, + ) + fsenc = fs.encode() if fs else None + optenc = options.encode() if options else None + ret = libc.mount(source.encode(), target.encode(), fsenc, flags, optenc) + if ret < 0: + err = ctypes.get_errno() + raise OSError( + err, + f"Error mounting {source} ({fs}) on {target}" + f" with options '{options}': {os.strerror(err)}", + ) + + +# unmout options +MNT_FORCE = 0x1 +MNT_DETACH = 0x2 +MNT_EXPIRE = 0x4 +UMOUNT_NOFOLLOW = 0x8 + + +def umount(target, options): + if not libc: + _load_libc() + libc.umount.argtypes = (ctypes.c_char_p, ctypes.c_uint) + + ret = libc.umount(target.encode(), int(options)) + if ret < 0: + err = ctypes.get_errno() + raise OSError( + err, + f"Error umounting {target} with options '{options}': {os.strerror(err)}", + ) + + +def pidfd_open(pid, flags=0): + if hasattr(os, "pidfd_open") and os.pidfd_open is not pidfd_open: + return os.pidfd_open(pid, flags) # pylint: disable=no-member + + if not libc: + _load_libc() + + try: + pfof = libc.pidfd_open + except AttributeError: + __NR_pidfd_open = 434 + _pidfd_open = libc.syscall + _pidfd_open.restype = ctypes.c_int + _pidfd_open.argtypes = ctypes.c_long, ctypes.c_uint, ctypes.c_uint + pfof = functools.partial(_pidfd_open, __NR_pidfd_open) + + fd = pfof(int(pid), int(flags)) + if fd == -1: + raise_oserror(ctypes.get_errno()) + + return fd + + +if not hasattr(os, "pidfd_open"): + os.pidfd_open = pidfd_open + + +def setns(fd, nstype): # noqa: D402 + """See setns(2) manpage.""" + if not libc: + _load_libc() + + if libc.setns(int(fd), int(nstype)) == -1: + raise_oserror(ctypes.get_errno()) + + +def unshare(flags): # noqa: D402 + """See unshare(2) manpage.""" + if not libc: + _load_libc() + + if libc.unshare(int(flags)) == -1: + raise_oserror(ctypes.get_errno()) + + +CLONE_NEWTIME = 0x00000080 +CLONE_VM = 0x00000100 +CLONE_FS = 0x00000200 +CLONE_FILES = 0x00000400 +CLONE_SIGHAND = 0x00000800 +CLONE_PIDFD = 0x00001000 +CLONE_PTRACE = 0x00002000 +CLONE_VFORK = 0x00004000 +CLONE_PARENT = 0x00008000 +CLONE_THREAD = 0x00010000 +CLONE_NEWNS = 0x00020000 +CLONE_SYSVSEM = 0x00040000 +CLONE_SETTLS = 0x00080000 +CLONE_PARENT_SETTID = 0x00100000 +CLONE_CHILD_CLEARTID = 0x00200000 +CLONE_DETACHED = 0x00400000 +CLONE_UNTRACED = 0x00800000 +CLONE_CHILD_SETTID = 0x01000000 +CLONE_NEWCGROUP = 0x02000000 +CLONE_NEWUTS = 0x04000000 +CLONE_NEWIPC = 0x08000000 +CLONE_NEWUSER = 0x10000000 +CLONE_NEWPID = 0x20000000 +CLONE_NEWNET = 0x40000000 +CLONE_IO = 0x80000000 + +clone_flag_names = { + CLONE_NEWTIME: "CLONE_NEWTIME", + CLONE_VM: "CLONE_VM", + CLONE_FS: "CLONE_FS", + CLONE_FILES: "CLONE_FILES", + CLONE_SIGHAND: "CLONE_SIGHAND", + CLONE_PIDFD: "CLONE_PIDFD", + CLONE_PTRACE: "CLONE_PTRACE", + CLONE_VFORK: "CLONE_VFORK", + CLONE_PARENT: "CLONE_PARENT", + CLONE_THREAD: "CLONE_THREAD", + CLONE_NEWNS: "CLONE_NEWNS", + CLONE_SYSVSEM: "CLONE_SYSVSEM", + CLONE_SETTLS: "CLONE_SETTLS", + CLONE_PARENT_SETTID: "CLONE_PARENT_SETTID", + CLONE_CHILD_CLEARTID: "CLONE_CHILD_CLEARTID", + CLONE_DETACHED: "CLONE_DETACHED", + CLONE_UNTRACED: "CLONE_UNTRACED", + CLONE_CHILD_SETTID: "CLONE_CHILD_SETTID", + CLONE_NEWCGROUP: "CLONE_NEWCGROUP", + CLONE_NEWUTS: "CLONE_NEWUTS", + CLONE_NEWIPC: "CLONE_NEWIPC", + CLONE_NEWUSER: "CLONE_NEWUSER", + CLONE_NEWPID: "CLONE_NEWPID", + CLONE_NEWNET: "CLONE_NEWNET", + CLONE_IO: "CLONE_IO", +} + + +def clone_flag_string(flags): + ns = [v for k, v in clone_flag_names.items() if k & flags] + if ns: + return "|".join(ns) + return "None" + + +namespace_files = { + CLONE_NEWUSER: "ns/user", + CLONE_NEWCGROUP: "ns/cgroup", + CLONE_NEWIPC: "ns/ipc", + CLONE_NEWUTS: "ns/uts", + CLONE_NEWNET: "ns/net", + CLONE_NEWPID: "ns/pid_for_children", + CLONE_NEWNS: "ns/mnt", + CLONE_NEWTIME: "ns/time_for_children", +} + +PR_SET_PDEATHSIG = 1 +PR_GET_PDEATHSIG = 2 +PR_SET_NAME = 15 +PR_GET_NAME = 16 + + +def set_process_name(name): + if not libc: + _load_libc() + + # Why does uncommenting this cause failure? + # libc.prctl.argtypes = ( + # ctypes.c_int, + # ctypes.c_ulong, + # ctypes.c_ulong, + # ctypes.c_ulong, + # ctypes.c_ulong, + # ) + + s = ctypes.create_string_buffer(bytes(name, encoding="ascii")) + sr = ctypes.byref(s) + libc.prctl(PR_SET_NAME, sr, 0, 0, 0) + + +def set_parent_death_signal(signum): + if not libc: + _load_libc() + + # Why does uncommenting this cause failure? + libc.prctl.argtypes = ( + ctypes.c_int, + ctypes.c_ulong, + ctypes.c_ulong, + ctypes.c_ulong, + ctypes.c_ulong, + ) + + libc.prctl(PR_SET_PDEATHSIG, signum, 0, 0, 0) diff --git a/tests/topotests/munet/logconf-mutest.yaml b/tests/topotests/munet/logconf-mutest.yaml new file mode 100644 index 0000000000..b450fb9382 --- /dev/null +++ b/tests/topotests/munet/logconf-mutest.yaml @@ -0,0 +1,84 @@ +version: 1 +formatters: + brief: + format: '%(levelname)5s: %(message)s' + operfmt: + class: munet.mulog.ColorFormatter + format: ' ------| %(message)s' + exec: + format: '%(asctime)s %(levelname)5s: %(name)s: %(message)s' + output: + format: '%(asctime)s %(levelname)5s: OUTPUT: %(message)s' + results: + # format: '%(asctime)s %(levelname)5s: %(message)s' + format: '%(message)s' + +handlers: + console: + level: WARNING + class: logging.StreamHandler + formatter: brief + stream: ext://sys.stderr + info_console: + level: INFO + class: logging.StreamHandler + formatter: brief + stream: ext://sys.stderr + oper_console: + level: DEBUG + class: logging.StreamHandler + formatter: operfmt + stream: ext://sys.stderr + exec: + level: DEBUG + class: logging.FileHandler + formatter: exec + filename: mutest-exec.log + mode: w + output: + level: DEBUG + class: munet.mulog.MultiFileHandler + root_path: "mutest.output" + formatter: output + filename: mutest-output.log + mode: w + results: + level: INFO + class: munet.mulog.MultiFileHandler + root_path: "mutest.results" + new_handler_level: DEBUG + formatter: results + filename: mutest-results.log + mode: w + +root: + level: DEBUG + handlers: [ "console", "exec" ] + +loggers: + # These are some loggers that get used... + # munet: + # level: DEBUG + # propagate: true + # munet.base.commander + # level: DEBUG + # propagate: true + # mutest.error: + # level: DEBUG + # propagate: true + mutest.output: + level: DEBUG + handlers: ["output", "exec"] + propagate: false + mutest.results: + level: DEBUG + handlers: [ "info_console", "exec", "output", "results" ] + # We don't propagate this b/c we want a lower level accept on the console + # Instead we use info_console and exec to cover what root would log to. + propagate: false + # This is used to debug the operation of mutest + mutest.oper: + # Records are emitted at DEBUG so this will normally filter everything + level: INFO + handlers: [ "oper_console" ] + propagate: false diff --git a/tests/topotests/munet/logconf.yaml b/tests/topotests/munet/logconf.yaml new file mode 100644 index 0000000000..430ee2096d --- /dev/null +++ b/tests/topotests/munet/logconf.yaml @@ -0,0 +1,32 @@ +version: 1 +formatters: + brief: + format: '%(asctime)s: %(levelname)s: %(message)s' + precise: + format: '%(asctime)s %(levelname)s: %(name)s: %(message)s' + +handlers: + console: + class: logging.StreamHandler + formatter: brief + level: INFO + stream: ext://sys.stderr + file: + class: logging.FileHandler + formatter: precise + level: DEBUG + filename: munet-exec.log + mode: w + +root: + level: DEBUG + handlers: [ "console", "file" ] + +# these are some loggers that get used. +# loggers: +# munet: +# level: DEBUG +# propagate: true +# munet.base.commander +# level: DEBUG +# propagate: true diff --git a/tests/topotests/munet/mucmd.py b/tests/topotests/munet/mucmd.py new file mode 100644 index 0000000000..5518c6dcfe --- /dev/null +++ b/tests/topotests/munet/mucmd.py @@ -0,0 +1,111 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# December 5 2021, Christian Hopps <chopps@labn.net> +# +# Copyright 2021, LabN Consulting, L.L.C. +# +"""A command that allows external command execution inside nodes.""" +import argparse +import json +import os +import subprocess +import sys + +from pathlib import Path + + +def newest_file_in(filename, paths, has_sibling=None): + new = None + newst = None + items = (x for y in paths for x in Path(y).rglob(filename)) + for e in items: + st = os.stat(e) + if has_sibling and not e.parent.joinpath(has_sibling).exists(): + continue + if not new or st.st_mtime_ns > newst.st_mtime_ns: + new = e + newst = st + continue + return new, newst + + +def main(*args): + ap = argparse.ArgumentParser(args) + ap.add_argument("-d", "--rundir", help="runtime directory for tempfiles, logs, etc") + ap.add_argument("node", nargs="?", help="node to enter or run command inside") + ap.add_argument( + "shellcmd", + nargs=argparse.REMAINDER, + help="optional shell-command to execute on NODE", + ) + args = ap.parse_args() + if args.rundir: + configpath = Path(args.rundir).joinpath("config.json") + else: + configpath, _ = newest_file_in( + "config.json", + ["/tmp/munet", "/tmp/mutest", "/tmp/unet-test"], + has_sibling=args.node, + ) + print(f'Using "{configpath}"') + + if not configpath.exists(): + print(f'"{configpath}" not found') + return 1 + rundir = configpath.parent + + nodes = [] + config = json.load(open(configpath, encoding="utf-8")) + nodes = list(config.get("topology", {}).get("nodes", [])) + envcfg = config.get("mucmd", {}).get("env", {}) + + # If args.node is not a node it's part of shellcmd + if args.node and args.node not in nodes: + if args.node != ".": + args.shellcmd[0:0] = [args.node] + args.node = None + + if args.node: + name = args.node + nodedir = rundir.joinpath(name) + if not nodedir.exists(): + print('"{name}" node doesn\'t exist in "{rundir}"') + return 1 + rundir = nodedir + else: + name = "munet" + pidpath = rundir.joinpath("nspid") + pid = open(pidpath, encoding="ascii").read().strip() + + env = {**os.environ} + env["MUNET_NODENAME"] = name + env["MUNET_RUNDIR"] = str(rundir) + + for k in envcfg: + envcfg[k] = envcfg[k].replace("%NAME%", str(name)) + envcfg[k] = envcfg[k].replace("%RUNDIR%", str(rundir)) + + # Can't use -F if it's a new pid namespace + ecmd = "/usr/bin/nsenter" + eargs = [ecmd] + + output = subprocess.check_output(["/usr/bin/nsenter", "--help"], encoding="utf-8") + if " -a," in output: + eargs.append("-a") + else: + # -U doesn't work + for flag in ["-u", "-i", "-m", "-n", "-C", "-T"]: + if f" {flag}," in output: + eargs.append(flag) + eargs.append(f"--pid=/proc/{pid}/ns/pid_for_children") + eargs.append(f"--wd={rundir}") + eargs.extend(["-t", pid]) + eargs += args.shellcmd + # print("Using ", eargs) + return os.execvpe(ecmd, eargs, {**env, **envcfg}) + + +if __name__ == "__main__": + exit_status = main() + sys.exit(exit_status) diff --git a/tests/topotests/munet/mulog.py b/tests/topotests/munet/mulog.py new file mode 100644 index 0000000000..f840eae2d8 --- /dev/null +++ b/tests/topotests/munet/mulog.py @@ -0,0 +1,122 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# December 4 2022, Christian Hopps <chopps@labn.net> +# +# Copyright (c) 2022, LabN Consulting, L.L.C. +# +"""Utilities for logging in munet.""" + +import logging + +from pathlib import Path + + +class MultiFileHandler(logging.FileHandler): + """A logging handler that logs to new files based on the logger name. + + The MultiFileHandler operates as a FileHandler with additional functionality. In + addition to logging to the specified logging file MultiFileHandler also creates new + FileHandlers for child loggers based on a root logging name path. + + The ``root_path`` determines when to create a new FileHandler. For each received log + record, ``root_path`` is removed from the logger name of the record if present, and + the resulting channel path (if any) determines the directory for a new log file to + also emit the record to. The new file path is constructed by starting with the + directory ``filename`` resides in, then joining the path determined above after + converting "." to "/" and finally by adding back the basename of ``filename``. + + record logger path => mutest.output.testingfoo + root_path => mutest.output + base filename => /tmp/mutest/mutest-exec.log + new logfile => /tmp/mutest/testingfoo/mutest-exec.log + + All messages are also emitted to the common FileLogger for ``filename``. + + If a log record is from a logger that does not start with ``root_path`` no file is + created and the normal emit occurs. + + Args: + root_path: the logging path of the root level for this handler. + new_handler_level: logging level for newly created handlers + log_dir: the log directory to put log files in. + filename: the base log file. + """ + + def __init__(self, root_path, filename=None, **kwargs): + self.__root_path = root_path + self.__basename = Path(filename).name + if root_path[-1] != ".": + self.__root_path += "." + self.__root_pathlen = len(self.__root_path) + self.__kwargs = kwargs + self.__log_dir = Path(filename).absolute().parent + self.__log_dir.mkdir(parents=True, exist_ok=True) + self.__filenames = {} + self.__added = set() + + if "new_handler_level" not in kwargs: + self.__new_handler_level = logging.NOTSET + else: + new_handler_level = kwargs["new_handler_level"] + del kwargs["new_handler_level"] + self.__new_handler_level = new_handler_level + + super().__init__(filename=filename, **kwargs) + + if self.__new_handler_level is None: + self.__new_handler_level = self.level + + def __log_filename(self, name): + if name in self.__filenames: + return self.__filenames[name] + + if not name.startswith(self.__root_path): + newname = None + else: + newname = name[self.__root_pathlen :] + newname = Path(newname.replace(".", "/")) + newname = self.__log_dir.joinpath(newname) + newname = newname.joinpath(self.__basename) + self.__filenames[name] = newname + + self.__filenames[name] = newname + return newname + + def emit(self, record): + newname = self.__log_filename(record.name) + if newname: + if newname not in self.__added: + self.__added.add(newname) + h = logging.FileHandler(filename=newname, **self.__kwargs) + h.setLevel(self.__new_handler_level) + h.setFormatter(self.formatter) + logging.getLogger(record.name).addHandler(h) + h.emit(record) + super().emit(record) + + +class ColorFormatter(logging.Formatter): + """A formatter that adds color sequences based on level.""" + + def __init__(self, fmt=None, datefmt=None, style="%", **kwargs): + grey = "\x1b[90m" + yellow = "\x1b[33m" + red = "\x1b[31m" + bold_red = "\x1b[31;1m" + reset = "\x1b[0m" + # basefmt = " ------| %(message)s " + + self.formatters = { + logging.DEBUG: logging.Formatter(grey + fmt + reset), + logging.INFO: logging.Formatter(grey + fmt + reset), + logging.WARNING: logging.Formatter(yellow + fmt + reset), + logging.ERROR: logging.Formatter(red + fmt + reset), + logging.CRITICAL: logging.Formatter(bold_red + fmt + reset), + } + # Why are we even bothering? + super().__init__(fmt, datefmt, style, **kwargs) + + def format(self, record): + formatter = self.formatters.get(record.levelno) + return formatter.format(record) diff --git a/tests/topotests/munet/munet-schema.json b/tests/topotests/munet/munet-schema.json new file mode 100644 index 0000000000..a1dcd878dd --- /dev/null +++ b/tests/topotests/munet/munet-schema.json @@ -0,0 +1,654 @@ +{ + "title": "labn-munet-config", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "Generated by pyang from module labn-munet-config", + "type": "object", + "properties": { + "cli": { + "type": "object", + "properties": { + "commands": { + "type": "array", + "items": { + "type": "object", + "properties": { + "exec": { + "type": "string" + }, + "exec-kind": { + "type": "array", + "items": { + "type": "object", + "properties": { + "kind": { + "type": "string" + }, + "exec": { + "type": "string" + } + } + } + }, + "format": { + "type": "string" + }, + "help": { + "type": "string" + }, + "interactive": { + "type": "boolean" + }, + "kinds": { + "type": "array", + "items": { + "type": "string" + } + }, + "name": { + "type": "string" + }, + "new-window": { + "type": "boolean" + }, + "top-level": { + "type": "boolean" + } + } + } + } + } + }, + "kinds": { + "type": "array", + "items": { + "type": "object", + "properties": { + "merge": { + "type": "array", + "items": { + "type": "string" + } + }, + "cap-add": { + "type": "array", + "items": { + "type": "string" + } + }, + "cap-remove": { + "type": "array", + "items": { + "type": "string" + } + }, + "cmd": { + "type": "string" + }, + "cleanup-cmd": { + "type": "string" + }, + "ready-cmd": { + "type": "string" + }, + "image": { + "type": "string" + }, + "server": { + "type": "string" + }, + "server-port": { + "type": "number" + }, + "qemu": { + "type": "object", + "properties": { + "bios": { + "type": "string" + }, + "disk": { + "type": "string" + }, + "kerenel": { + "type": "string" + }, + "initrd": { + "type": "string" + }, + "kvm": { + "type": "boolean" + }, + "ncpu": { + "type": "integer" + }, + "memory": { + "type": "string" + }, + "root": { + "type": "string" + }, + "cmdline-extra": { + "type": "string" + }, + "extra-args": { + "type": "string" + }, + "console": { + "type": "object", + "properties": { + "user": { + "type": "string" + }, + "password": { + "type": "string" + }, + "expects": { + "type": "array", + "items": { + "type": "string" + } + }, + "sends": { + "type": "array", + "items": { + "type": "string" + } + }, + "timeout": { + "type": "integer" + } + } + } + } + }, + "connections": { + "type": "array", + "items": { + "type": "object", + "properties": { + "to": { + "type": "string" + }, + "ip": { + "type": "string" + }, + "ipv6": { + "type": "string" + }, + "name": { + "type": "string" + }, + "hostintf": { + "type": "string" + }, + "physical": { + "type": "string" + }, + "remote-name": { + "type": "string" + }, + "driver": { + "type": "string" + }, + "delay": { + "type": "integer" + }, + "jitter": { + "type": "integer" + }, + "jitter-correlation": { + "type": "string" + }, + "loss": { + "type": "integer" + }, + "loss-correlation": { + "type": "string" + }, + "rate": { + "type": "object", + "properties": { + "rate": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ] + }, + "limit": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ] + }, + "burst": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ] + } + } + } + } + } + }, + "env": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "value": { + "type": "string" + } + } + } + }, + "gdb-cmd": { + "type": "string" + }, + "gdb-target-cmds": { + "type": "array", + "items": { + "type": "string" + } + }, + "gdb-run-cmds": { + "type": "array", + "items": { + "type": "string" + } + }, + "init": { + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "string" + } + ] + }, + "mounts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "destination": { + "type": "string" + }, + "source": { + "type": "string" + }, + "tmpfs-size": { + "type": "string" + }, + "type": { + "type": "string" + } + } + } + }, + "name": { + "type": "string" + }, + "podman": { + "type": "object", + "properties": { + "extra-args": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "privileged": { + "type": "boolean" + }, + "shell": { + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "string" + } + ] + }, + "volumes": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + }, + "topology": { + "type": "object", + "properties": { + "dns-network": { + "type": "string" + }, + "ipv6-enable": { + "type": "boolean" + }, + "networks-autonumber": { + "type": "boolean" + }, + "networks": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "ip": { + "type": "string" + }, + "ipv6": { + "type": "string" + } + } + } + }, + "nodes": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "kind": { + "type": "string" + }, + "cap-add": { + "type": "array", + "items": { + "type": "string" + } + }, + "cap-remove": { + "type": "array", + "items": { + "type": "string" + } + }, + "cmd": { + "type": "string" + }, + "cleanup-cmd": { + "type": "string" + }, + "ready-cmd": { + "type": "string" + }, + "image": { + "type": "string" + }, + "server": { + "type": "string" + }, + "server-port": { + "type": "number" + }, + "qemu": { + "type": "object", + "properties": { + "bios": { + "type": "string" + }, + "disk": { + "type": "string" + }, + "kerenel": { + "type": "string" + }, + "initrd": { + "type": "string" + }, + "kvm": { + "type": "boolean" + }, + "ncpu": { + "type": "integer" + }, + "memory": { + "type": "string" + }, + "root": { + "type": "string" + }, + "cmdline-extra": { + "type": "string" + }, + "extra-args": { + "type": "string" + }, + "console": { + "type": "object", + "properties": { + "user": { + "type": "string" + }, + "password": { + "type": "string" + }, + "expects": { + "type": "array", + "items": { + "type": "string" + } + }, + "sends": { + "type": "array", + "items": { + "type": "string" + } + }, + "timeout": { + "type": "integer" + } + } + } + } + }, + "connections": { + "type": "array", + "items": { + "type": "object", + "properties": { + "to": { + "type": "string" + }, + "ip": { + "type": "string" + }, + "ipv6": { + "type": "string" + }, + "name": { + "type": "string" + }, + "hostintf": { + "type": "string" + }, + "physical": { + "type": "string" + }, + "remote-name": { + "type": "string" + }, + "driver": { + "type": "string" + }, + "delay": { + "type": "integer" + }, + "jitter": { + "type": "integer" + }, + "jitter-correlation": { + "type": "string" + }, + "loss": { + "type": "integer" + }, + "loss-correlation": { + "type": "string" + }, + "rate": { + "type": "object", + "properties": { + "rate": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ] + }, + "limit": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ] + }, + "burst": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ] + } + } + } + } + } + }, + "env": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "value": { + "type": "string" + } + } + } + }, + "gdb-cmd": { + "type": "string" + }, + "gdb-target-cmds": { + "type": "array", + "items": { + "type": "string" + } + }, + "gdb-run-cmds": { + "type": "array", + "items": { + "type": "string" + } + }, + "init": { + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "string" + } + ] + }, + "mounts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "destination": { + "type": "string" + }, + "source": { + "type": "string" + }, + "tmpfs-size": { + "type": "string" + }, + "type": { + "type": "string" + } + } + } + }, + "name": { + "type": "string" + }, + "podman": { + "type": "object", + "properties": { + "extra-args": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "privileged": { + "type": "boolean" + }, + "shell": { + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "string" + } + ] + }, + "volumes": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + } + }, + "version": { + "type": "integer" + } + } +}
\ No newline at end of file diff --git a/tests/topotests/munet/mutest/__main__.py b/tests/topotests/munet/mutest/__main__.py new file mode 100644 index 0000000000..c87031112d --- /dev/null +++ b/tests/topotests/munet/mutest/__main__.py @@ -0,0 +1,445 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# December 2 2022, Christian Hopps <chopps@labn.net> +# +# Copyright (c) 2022, LabN Consulting, L.L.C. +# +"""Command to execute mutests.""" + +import asyncio +import logging +import os +import subprocess +import sys +import time + +from argparse import ArgumentParser +from argparse import Namespace +from copy import deepcopy +from pathlib import Path +from typing import Union + +from munet import parser +from munet.base import Bridge +from munet.base import get_event_loop +from munet.mutest import userapi as uapi +from munet.native import L3NodeMixin +from munet.native import Munet +from munet.parser import async_build_topology +from munet.parser import get_config + + +# We want all but critical to fit in 5 characters for alignment +logging.addLevelName(logging.WARNING, "WARN") +root_logger = logging.getLogger("") +exec_formatter = logging.Formatter("%(asctime)s %(levelname)5s: %(name)s: %(message)s") + + +async def get_unet(config: dict, croot: Path, rundir: Path, unshare: bool = False): + """Create and run a new Munet topology. + + The topology is built from the given ``config`` to run inside the path indicated + by ``rundir``. If ``unshare`` is True then the process will unshare into it's + own private namespace. + + Args: + config: a config dictionary obtained from ``munet.parser.get_config``. This + value will be modified and stored in the built ``Munet`` object. + croot: common root of all tests, used to search for ``kinds.yaml`` files. + rundir: the path to the run directory for this topology. + unshare: True to unshare the process into it's own private namespace. + + Yields: + Munet: The constructed and running topology. + """ + tasks = [] + unet = None + try: + try: + unet = await async_build_topology( + config, rundir=str(rundir), unshare_inline=unshare + ) + except Exception as error: + logging.debug("unet build failed: %s", error, exc_info=True) + raise + try: + tasks = await unet.run() + except Exception as error: + logging.debug("unet run failed: %s", error, exc_info=True) + raise + logging.debug("unet topology running") + try: + yield unet + except Exception as error: + logging.error("unet fixture: yield unet unexpected exception: %s", error) + raise + except KeyboardInterrupt: + logging.info("Received keyboard while building topology") + raise + finally: + if unet: + await unet.async_delete() + + # No one ever awaits these so cancel them + logging.debug("unet fixture: cleanup") + for task in tasks: + task.cancel() + + # Reset the class variables so auto number is predictable + logging.debug("unet fixture: resetting ords to 1") + L3NodeMixin.next_ord = 1 + Bridge.next_ord = 1 + + +def common_root(path1: Union[str, Path], path2: Union[str, Path]) -> Path: + """Find the common root between 2 paths. + + Args: + path1: Path + path2: Path + Returns: + Path: the shared root components between ``path1`` and ``path2``. + + Examples: + >>> common_root("/foo/bar/baz", "/foo/bar/zip/zap") + PosixPath('/foo/bar') + >>> common_root("/foo/bar/baz", "/fod/bar/zip/zap") + PosixPath('/') + """ + apath1 = Path(path1).absolute().parts + apath2 = Path(path2).absolute().parts + alen = min(len(apath1), len(apath2)) + common = None + for a, b in zip(apath1[:alen], apath2[:alen]): + if a != b: + break + common = common.joinpath(a) if common else Path(a) + return common + + +async def collect(args: Namespace): + """Collect test files. + + Files must match the pattern ``mutest_*.py``, and their containing + directory must have a munet config file present. This function also changes + the current directory to the common parent of all the tests, and paths are + returned relative to the common directory. + + Args: + args: argparse results + + Returns: + (commondir, tests, configs): where ``commondir`` is the path representing + the common parent directory of all the testsd, ``tests`` is a + dictionary of lists of test files, keyed on their containing directory + path, and ``configs`` is a dictionary of config dictionaries also keyed + on its containing directory path. The directory paths are relative to a + common ancestor. + """ + file_select = args.file_select + upaths = args.paths if args.paths else ["."] + globpaths = set() + for upath in (Path(x) for x in upaths): + if upath.is_file(): + paths = {upath.absolute()} + else: + paths = {x.absolute() for x in Path(upath).rglob(file_select)} + globpaths |= paths + tests = {} + configs = {} + + # Find the common root + # We don't actually need this anymore, the idea was prefix test names + # with uncommon paths elements to automatically differentiate them. + common = None + sortedpaths = [] + for path in sorted(globpaths): + sortedpaths.append(path) + dirpath = path.parent + common = common_root(common, dirpath) if common else dirpath + + ocwd = Path().absolute() + try: + os.chdir(common) + # Work with relative paths to the common directory + for path in (x.relative_to(common) for x in sortedpaths): + dirpath = path.parent + if dirpath not in configs: + try: + configs[dirpath] = get_config(search=[dirpath]) + except FileNotFoundError: + logging.warning( + "Skipping '%s' as munet.{yaml,toml,json} not found in '%s'", + path, + dirpath, + ) + continue + if dirpath not in tests: + tests[dirpath] = [] + tests[dirpath].append(path.absolute()) + finally: + os.chdir(ocwd) + return common, tests, configs + + +async def execute_test( + unet: Munet, + test: Path, + args: Namespace, + test_num: int, + exec_handler: logging.Handler, +) -> (int, int, int, Exception): + """Execute a test case script. + + Using the built and running topology in ``unet`` for targets + execute the test case script file ``test``. + + Args: + unet: a running topology. + test: path to the test case script file. + args: argparse results. + test_num: the number of this test case in the run. + exec_handler: exec file handler to add to test loggers which do not propagate. + """ + test_name = testname_from_path(test) + + # Get test case loggers + logger = logging.getLogger(f"mutest.output.{test_name}") + reslog = logging.getLogger(f"mutest.results.{test_name}") + logger.addHandler(exec_handler) + reslog.addHandler(exec_handler) + + # We need to send an info level log to cause the speciifc handler to be + # created, otherwise all these debug ones don't get through + reslog.info("") + + # reslog.debug("START: %s:%s from %s", test_num, test_name, test.stem) + # reslog.debug("-" * 70) + + targets = dict(unet.hosts.items()) + targets["."] = unet + + tc = uapi.TestCase( + str(test_num), test_name, test, targets, logger, reslog, args.full_summary + ) + passed, failed, e = tc.execute() + + run_time = time.time() - tc.info.start_time + + status = "PASS" if not (failed or e) else "FAIL" + + # Turn off for now + reslog.debug("-" * 70) + reslog.debug( + "stats: %d steps, %d pass, %d fail, %s abort, %4.2fs elapsed", + passed + failed, + passed, + failed, + 1 if e else 0, + run_time, + ) + reslog.debug("-" * 70) + reslog.debug("END: %s %s:%s\n", status, test_num, test_name) + + return passed, failed, e + + +def testname_from_path(path: Path) -> str: + """Return test name based on the path to the test file. + + Args: + path: path to the test file. + + Returns: + str: the name of the test. + """ + return str(Path(path).stem).replace("/", ".") + + +def print_header(reslog, unet): + targets = dict(unet.hosts.items()) + nmax = max(len(x) for x in targets) + nmax = max(nmax, len("TARGET")) + sum_fmt = uapi.TestCase.sum_fmt.format(nmax) + reslog.info(sum_fmt, "NUMBER", "STAT", "TARGET", "TIME", "DESCRIPTION") + reslog.info("-" * 70) + + +async def run_tests(args): + reslog = logging.getLogger("mutest.results") + + common, tests, configs = await collect(args) + results = [] + errlog = logging.getLogger("mutest.error") + reslog = logging.getLogger("mutest.results") + printed_header = False + tnum = 0 + start_time = time.time() + try: + for dirpath in tests: + test_files = tests[dirpath] + for test in test_files: + tnum += 1 + config = deepcopy(configs[dirpath]) + test_name = testname_from_path(test) + rundir = args.rundir.joinpath(test_name) + + # Add an test case exec file handler to the root logger and result + # logger + exec_path = rundir.joinpath("mutest-exec.log") + exec_path.parent.mkdir(parents=True, exist_ok=True) + exec_handler = logging.FileHandler(exec_path, "w") + exec_handler.setFormatter(exec_formatter) + root_logger.addHandler(exec_handler) + + try: + async for unet in get_unet(config, common, rundir): + if not printed_header: + print_header(reslog, unet) + printed_header = True + passed, failed, e = await execute_test( + unet, test, args, tnum, exec_handler + ) + except KeyboardInterrupt as error: + errlog.warning("KeyboardInterrupt while running test %s", test_name) + passed, failed, e = 0, 0, error + raise + except Exception as error: + logging.error( + "Error executing test %s: %s", test, error, exc_info=True + ) + errlog.error( + "Error executing test %s: %s", test, error, exc_info=True + ) + passed, failed, e = 0, 0, error + finally: + # Remove the test case exec file handler form the root logger. + root_logger.removeHandler(exec_handler) + results.append((test_name, passed, failed, e)) + + except KeyboardInterrupt: + pass + + run_time = time.time() - start_time + tnum = 0 + tpassed = 0 + tfailed = 0 + texc = 0 + + spassed = 0 + sfailed = 0 + + for result in results: + _, passed, failed, e = result + tnum += 1 + spassed += passed + sfailed += failed + if e: + texc += 1 + if failed or e: + tfailed += 1 + else: + tpassed += 1 + + reslog.info("") + reslog.info( + "run stats: %s steps, %s pass, %s fail, %s abort, %4.2fs elapsed", + spassed + sfailed, + spassed, + sfailed, + texc, + run_time, + ) + reslog.info("-" * 70) + + tnum = 0 + for result in results: + test_name, passed, failed, e = result + tnum += 1 + s = "FAIL" if failed or e else "PASS" + reslog.info(" %s %s:%s", s, tnum, test_name) + + reslog.info("-" * 70) + reslog.info( + "END RUN: %s test scripts, %s passed, %s failed", tnum, tpassed, tfailed + ) + + return 1 if tfailed else 0 + + +async def async_main(args): + status = 3 + try: + # For some reson we are not catching exceptions raised inside + status = await run_tests(args) + except KeyboardInterrupt: + logging.info("Exiting (async_main), received KeyboardInterrupt in main") + except Exception as error: + logging.info( + "Exiting (async_main), unexpected exception %s", error, exc_info=True + ) + logging.debug("async_main returns %s", status) + return status + + +def main(): + ap = ArgumentParser() + ap.add_argument( + "--dist", + type=int, + nargs="?", + const=-1, + default=0, + action="store", + metavar="NUM-THREADS", + help="Run in parallel, value is num. of threads or no value for auto", + ) + ap.add_argument("-d", "--rundir", help="runtime directory for tempfiles, logs, etc") + ap.add_argument( + "--file-select", default="mutest_*.py", help="shell glob for finding tests" + ) + ap.add_argument("--log-config", help="logging config file (yaml, toml, json, ...)") + ap.add_argument( + "-V", + "--full-summary", + action="store_true", + help="print full summary headers from docstrings", + ) + ap.add_argument( + "-v", dest="verbose", action="count", default=0, help="More -v's, more verbose" + ) + ap.add_argument("paths", nargs="*", help="Paths to collect tests from") + args = ap.parse_args() + + rundir = args.rundir if args.rundir else "/tmp/mutest" + args.rundir = Path(rundir) + os.environ["MUNET_RUNDIR"] = rundir + subprocess.run(f"mkdir -p {rundir} && chmod 755 {rundir}", check=True, shell=True) + + config = parser.setup_logging(args, config_base="logconf-mutest") + # Grab the exec formatter from the logging config + if fconfig := config.get("formatters", {}).get("exec"): + global exec_formatter # pylint: disable=W291,W0603 + exec_formatter = logging.Formatter( + fconfig.get("format"), fconfig.get("datefmt") + ) + + loop = None + status = 4 + try: + loop = get_event_loop() + status = loop.run_until_complete(async_main(args)) + except KeyboardInterrupt: + logging.info("Exiting (main), received KeyboardInterrupt in main") + except Exception as error: + logging.info("Exiting (main), unexpected exception %s", error, exc_info=True) + finally: + if loop: + loop.close() + + sys.exit(status) + + +if __name__ == "__main__": + main() diff --git a/tests/topotests/munet/mutest/userapi.py b/tests/topotests/munet/mutest/userapi.py new file mode 100644 index 0000000000..1df8c0d012 --- /dev/null +++ b/tests/topotests/munet/mutest/userapi.py @@ -0,0 +1,1111 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright 2017, 2022, LabN Consulting, L.L.C. +"""Mutest is a simple send/expect based testing framework. + +This module implements the basic send/expect functionality for mutest. The test +developer first creates a munet topology (:ref:`munet-config`) and then writes test +scripts ("test cases") which are composed of calls to the functions defined below +("steps"). In short these are: + +Send/Expect functions: + + - :py:func:`step` + + - :py:func:`step_json` + + - :py:func:`match_step` + + - :py:func:`match_step_json` + + - :py:func:`wait_step` + + - :py:func:`wait_step_json` + +Control/Utility functions: + + - :py:func:`script_dir` + + - :py:func:`include` + + - :py:func:`log` + + - :py:func:`test` + +Test scripts are located by the :command:`mutest` command by their name. The name of a +test script should take the form ``mutest_TESTNAME.py`` where ``TESTNAME`` is replaced +with a user chosen name for the test case. + +Here's a simple example test script which first checks that a specific forwarding entry +is in the FIB for the IP destination ``10.0.1.1``. Then it checks repeatedly for up to +10 seconds for a second forwarding entry in the FIB for the IP destination ``10.0.2.1``. + +.. code-block:: python + + match_step("r1", 'vtysh -c "show ip fib 10.0.1.1"', "Routing entry for 10.0.1.0/24", + "Check for FIB entry for 10.0.1.1") + + wait_step("r1", + 'vtysh -c "show ip fib 10.0.2.1"', + "Routing entry for 10.0.2.0/24", + desc="Check for FIB entry for 10.0.2.1", + timeout=10) + +Notice that the call arguments can be specified by their correct position in the list or +using keyword names, and they can also be specified over multiple lines if preferred. + +All of the functions are documented and defined below. +""" + +# pylint: disable=global-statement + +import functools +import json +import logging +import pprint +import re +import time + +from pathlib import Path +from typing import Any +from typing import Union + +from deepdiff import DeepDiff as json_cmp + +from munet.base import Commander + + +class TestCaseInfo: + """Object to hold nestable TestCase Results.""" + + def __init__(self, tag: str, name: str, path: Path): + self.path = path.absolute() + self.tag = tag + self.name = name + self.steps = 0 + self.passed = 0 + self.failed = 0 + self.start_time = time.time() + self.step_start_time = self.start_time + self.run_time = None + + def __repr__(self): + return ( + f"TestCaseInfo({self.tag} {self.name} steps {self.steps} " + f"p {self.passed} f {self.failed} path {self.path})" + ) + + +class TestCase: + """A mutest testcase. + + This is normally meant to be used internally by the mutest command to + implement the user API. See README-mutest.org for usage details on the + user API. + + Args: + tag: identity of the test in a run. (x.x...) + name: the name of the test case + path: the test file that is being executed. + targets: a dictionary of objects which implement ``cmd_nostatus(str)`` + output_logger: a logger for output and other messages from the test. + result_logger: a logger to output the results of test steps to. + full_summary: if True then print entire doctstring instead of + only the first line in the results report + + Attributes: + tag: identity of the test in a run + name: the name of the test + targets: dictionary of targets. + + steps: total steps executed so far. + passed: number of passing steps. + failed: number of failing steps. + + last: the last command output. + last_m: the last result of re.search during a matching step on the output with + newlines converted to spaces. + + :meta private: + """ + + # sum_hfmt = "{:5.5s} {:4.4s} {:>6.6s} {}" + # sum_dfmt = "{:5s} {:4.4s} {:^6.6s} {}" + sum_fmt = "%-8.8s %4.4s %{}s %6s %s" + + def __init__( + self, + tag: int, + name: str, + path: Path, + targets: dict, + output_logger: logging.Logger = None, + result_logger: logging.Logger = None, + full_summary: bool = False, + ): + + self.info = TestCaseInfo(tag, name, path) + self.__saved_info = [] + self.__short_doc_header = not full_summary + + self.__space_before_result = False + + # we are only ever in a section once, an include ends a section + # so are never in section+include, and another section ends a + # section, so we don't need __in_section to be save in the + # TestCaseInfo struct. + self.__in_section = False + + self.targets = targets + + self.last = "" + self.last_m = None + + self.rlog = result_logger + self.olog = output_logger + self.logf = functools.partial(self.olog.log, logging.INFO) + + oplog = logging.getLogger("mutest.oper") + self.oplogf = oplog.debug + self.oplogf("new TestCase: tag: %s name: %s path: %s", tag, name, path) + + # find the longerst target name and make target field that wide + nmax = max(len(x) for x in targets) + nmax = max(nmax, len("TARGET")) + self.sum_fmt = TestCase.sum_fmt.format(nmax) + + # Let's keep this out of summary for now + self.rlog.debug(self.sum_fmt, "NUMBER", "STAT", "TARGET", "TIME", "DESCRIPTION") + self.rlog.debug("-" * 70) + + @property + def tag(self): + return self.info.tag + + @property + def name(self): + return self.info.name + + @property + def steps(self): + return self.info.steps + + @property + def passed(self): + return self.info.passed + + @property + def failed(self): + return self.info.failed + + def execute(self): + """Execute the test case. + + :meta private: + """ + assert TestCase.g_tc is None + self.oplogf("execute") + try: + TestCase.g_tc = self + e = self.__exec_script(self.info.path, True, False) + except BaseException: + self.__end_test() + raise + return *self.__end_test(), e + + def __del__(self): + if TestCase.g_tc is self: + logging.error("Internal error, TestCase.__end_test() was not called!") + TestCase.g_tc = None + + def __push_execinfo(self, path: Path): + self.oplogf( + "__push_execinfo: path: %s current top is %s", + path, + pprint.pformat(self.info), + ) + newname = self.name + path.stem + self.info.steps += 1 + self.__saved_info.append(self.info) + tag = f"{self.info.tag}.{self.info.steps}" + self.info = TestCaseInfo(tag, newname, path) + self.oplogf("__push_execinfo: now on top: %s", pprint.pformat(self.info)) + + def __pop_execinfo(self): + # do something with tag? + finished_info = self.info + self.info = self.__saved_info.pop() + self.oplogf(" __pop_execinfo: poppped: %s", pprint.pformat(finished_info)) + self.oplogf(" __pop_execinfo: now on top: %s", pprint.pformat(self.info)) + return finished_info + + def __print_header(self, tag, header, add_newline=False): + # self.olog.info(self.sum_fmt, tag, "", "", "", header) + self.olog.info("== %s ==", f"TEST: {tag}. {header}") + if add_newline: + self.rlog.info("") + self.rlog.info("%s. %s", tag, header) + + def __exec_script(self, path, print_header, add_newline): + + # Below was the original method to avoid the global TestCase + # variable; however, we need global functions so we can import them + # into test scripts. Without imports pylint will complain about undefined + # functions and the resulting christmas tree of warnings is annoying. + # + # pylint: disable=possibly-unused-variable,exec-used,redefined-outer-name + # include = self.include + # log = self.logf + # match_step = self.match_step + # match_step_json = self.match_step_json + # step = self.step + # step_json = self.step_json + # test = self.test + # wait_step = self.wait_step + # wait_step_json = self.wait_step_json + + name = f"{path.stem}{self.tag}" + name = re.sub(r"\W|^(?=\d)", "_", name) + + _ok_result = "marker" + try: + self.oplogf("__exec_script: path %s", path) + script = open(path, "r", encoding="utf-8").read() + + # Load the script into a function. + script = script.strip() + s2 = ( + # f"async def _{name}(ok_result):\n" + f"def _{name}(ok_result):\n" + + " " + + script.replace("\n", "\n ") + + "\n return ok_result\n" + + "\n" + ) + exec(s2) + + # Extract any docstring as a title. + if print_header: + title = locals()[f"_{name}"].__doc__.lstrip() + if self.__short_doc_header and (title := title.lstrip()): + if (idx := title.find("\n")) != -1: + title = title[:idx].strip() + if not title: + title = f"Test from file: {self.info.path.name}" + self.__print_header(self.info.tag, title, add_newline) + self.__space_before_result = False + + # Execute the function. + result = locals()[f"_{name}"](_ok_result) + + # Here's where we can do async in the future if we want. + # result = await locals()[f"_{name}"](_ok_result) + except Exception as error: + logging.error( + "Unexpected exception executing %s: %s", name, error, exc_info=True + ) + return error + else: + if result is not _ok_result: + logging.info("%s returned early, result: %s", name, result) + else: + self.oplogf("__exec_script: name %s completed normally", name) + return None + + def __post_result(self, target, success, rstr, logstr=None): + self.oplogf( + "__post_result: target: %s success %s rstr %s", target, success, rstr + ) + if success: + self.info.passed += 1 + status = "PASS" + outlf = self.logf + reslf = self.rlog.info + else: + self.info.failed += 1 + status = "FAIL" + outlf = self.olog.warning + reslf = self.rlog.warning + + self.info.steps += 1 + if logstr is not None: + outlf("R:%d %s: %s" % (self.steps, status, logstr)) + + run_time = time.time() - self.info.step_start_time + + stepstr = f"{self.tag}.{self.steps}" + rtimes = _delta_time_str(run_time) + + if self.__space_before_result: + self.rlog.info("") + self.__space_before_result = False + + reslf(self.sum_fmt, stepstr, status, target, rtimes, rstr) + + # start counting for next step now + self.info.step_start_time = time.time() + + def __end_test(self) -> (int, int): + """End the test log final results. + + Returns: + number of steps, number passed, number failed, run time. + """ + self.oplogf("__end_test: __in_section: %s", self.__in_section) + if self.__in_section: + self.__end_section() + + passed, failed = self.info.passed, self.info.failed + + # No close for loggers + # self.olog.close() + # self.rlog.close() + self.olog = None + self.rlog = None + + assert ( + TestCase.g_tc == self + ), "TestCase global unexpectedly someon else in __end_test" + TestCase.g_tc = None + + self.info.run_time = time.time() - self.info.start_time + return passed, failed + + def _command( + self, + target: str, + cmd: str, + ) -> str: + """Execute a ``cmd`` and return result. + + Args: + target: the target to execute the command on. + cmd: string to execut on the target. + """ + out = self.targets[target].cmd_nostatus(cmd, warn=False) + self.last = out = out.rstrip() + report = out if out else "<no output>" + self.logf("COMMAND OUTPUT:\n%s", report) + return out + + def _command_json( + self, + target: str, + cmd: str, + ) -> dict: + """Execute a json ``cmd`` and return json result. + + Args: + target: the target to execute the command on. + cmd: string to execut on the target. + """ + out = self.targets[target].cmd_nostatus(cmd, warn=False) + self.last = out = out.rstrip() + try: + js = json.loads(out) + except Exception as error: + js = {} + self.olog.warning( + "JSON load failed. Check command output is in JSON format: %s", + error, + ) + self.logf("COMMAND OUTPUT:\n%s", out) + return js + + def _match_command( + self, + target: str, + cmd: str, + match: str, + expect_fail: bool, + flags: int, + ) -> (bool, Union[str, list]): + """Execute a ``cmd`` and check result. + + Args: + target: the target to execute the command on. + cmd: string to execute on the target. + match: regex to ``re.search()`` for in output. + expect_fail: if True then succeed when the regexp doesn't match. + flags: python regex flags to modify matching behavior + + Returns: + (success, matches): if the match fails then "matches" will be None, + otherwise if there were matching groups then groups() will be returned in + ``matches`` otherwise group(0) (i.e., the matching text). + """ + out = self._command(target, cmd) + search = re.search(match, out, flags) + self.last_m = search + if search is None: + success = expect_fail + ret = None + else: + success = not expect_fail + ret = search.groups() + if not ret: + ret = search.group(0) + + level = logging.DEBUG if success else logging.WARNING + self.olog.log(level, "matched:%s:", ret) + return success, ret + + def _match_command_json( + self, + target: str, + cmd: str, + match: Union[str, dict], + expect_fail: bool, + ) -> Union[str, dict]: + """Execute a json ``cmd`` and check result. + + Args: + target: the target to execute the command on. + cmd: string to execut on the target. + match: A json ``str`` or object (``dict``) to compare against the json + output from ``cmd``. + expect_fail: if True then succeed when the json doesn't match. + """ + js = self._command_json(target, cmd) + try: + expect = json.loads(match) + except Exception as error: + expect = {} + self.olog.warning( + "JSON load failed. Check match value is in JSON format: %s", error + ) + + if json_diff := json_cmp(expect, js): + success = expect_fail + if not success: + self.logf("JSON DIFF:%s:" % json_diff) + return success, json_diff + + success = not expect_fail + return success, js + + def _wait( + self, + target: str, + cmd: str, + match: Union[str, dict], + is_json: bool, + timeout: float, + interval: float, + expect_fail: bool, + flags: int, + ) -> Union[str, dict]: + """Execute a command repeatedly waiting for result until timeout.""" + startt = time.time() + endt = startt + timeout + + success = False + ret = None + while not success and time.time() < endt: + if is_json: + success, ret = self._match_command_json(target, cmd, match, expect_fail) + else: + success, ret = self._match_command( + target, cmd, match, expect_fail, flags + ) + if not success: + time.sleep(interval) + return success, ret + + # --------------------- + # Public APIs for User + # --------------------- + + def include(self, pathname: str, new_section: bool = False): + """See :py:func:`~munet.mutest.userapi.include`. + + :meta private: + """ + path = Path(pathname) + path = self.info.path.parent.joinpath(path) + + self.oplogf( + "include: new path: %s create section: %s currently __in_section: %s", + path, + new_section, + self.__in_section, + ) + + if new_section: + self.oplogf("include: starting new exec section") + self.__start_exec_section(path) + our_info = self.info + # Note we do *not* mark __in_section True + else: + # swap the current path inside the top info + old_path = self.info.path + self.info.path = path + self.oplogf("include: swapped info path: new %s old %s", path, old_path) + + self.__exec_script(path, print_header=new_section, add_newline=new_section) + + if new_section: + # Something within the section creating include has also created a section + # end it, sections do not cross section creating file boundaries + if self.__in_section: + self.oplogf( + "include done: path: %s __in_section calling __end_section", path + ) + self.__end_section() + + # We should now be back to the info we started with, b/c we don't actually + # start a new section (__in_section) that then could have been ended inside + # the included file. + assert our_info == self.info + + self.oplogf( + "include done: path: %s new_section calling __end_section", path + ) + self.__end_section() + else: + # The current top path could be anything due to multiple inline includes as + # well as section swap in and out. Forcibly return the top path to the file + # we are returning to + self.info.path = old_path + self.oplogf("include: restored info path: %s", old_path) + + def __end_section(self): + self.oplogf("__end_section: __in_section: %s", self.__in_section) + info = self.__pop_execinfo() + passed, failed = info.passed, info.failed + self.info.passed += passed + self.info.failed += failed + self.__space_before_result = True + self.oplogf("__end_section setting __in_section to False") + self.__in_section = False + + def __start_exec_section(self, path): + self.oplogf("__start_exec_section: __in_section: %s", self.__in_section) + if self.__in_section: + self.__end_section() + + self.__push_execinfo(path) + self.__space_before_result = False + self.oplogf("NOT setting __in_section to True") + assert not self.__in_section + + def section(self, desc: str): + """See :py:func:`~munet.mutest.userapi.section`. + + :meta private: + """ + self.oplogf("section: __in_section: %s", self.__in_section) + # Grab path before we pop the current info off the top + path = self.info.path + old_steps = self.info.steps + + if self.__in_section: + self.__end_section() + + self.__push_execinfo(path) + add_nl = self.info.steps <= old_steps + + self.__space_before_result = False + self.__in_section = True + self.oplogf(" section setting __in_section to True") + self.__print_header(self.info.tag, desc, add_nl) + + def step(self, target: str, cmd: str) -> str: + """See :py:func:`~munet.mutest.userapi.step`. + + :meta private: + """ + self.logf( + "#%s.%s:%s:STEP:%s:%s", + self.tag, + self.steps + 1, + self.info.path, + target, + cmd, + ) + return self._command(target, cmd) + + def step_json(self, target: str, cmd: str) -> dict: + """See :py:func:`~munet.mutest.userapi.step_json`. + + :meta private: + """ + self.logf( + "#%s.%s:%s:STEP_JSON:%s:%s", + self.tag, + self.steps + 1, + self.info.path, + target, + cmd, + ) + return self._command_json(target, cmd) + + def match_step( + self, + target: str, + cmd: str, + match: str, + desc: str = "", + expect_fail: bool = False, + flags: int = re.DOTALL, + ) -> (bool, Union[str, list]): + """See :py:func:`~munet.mutest.userapi.match_step`. + + :meta private: + """ + self.logf( + "#%s.%s:%s:MATCH_STEP:%s:%s:%s:%s:%s:%s", + self.tag, + self.steps + 1, + self.info.path, + target, + cmd, + match, + desc, + expect_fail, + flags, + ) + success, ret = self._match_command(target, cmd, match, expect_fail, flags) + if desc: + self.__post_result(target, success, desc) + return success, ret + + def test_step(self, expr_or_value: Any, desc: str, target: str = "") -> bool: + """See :py:func:`~munet.mutest.userapi.test`. + + :meta private: + """ + success = bool(expr_or_value) + self.__post_result(target, success, desc) + return success + + def match_step_json( + self, + target: str, + cmd: str, + match: Union[str, dict], + desc: str = "", + expect_fail: bool = False, + ) -> (bool, Union[str, dict]): + """See :py:func:`~munet.mutest.userapi.match_step_json`. + + :meta private: + """ + self.logf( + "#%s.%s:%s:MATCH_STEP_JSON:%s:%s:%s:%s:%s", + self.tag, + self.steps + 1, + self.info.path, + target, + cmd, + match, + desc, + expect_fail, + ) + success, ret = self._match_command_json(target, cmd, match, expect_fail) + if desc: + self.__post_result(target, success, desc) + return success, ret + + def wait_step( + self, + target: str, + cmd: str, + match: Union[str, dict], + desc: str = "", + timeout=10, + interval=0.5, + expect_fail: bool = False, + flags: int = re.DOTALL, + ) -> (bool, Union[str, list]): + """See :py:func:`~munet.mutest.userapi.wait_step`. + + :meta private: + """ + if interval is None: + interval = min(timeout / 20, 0.25) + self.logf( + "#%s.%s:%s:WAIT_STEP:%s:%s:%s:%s:%s:%s:%s:%s", + self.tag, + self.steps + 1, + self.info.path, + target, + cmd, + match, + timeout, + interval, + desc, + expect_fail, + flags, + ) + success, ret = self._wait( + target, cmd, match, False, timeout, interval, expect_fail, flags + ) + if desc: + self.__post_result(target, success, desc) + return success, ret + + def wait_step_json( + self, + target: str, + cmd: str, + match: Union[str, dict], + desc: str = "", + timeout=10, + interval=None, + expect_fail: bool = False, + ) -> (bool, Union[str, dict]): + """See :py:func:`~munet.mutest.userapi.wait_step_json`. + + :meta private: + """ + if interval is None: + interval = min(timeout / 20, 0.25) + self.logf( + "#%s.%s:%s:WAIT_STEP:%s:%s:%s:%s:%s:%s:%s", + self.tag, + self.steps + 1, + self.info.path, + target, + cmd, + match, + timeout, + interval, + desc, + expect_fail, + ) + success, ret = self._wait( + target, cmd, match, True, timeout, interval, expect_fail, 0 + ) + if desc: + self.__post_result(target, success, desc) + return success, ret + + +# A non-rentrant global to allow for simplified operations +TestCase.g_tc = None + +# pylint: disable=protected-access + + +def _delta_time_str(run_time: float) -> str: + if run_time < 0.0001: + return "0.0" + if run_time < 0.001: + return f"{run_time:1.4f}" + if run_time < 0.01: + return f"{run_time:2.3f}" + if run_time < 0.1: + return f"{run_time:3.2f}" + if run_time < 100: + return f"{run_time:4.1f}" + return f"{run_time:5f}s" + + +def section(desc: str): + """Start a new section for steps, with a description. + + This starts a new section of tests. The result is basically + the same as doing a non-inline include. The current test number + is used to form a new sub-set of test steps. So if the current + test number is 2.3, a section will now number subsequent steps + 2.3.1, 2.3.2, ... + + A subsequent :py:func:`section` or non-inline :py:func:`include` + call ends the current section and advances the base test number. + + Args: + desc: the description for the new section. + """ + TestCase.g_tc.section(desc) + + +def log(fmt, *args, **kwargs): + """Log a message in the testcase output log.""" + return TestCase.g_tc.logf(fmt, *args, **kwargs) + + +def include(pathname: str, new_section=False): + """Include a file as part of testcase. + + Args: + pathname: the file to include. + new_section: if a new section should be created, otherwise + commands are executed inline. + """ + return TestCase.g_tc.include(pathname, new_section) + + +def script_dir() -> Path: + """The pathname to the directory containing the current script file. + + When an include() is called the script_dir is updated to be current with the + includeded file, and is reverted to the previous value when the include completes. + """ + return TestCase.g_tc.info.path.parent + + +def get_target(name: str) -> Commander: + """Get the target object with the given ``name``.""" + return TestCase.g_tc.targets[name] + + +def step(target: str, cmd: str) -> str: + """Execute a ``cmd`` on a ``target`` and return the output. + + Args: + target: the target to execute the ``cmd`` on. + cmd: string to execute on the target. + + Returns: + Returns the ``str`` output of the ``cmd``. + """ + return TestCase.g_tc.step(target, cmd) + + +def step_json(target: str, cmd: str) -> dict: + """Execute a json ``cmd`` on a ``target`` and return the json object. + + Args: + target: the target to execute the ``cmd`` on. + cmd: string to execute on the target. + + Returns: + Returns the json object after parsing the ``cmd`` output. + + If json parse fails, a warning is logged and an empty ``dict`` is used. + """ + return TestCase.g_tc.step_json(target, cmd) + + +def test_step(expr_or_value: Any, desc: str, target: str = "") -> bool: + """Evaluates ``expr_or_value`` and posts a result base on it bool(expr). + + If ``expr_or_value`` evaluates to a positive result (i.e., True, non-zero, non-None, + non-empty string, non-empty list, etc..) then a PASS result is recorded, otherwise + record a FAIL is recorded. + + Args: + expr: an expression or value to evaluate + desc: description of this test step. + target: optional target to associate with this test in the result string. + + Returns: + A bool indicating the test PASS or FAIL result. + """ + return TestCase.g_tc.test_step(expr_or_value, desc, target) + + +def match_step( + target: str, + cmd: str, + match: str, + desc: str = "", + expect_fail: bool = False, + flags: int = re.DOTALL, +) -> (bool, Union[str, list]): + """Execute a ``cmd`` on a ``target`` check result. + + Execute ``cmd`` on ``target`` and check if the regexp in ``match`` + matches or doesn't match (according to the ``expect_fail`` value) the + ``cmd`` output. + + If the ``match`` regexp includes groups and if the match succeeds + the group values will be returned in a list, otherwise the command + output is returned. + + Args: + target: the target to execute the ``cmd`` on. + cmd: string to execut on the ``target``. + match: regex to match against output. + desc: description of test, if no description then no result is logged. + expect_fail: if True then succeed when the regexp doesn't match. + flags: python regex flags to modify matching behavior + + Returns: + Returns a 2-tuple. The first value is a bool indicating ``success``. + The second value will be a list from ``re.Match.groups()`` if non-empty, + otherwise ``re.Match.group(0)`` if there was a match otherwise None. + """ + return TestCase.g_tc.match_step(target, cmd, match, desc, expect_fail, flags) + + +def match_step_json( + target: str, + cmd: str, + match: Union[str, dict], + desc: str = "", + expect_fail: bool = False, +) -> (bool, Union[str, dict]): + """Execute a ``cmd`` on a ``target`` check result. + + Execute ``cmd`` on ``target`` and check if the json object in ``match`` + matches or doesn't match (according to the ``expect_fail`` value) the + json output from ``cmd``. + + Args: + target: the target to execute the ``cmd`` on. + cmd: string to execut on the ``target``. + match: A json ``str`` or object (``dict``) to compare against the json + output from ``cmd``. + desc: description of test, if no description then no result is logged. + expect_fail: if True then succeed if the a json doesn't match. + + Returns: + Returns a 2-tuple. The first value is a bool indicating ``success``. The + second value is a ``str`` diff if there is a difference found in the json + compare, otherwise the value is the json object (``dict``) from the ``cmd``. + + If json parse fails, a warning is logged and an empty ``dict`` is used. + """ + return TestCase.g_tc.match_step_json(target, cmd, match, desc, expect_fail) + + +def wait_step( + target: str, + cmd: str, + match: Union[str, dict], + desc: str = "", + timeout: float = 10.0, + interval: float = 0.5, + expect_fail: bool = False, + flags: int = re.DOTALL, +) -> (bool, Union[str, list]): + """Execute a ``cmd`` on a ``target`` repeatedly, looking for a result. + + Execute ``cmd`` on ``target``, every ``interval`` seconds for up to ``timeout`` + seconds until the output of ``cmd`` does or doesn't match (according to the + ``expect_fail`` value) the ``match`` value. + + Args: + target: the target to execute the ``cmd`` on. + cmd: string to execut on the ``target``. + match: regexp to match against output. + timeout: The number of seconds to repeat the ``cmd`` looking for a match + (or non-match if ``expect_fail`` is True). + interval: The number of seconds between running the ``cmd``. If not + specified the value is calculated from the timeout value so that on + average the cmd will execute 10 times. The minimum calculated interval + is .25s, shorter values can be passed explicitly. + desc: description of test, if no description then no result is logged. + expect_fail: if True then succeed when the regexp *doesn't* match. + flags: python regex flags to modify matching behavior + + Returns: + Returns a 2-tuple. The first value is a bool indicating ``success``. + The second value will be a list from ``re.Match.groups()`` if non-empty, + otherwise ``re.Match.group(0)`` if there was a match otherwise None. + """ + return TestCase.g_tc.wait_step( + target, cmd, match, desc, timeout, interval, expect_fail, flags + ) + + +def wait_step_json( + target: str, + cmd: str, + match: Union[str, dict], + desc: str = "", + timeout=10, + interval=None, + expect_fail: bool = False, +) -> (bool, Union[str, dict]): + """Execute a cmd repeatedly and wait for matching result. + + Execute ``cmd`` on ``target``, every ``interval`` seconds until + the output of ``cmd`` matches or doesn't match (according to the + ``expect_fail`` value) ``match``, for up to ``timeout`` seconds. + + ``match`` is a regular expression to search for in the output of ``cmd`` when + ``is_json`` is False. + + When ``is_json`` is True ``match`` must be a json object or a ``str`` which + parses into a json object. Likewise, the ``cmd`` output is parsed into a json + object and then a comparison is done between the two json objects. + + Args: + target: the target to execute the ``cmd`` on. + cmd: string to execut on the ``target``. + match: A json object or str representation of one to compare against json + output from ``cmd``. + desc: description of test, if no description then no result is logged. + timeout: The number of seconds to repeat the ``cmd`` looking for a match + (or non-match if ``expect_fail`` is True). + interval: The number of seconds between running the ``cmd``. If not + specified the value is calculated from the timeout value so that on + average the cmd will execute 10 times. The minimum calculated interval + is .25s, shorter values can be passed explicitly. + expect_fail: if True then succeed if the a json doesn't match. + + Returns: + Returns a 2-tuple. The first value is a bool indicating ``success``. + The second value is a ``str`` diff if there is a difference found in the + json compare, otherwise the value is a json object (dict) from the ``cmd`` + output. + + If json parse fails, a warning is logged and an empty ``dict`` is used. + """ + return TestCase.g_tc.wait_step_json( + target, cmd, match, desc, timeout, interval, expect_fail + ) + + +def luInclude(filename, CallOnFail=None): + """Backward compatible API, do not use in new tests.""" + return include(filename) + + +def luLast(usenl=False): + """Backward compatible API, do not use in new tests.""" + del usenl + return TestCase.g_tc.last_m + + +def luCommand( + target, + cmd, + regexp=".", + op="none", + result="", + ltime=10, + returnJson=False, + wait_time=0.5, +): + """Backward compatible API, do not use in new tests. + + Only non-json is verified to any degree of confidence by code inspection. + + For non-json should return match.group() if match else return bool(op == "fail"). + + For json if no diff return the json else diff return bool(op == "jsoncmp_fail") + bug if no json from output (fail parse) could maybe generate diff, which could + then return + """ + if op == "wait": + if returnJson: + return wait_step_json(target, cmd, regexp, result, ltime, wait_time) + + success, _ = wait_step(target, cmd, regexp, result, ltime, wait_time) + match = luLast() + if success and match is not None: + return match.group() + return success + + if op == "none": + if returnJson: + return step_json(target, cmd) + return step(target, cmd) + + if returnJson and op in ("jsoncmp_fail", "jsoncmp_pass"): + expect_fail = op == "jsoncmp_fail" + return match_step_json(target, cmd, regexp, result, expect_fail) + + assert not returnJson + assert op in ("fail", "pass") + expect_fail = op == "fail" + success, _ = match_step(target, cmd, regexp, result, expect_fail) + match = luLast() + if success and match is not None: + return match.group() + return success diff --git a/tests/topotests/munet/mutestshare.py b/tests/topotests/munet/mutestshare.py new file mode 100644 index 0000000000..95ffa74e7b --- /dev/null +++ b/tests/topotests/munet/mutestshare.py @@ -0,0 +1,254 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# January 28 2023, Christian Hopps <chopps@labn.net> +# +# Copyright (c) 2023, LabN Consulting, L.L.C. +# +"""A tiny init for namespaces in python inspired by the C program tini.""" +import argparse +import errno +import logging +import os +import shlex +import signal +import subprocess +import sys +import threading +import time + +from signal import Signals as S + +from . import linux +from .base import commander + + +child_pid = -1 +very_verbose = False +restore_signals = set() + + +def vdebug(*args, **kwargs): + if very_verbose: + logging.debug(*args, **kwargs) + + +def exit_with_status(pid, status): + try: + ec = status >> 8 if bool(status & 0xFF00) else status | 0x80 + logging.debug("reaped our child, exiting %s", ec) + sys.exit(ec) + except ValueError: + vdebug("pid %s didn't actually exit", pid) + + +def waitpid(tag): + logging.debug("%s: waitid for exiting processes", tag) + idobj = os.waitid(os.P_ALL, 0, os.WEXITED) + pid = idobj.si_pid + status = idobj.si_status + if pid == child_pid: + exit_with_status(pid, status) + else: + logging.debug("%s: reaped zombie pid %s with status %s", tag, pid, status) + + +def new_process_group(): + pid = os.getpid() + try: + pgid = os.getpgrp() + if pgid == pid: + logging.debug("already process group leader %s", pgid) + else: + logging.debug("creating new process group %s", pid) + os.setpgid(pid, 0) + except Exception as error: + logging.warning("unable to get new process group: %s", error) + return + + # Block these in order to allow foregrounding, otherwise we'd get SIGTTOU blocked + signal.signal(S.SIGTTIN, signal.SIG_IGN) + signal.signal(S.SIGTTOU, signal.SIG_IGN) + fd = sys.stdin.fileno() + if not os.isatty(fd): + logging.debug("stdin not a tty no foregrounding required") + else: + try: + # This will error if our session no longer associated with controlling tty. + pgid = os.tcgetpgrp(fd) + if pgid == pid: + logging.debug("process group already in foreground %s", pgid) + else: + logging.debug("making us the foreground pgid backgrounding %s", pgid) + os.tcsetpgrp(fd, pid) + except OSError as error: + if error.errno == errno.ENOTTY: + logging.debug("session is no longer associated with controlling tty") + else: + logging.warning("unable to foreground pgid %s: %s", pid, error) + signal.signal(S.SIGTTIN, signal.SIG_DFL) + signal.signal(S.SIGTTOU, signal.SIG_DFL) + + +def exec_child(exec_args): + # Restore signals to default handling: + for snum in restore_signals: + signal.signal(snum, signal.SIG_DFL) + + # Create new process group. + new_process_group() + + estring = shlex.join(exec_args) + try: + # and exec the process + logging.debug("child: executing '%s'", estring) + os.execvp(exec_args[0], exec_args) + # NOTREACHED + except Exception as error: + logging.warning("child: unable to execute '%s': %s", estring, error) + raise + + +def is_creating_pid_namespace(): + p1name = subprocess.check_output( + "readlink /proc/self/pid", stderr=subprocess.STDOUT, shell=True + ) + p2name = subprocess.check_output( + "readlink /proc/self/pid_for_children", stderr=subprocess.STDOUT, shell=True + ) + return p1name != p2name + + +def restore_namespace(ppid_fd, uflags): + fd = ppid_fd + retry = 3 + for i in range(0, retry): + try: + linux.setns(fd, uflags) + except OSError as error: + logging.warning("could not reset to old namespace fd %s: %s", fd, error) + if i == retry - 1: + raise + time.sleep(1) + os.close(fd) + + +def create_thread_test(): + def runthread(name): + logging.info("In thread: %s", name) + + logging.info("Create thread") + thread = threading.Thread(target=runthread, args=(1,)) + logging.info("Run thread") + thread.start() + logging.info("Join thread") + thread.join() + + +def run(args): + del args + # We look for this b/c the unshare pid will share with /sibn/init + # nselm = "pid_for_children" + # nsflags.append(f"--pid={pp / nselm}") + # mutini now forks when created this way + # cmd.append("--pid") + # cmd.append("--fork") + # cmd.append("--kill-child") + # cmd.append("--mount-proc") + + uflags = linux.CLONE_NEWPID + nslist = ["pid_for_children"] + uflags |= linux.CLONE_NEWNS + nslist.append("mnt") + uflags |= linux.CLONE_NEWNET + nslist.append("net") + + # Before values + pid = os.getpid() + nsdict = {x: os.readlink(f"/tmp/mu-global-proc/{pid}/ns/{x}") for x in nslist} + + # + # UNSHARE + # + create_thread_test() + + ppid = os.getppid() + ppid_fd = linux.pidfd_open(ppid) + linux.unshare(uflags) + + # random syscall's fail until we fork a child to establish the new pid namespace. + global child_pid # pylint: disable=global-statement + child_pid = os.fork() + if not child_pid: + logging.info("In child sleeping") + time.sleep(1200) + sys.exit(1) + + # verify after values differ + nnsdict = {x: os.readlink(f"/tmp/mu-global-proc/{pid}/ns/{x}") for x in nslist} + assert not {k for k in nsdict if nsdict[k] == nnsdict[k]} + + # Remount / and any future mounts below it as private + commander.cmd_raises("mount --make-rprivate /") + # Mount a new /proc in our new namespace + commander.cmd_raises("mount -t proc proc /proc") + + # + # In NEW NS + # + + cid = os.fork() + if not cid: + logging.info("In second child sleeping") + time.sleep(4) + sys.exit(1) + logging.info("Waiting for second child") + os.waitpid(cid, 0) + + try: + create_thread_test() + except Exception as error: + print(error) + + # + # RESTORE + # + + logging.info("In new namespace, restoring old") + # Make sure we can go back, not sure since this is PID namespace, but maybe + restore_namespace(ppid_fd, uflags) + + # verify after values the same + nnsdict = {x: os.readlink(f"/proc/self/ns/{x}") for x in nslist} + assert nsdict == nnsdict + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument( + "-v", dest="verbose", action="count", default=0, help="More -v's, more verbose" + ) + ap.add_argument("rest", nargs=argparse.REMAINDER) + args = ap.parse_args() + + level = logging.DEBUG if args.verbose else logging.INFO + if args.verbose > 1: + global very_verbose # pylint: disable=global-statement + very_verbose = True + logging.basicConfig( + level=level, format="%(asctime)s mutini: %(levelname)s: %(message)s" + ) + + status = 4 + try: + run(args) + except KeyboardInterrupt: + logging.info("exiting (main), received KeyboardInterrupt in main") + except Exception as error: + logging.info("exiting (main), unexpected exception %s", error, exc_info=True) + + sys.exit(status) + + +if __name__ == "__main__": + main() diff --git a/tests/topotests/munet/mutini.py b/tests/topotests/munet/mutini.py new file mode 100755 index 0000000000..e805f3245d --- /dev/null +++ b/tests/topotests/munet/mutini.py @@ -0,0 +1,428 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# January 28 2023, Christian Hopps <chopps@labn.net> +# +# Copyright (c) 2023, LabN Consulting, L.L.C. +# +"""A tiny init for namespaces in python inspired by the C program tini.""" + + +# pylint: disable=global-statement +import argparse +import errno +import logging +import os +import re +import shlex +import signal +import subprocess +import sys + +from signal import Signals as S + + +try: + from munet import linux +except ModuleNotFoundError: + # We cannot use relative imports and still run this module directly as a script, and + # there are some use cases where we want to run this file as a script. + sys.path.append(os.path.dirname(os.path.realpath(__file__))) + import linux + + +class g: + """Global variables for our program.""" + + child_pid = -1 + orig_pid = os.getpid() + exit_signal = False + pid_status_cache = {} + restore_signals = set() + very_verbose = False + + +unshare_flags = { + "C": linux.CLONE_NEWCGROUP, + "i": linux.CLONE_NEWIPC, + "m": linux.CLONE_NEWNS, + "n": linux.CLONE_NEWNET, + "p": linux.CLONE_NEWPID, + "u": linux.CLONE_NEWUTS, + "T": linux.CLONE_NEWTIME, +} + + +ignored_signals = { + S.SIGTTIN, + S.SIGTTOU, +} +abort_signals = { + S.SIGABRT, + S.SIGBUS, + S.SIGFPE, + S.SIGILL, + S.SIGKILL, + S.SIGSEGV, + S.SIGSTOP, + S.SIGSYS, + S.SIGTRAP, +} +no_prop_signals = abort_signals | ignored_signals | {S.SIGCHLD} + + +def vdebug(*args, **kwargs): + if g.very_verbose: + logging.debug(*args, **kwargs) + + +def get_pid_status_item(status, stat): + m = re.search(rf"(?:^|\n){stat}:\t(.*)(?:\n|$)", status) + return m.group(1).strip() if m else None + + +def pget_pid_status_item(pid, stat): + if pid not in g.pid_status_cache: + with open(f"/proc/{pid}/status", "r", encoding="utf-8") as f: + g.pid_status_cache[pid] = f.read().strip() + return get_pid_status_item(g.pid_status_cache[pid], stat).strip() + + +def get_pid_name(pid): + try: + return get_pid_status_item(g.pid_status_cache[pid], "Name") + except Exception: + return str(pid) + + +# def init_get_child_pids(): +# """Return list of "children" pids. +# We consider any process with a 0 parent pid to also be our child as it +# nsentered our pid namespace from an external parent. +# """ +# g.pid_status_cache.clear() +# pids = (int(x) for x in os.listdir("/proc") if x.isdigit() and x != "1") +# return ( +# x for x in pids if x == g.child_pid or pget_pid_status_item(x, "PPid") == "0" +# ) + + +def exit_with_status(status): + if os.WIFEXITED(status): + ec = os.WEXITSTATUS(status) + elif os.WIFSIGNALED(status): + ec = 0x80 | os.WTERMSIG(status) + else: + ec = 255 + logging.debug("exiting with code %s", ec) + sys.exit(ec) + + +def waitpid(tag): + logging.debug("%s: waitid for exiting process", tag) + idobj = os.waitid(os.P_ALL, 0, os.WEXITED) + pid = idobj.si_pid + status = idobj.si_status + + if pid != g.child_pid: + pidname = get_pid_name(pid) + logging.debug( + "%s: reaped zombie %s (%s) w/ status %s", tag, pid, pidname, status + ) + return + + logging.debug("reaped child with status %s", status) + exit_with_status(status) + # NOTREACHED + + +def sig_trasmit(signum, _): + signame = signal.Signals(signum).name + if g.child_pid == -1: + # We've received a signal after setting up to be init proc + # but prior to fork or fork returning with child pid + logging.debug("received %s prior to child exec, exiting", signame) + sys.exit(0x80 | signum) + + try: + os.kill(g.child_pid, signum) + except OSError as error: + if error.errno != errno.ESRCH: + logging.error( + "error forwarding signal %s to child, exiting: %s", signum, error + ) + sys.exit(0x80 | signum) + logging.debug("child pid %s exited prior to signaling", g.child_pid) + + +def sig_sigchld(signum, _): + assert signum == S.SIGCHLD + try: + waitpid("SIGCHLD") + except ChildProcessError as error: + logging.warning("got SIGCHLD but no pid to wait on: %s", error) + + +def setup_init_signals(): + valid = set(signal.valid_signals()) + named = set(x.value for x in signal.Signals) + for snum in sorted(named): + if snum not in valid: + continue + if S.SIGRTMIN <= snum <= S.SIGRTMAX: + continue + + sname = signal.Signals(snum).name + if snum == S.SIGCHLD: + vdebug("installing local handler for %s", sname) + signal.signal(snum, sig_sigchld) + g.restore_signals.add(snum) + elif snum in ignored_signals: + vdebug("installing ignore handler for %s", sname) + signal.signal(snum, signal.SIG_IGN) + g.restore_signals.add(snum) + elif snum in abort_signals: + vdebug("leaving default handler for %s", sname) + # signal.signal(snum, signal.SIG_DFL) + else: + vdebug("installing trasmit signal handler for %s", sname) + try: + signal.signal(snum, sig_trasmit) + g.restore_signals.add(snum) + except OSError as error: + logging.warning( + "failed installing signal handler for %s: %s", sname, error + ) + + +def new_process_group(): + """Create and lead a new process group. + + This function will create a new process group if we are not yet leading one, and + additionally foreground said process group in our session. This foregrounding + action is copied from tini, and I believe serves a purpose when serving as init + for a container (e.g., podman). + """ + pid = os.getpid() + try: + pgid = os.getpgrp() + if pgid == pid: + logging.debug("already process group leader %s", pgid) + else: + logging.debug("creating new process group %s", pid) + os.setpgid(pid, 0) + except Exception as error: + logging.warning("unable to get new process group: %s", error) + return + + # Block these in order to allow foregrounding, otherwise we'd get SIGTTOU blocked + signal.signal(S.SIGTTIN, signal.SIG_IGN) + signal.signal(S.SIGTTOU, signal.SIG_IGN) + fd = sys.stdin.fileno() + if not os.isatty(fd): + logging.debug("stdin not a tty no foregrounding required") + else: + try: + # This will error if our session no longer associated with controlling tty. + pgid = os.tcgetpgrp(fd) + if pgid == pid: + logging.debug("process group already in foreground %s", pgid) + else: + logging.debug("making us the foreground pgid backgrounding %s", pgid) + os.tcsetpgrp(fd, pid) + except OSError as error: + if error.errno == errno.ENOTTY: + logging.debug("session is no longer associated with controlling tty") + else: + logging.warning("unable to foreground pgid %s: %s", pid, error) + signal.signal(S.SIGTTIN, signal.SIG_DFL) + signal.signal(S.SIGTTOU, signal.SIG_DFL) + + +def is_creating_pid_namespace(): + p1name = subprocess.check_output( + "readlink /proc/self/pid", stderr=subprocess.STDOUT, shell=True + ) + p2name = subprocess.check_output( + "readlink /proc/self/pid_for_children", stderr=subprocess.STDOUT, shell=True + ) + return p1name != p2name + + +def be_init(new_pg, exec_args): + # + # Arrange for us to be killed when our parent dies, this will subsequently also kill + # all procs in any PID namespace we are init for. + # + logging.debug("set us to be SIGKILLed when parent exits") + linux.set_parent_death_signal(signal.SIGKILL) + + # If we are createing a new PID namespace for children... + if g.orig_pid != 1: + logging.debug("started as pid %s", g.orig_pid) + # assert is_creating_pid_namespace() + + # Fork to become pid 1 + logging.debug("forking to become pid 1") + child_pid = os.fork() + if child_pid: + logging.debug("in parent waiting on child pid %s to exit", child_pid) + status = os.wait() + logging.debug("got child exit status %s", status) + exit_with_status(status) + # NOTREACHED + + # We must be pid 1 now. + logging.debug("in child as pid %s", os.getpid()) + assert os.getpid() == 1 + + # We need a new /proc now. + logging.debug("mount new /proc") + linux.mount("proc", "/proc", "proc") + + # If the parent exists kill us using SIGKILL + logging.debug("set us to be SIGKILLed when parent exits") + linux.set_parent_death_signal(signal.SIGKILL) + + if not exec_args: + if not new_pg: + logging.debug("no exec args, no new process group") + # # if 0 == os.getpgid(0): + # status = os.setpgid(0, 1) + # logging.debug("os.setpgid(0, 1) == %s", status) + else: + logging.debug("no exec args, creating new process group") + # No exec so we are the "child". + new_process_group() + + while True: + logging.info("parent: waiting to reap zombies") + linux.pause() + # NOTREACHED + + # Set (parent) signal handlers before any fork to avoid race + setup_init_signals() + + logging.debug("forking to execute child") + g.child_pid = os.fork() + if g.child_pid == 0: + # In child, restore signals to default handling: + for snum in g.restore_signals: + signal.signal(snum, signal.SIG_DFL) + + # XXX is a new pg right? + new_process_group() + logging.debug("child: executing '%s'", shlex.join(exec_args)) + os.execvp(exec_args[0], exec_args) + # NOTREACHED + + while True: + logging.info("parent: waiting for child pid %s to exit", g.child_pid) + waitpid("parent") + + +def unshare(flags): + """Unshare into new namespaces.""" + uflags = 0 + for flag in flags: + if flag not in unshare_flags: + raise ValueError(f"unknown unshare flag '{flag}'") + uflags |= unshare_flags[flag] + new_pid = bool(uflags & linux.CLONE_NEWPID) + new_mnt = bool(uflags & linux.CLONE_NEWNS) + + logging.debug("unshareing with flags: %s", linux.clone_flag_string(uflags)) + linux.unshare(uflags) + + if new_pid and not new_mnt: + try: + # If we are not creating new mount namspace, remount /proc private + # so that our mount of a new /proc doesn't affect parent namespace + logging.debug("remount /proc recursive private") + linux.mount("none", "/proc", None, linux.MS_REC | linux.MS_PRIVATE) + except OSError as error: + # EINVAL is OK b/c /proc not mounted may cause an error + if error.errno != errno.EINVAL: + raise + if new_mnt: + # Remount root as recursive private. + logging.debug("remount / recursive private") + linux.mount("none", "/", None, linux.MS_REC | linux.MS_PRIVATE) + + # if new_pid: + # logging.debug("mount new /proc") + # linux.mount("proc", "/proc", "proc") + + return new_pid + + +def main(): + # + # Parse CLI args. + # + + ap = argparse.ArgumentParser() + ap.add_argument( + "-P", + "--no-proc-group", + action="store_true", + help="set to inherit the process group", + ) + valid_flags = "".join(unshare_flags) + ap.add_argument( + "--unshare-flags", + help=( + f"string of unshare(1) flags. Supported values from '{valid_flags}'." + " 'm' will remount `/` recursive private. 'p' will remount /proc" + " and fork, and the child will be signaled to exit on exit of parent.." + ), + ) + ap.add_argument( + "-v", dest="verbose", action="count", default=0, help="more -v's, more verbose" + ) + ap.add_argument("rest", nargs=argparse.REMAINDER) + args = ap.parse_args() + + # + # Setup logging. + # + + level = logging.DEBUG if args.verbose else logging.INFO + if args.verbose > 1: + g.very_verbose = True + logging.basicConfig( + level=level, format="%(asctime)s mutini: %(levelname)s: %(message)s" + ) + + # + # Run program + # + + status = 5 + try: + new_pid = False + if args.unshare_flags: + new_pid = unshare(args.unshare_flags) + + if g.orig_pid != 1 and not new_pid: + # Simply hold the namespaces + while True: + logging.info("holding namespace waiting to be signaled to exit") + linux.pause() + # NOTREACHED + + be_init(not args.no_proc_group, args.rest) + # NOTREACHED + logging.critical("Exited from be_init!") + except KeyboardInterrupt: + logging.info("exiting (main), received KeyboardInterrupt in main") + status = 0x80 | signal.SIGINT + except Exception as error: + logging.info("exiting (main), do to exception %s", error, exc_info=True) + + sys.exit(status) + + +if __name__ == "__main__": + main() diff --git a/tests/topotests/munet/native.py b/tests/topotests/munet/native.py new file mode 100644 index 0000000000..5e79b04b8a --- /dev/null +++ b/tests/topotests/munet/native.py @@ -0,0 +1,2952 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# October 1 2021, Christian Hopps <chopps@labn.net> +# +# Copyright (c) 2021-2022, LabN Consulting, L.L.C. +# +# pylint: disable=protected-access +"""A module that defines objects for standalone use.""" +import asyncio +import errno +import getpass +import ipaddress +import logging +import os +import random +import re +import shlex +import socket +import subprocess +import time + +from . import cli +from .base import BaseMunet +from .base import Bridge +from .base import Commander +from .base import LinuxNamespace +from .base import MunetError +from .base import Timeout +from .base import _async_get_exec_path +from .base import _get_exec_path +from .base import cmd_error +from .base import commander +from .base import fsafe_name +from .base import get_exec_path_host +from .config import config_subst +from .config import config_to_dict_with_key +from .config import find_matching_net_config +from .config import find_with_kv +from .config import merge_kind_config + + +class L3ContainerNotRunningError(MunetError): + """Exception if no running container exists.""" + + +def get_loopback_ips(c, nid): + if ip := c.get("ip"): + if ip == "auto": + return [ipaddress.ip_interface("10.255.0.0/32") + nid] + if isinstance(ip, str): + return [ipaddress.ip_interface(ip)] + return [ipaddress.ip_interface(x) for x in ip] + return [] + + +def make_ip_network(net, inc): + n = ipaddress.ip_network(net) + return ipaddress.ip_network( + (n.network_address + inc * n.num_addresses, n.prefixlen) + ) + + +def make_ip_interface(ia, inc): + ia = ipaddress.ip_interface(ia) + # this turns into a /32 fix this + ia = ia + ia.network.num_addresses * inc + # IPv6 + ia = ipaddress.ip_interface(str(ia).replace("/32", "/24").replace("/128", "/64")) + return ia + + +def get_ip_network(c, brid, ipv6=False): + ip = c.get("ipv6" if ipv6 else "ip") + if ip and str(ip) != "auto": + try: + ifip = ipaddress.ip_interface(ip) + if ifip.ip == ifip.network.network_address: + return ifip.network + return ifip + except ValueError: + return ipaddress.ip_network(ip) + if ipv6: + return make_ip_interface("fc00::fe/64", brid) + return make_ip_interface("10.0.0.254/24", brid) + + +def parse_pciaddr(devaddr): + comp = re.match( + "(?:([0-9A-Fa-f]{4}):)?([0-9A-Fa-f]{2}):([0-9A-Fa-f]{2}).([0-7])", devaddr + ).groups() + if comp[0] is None: + comp[0] = "0000" + return [int(x, 16) for x in comp] + + +def read_int_value(path): + return int(open(path, encoding="ascii").read()) + + +def read_str_value(path): + return open(path, encoding="ascii").read().strip() + + +def read_sym_basename(path): + return os.path.basename(os.readlink(path)) + + +async def to_thread(func): + """to_thread for python < 3.9.""" + try: + return await asyncio.to_thread(func) + except AttributeError: + logging.warning("Using backport to_thread") + return await asyncio.get_running_loop().run_in_executor(None, func) + + +def convert_ranges_to_bitmask(ranges): + bitmask = 0 + for r in ranges.split(","): + if "-" not in r: + bitmask |= 1 << int(r) + else: + x, y = (int(x) for x in r.split("-")) + for b in range(x, y + 1): + bitmask |= 1 << b + return bitmask + + +class L2Bridge(Bridge): + """A linux bridge with no IP network address.""" + + def __init__(self, name=None, unet=None, logger=None, mtu=None, config=None): + """Create a linux Bridge.""" + super().__init__(name=name, unet=unet, logger=logger, mtu=mtu) + + self.config = config if config else {} + + async def _async_delete(self): + self.logger.debug("%s: deleting", self) + await super()._async_delete() + + +class L3Bridge(Bridge): + """A linux bridge with associated IP network address.""" + + def __init__(self, name=None, unet=None, logger=None, mtu=None, config=None): + """Create a linux Bridge.""" + super().__init__(name=name, unet=unet, logger=logger, mtu=mtu) + + self.config = config if config else {} + + self.ip_interface = get_ip_network(self.config, self.id) + if hasattr(self.ip_interface, "network"): + self.ip_address = self.ip_interface.ip + self.ip_network = self.ip_interface.network + self.cmd_raises(f"ip addr add {self.ip_interface} dev {name}") + else: + self.ip_address = None + self.ip_network = self.ip_interface + + self.logger.debug("%s: set IPv4 network address to %s", self, self.ip_interface) + self.cmd_raises("sysctl -w net.ipv4.ip_forward=1") + + self.ip6_interface = None + if self.unet.ipv6_enable: + self.ip6_interface = get_ip_network(self.config, self.id, ipv6=True) + if hasattr(self.ip6_interface, "network"): + self.ip6_address = self.ip6_interface.ip + self.ip6_network = self.ip6_interface.network + self.cmd_raises(f"ip addr add {self.ip6_interface} dev {name}") + else: + self.ip6_address = None + self.ip6_network = self.ip6_interface + + self.logger.debug( + "%s: set IPv6 network address to %s", self, self.ip_interface + ) + self.cmd_raises("sysctl -w net.ipv6.conf.all.forwarding=1") + + self.is_nat = self.config.get("nat", False) + if self.is_nat: + self.cmd_raises( + "iptables -t nat -A POSTROUTING " + f"-s {self.ip_network} ! -d {self.ip_network} " + f"! -o {self.name} -j MASQUERADE" + ) + + def get_intf_addr(self, ifname, ipv6=False): + # None is a valid interface, we have the same address for all interfaces + # just make sure they aren't asking for something we don't have. + if ifname is not None and ifname not in self.intfs: + return None + return self.ip6_interface if ipv6 else self.ip_interface + + async def _async_delete(self): + self.logger.debug("%s: deleting", self) + + if self.config.get("nat", False): + self.cmd_status( + "iptables -t nat -D POSTROUTING " + f"-s {self.ip_network} ! -d {self.ip_network} " + f"! -o {self.name} -j MASQUERADE" + ) + await super()._async_delete() + + +class NodeMixin: + """Node attributes and functionality.""" + + next_ord = 1 + + @classmethod + def _get_next_ord(cls): + # Do not use `cls` here b/c that makes the variable class specific + n = L3NodeMixin.next_ord + L3NodeMixin.next_ord = n + 1 + return n + + def __init__(self, *args, config=None, **kwargs): + """Create a Node.""" + super().__init__(*args, **kwargs) + + self.config = config if config else {} + config = self.config + + self.id = int(config["id"]) if "id" in config else self._get_next_ord() + + self.cmd_p = None + self.container_id = None + self.cleanup_called = False + + # Clear and create rundir early + assert self.unet is not None + self.rundir = self.unet.rundir.joinpath(self.name) + commander.cmd_raises(f"rm -rf {self.rundir}") + commander.cmd_raises(f"mkdir -p {self.rundir}") + + def _shebang_prep(self, config_key): + cmd = self.config.get(config_key, "").strip() + if not cmd: + return [] + + script_name = fsafe_name(config_key) + + # shell_cmd is a union and can be boolean or string + shell_cmd = self.config.get("shell", "/bin/bash") + if not isinstance(shell_cmd, str): + if shell_cmd: + # i.e., "shell: true" + shell_cmd = "/bin/bash" + else: + # i.e., "shell: false" + shell_cmd = "" + + # If we have a shell_cmd then we create a cleanup_cmds file in run_cmd + # and volume mounted it + if shell_cmd: + # Create cleanup cmd file + cmd = cmd.replace("%CONFIGDIR%", str(self.unet.config_dirname)) + cmd = cmd.replace("%RUNDIR%", str(self.rundir)) + cmd = cmd.replace("%NAME%", str(self.name)) + cmd += "\n" + + # Write out our cleanup cmd file at this time too. + cmdpath = os.path.join(self.rundir, f"{script_name}.shebang") + with open(cmdpath, mode="w+", encoding="utf-8") as cmdfile: + cmdfile.write(f"#!{shell_cmd}\n") + cmdfile.write(cmd) + cmdfile.flush() + commander.cmd_raises(f"chmod 755 {cmdpath}") + + if self.container_id: + # XXX this counts on it being mounted in container, ugly + cmds = [f"/tmp/{script_name}.shebang"] + else: + cmds = [cmdpath] + else: + cmds = [] + if isinstance(cmd, str): + cmds.extend(shlex.split(cmd)) + else: + cmds.extend(cmd) + cmds = [ + x.replace("%CONFIGDIR%", str(self.unet.config_dirname)) for x in cmds + ] + cmds = [x.replace("%RUNDIR%", str(self.rundir)) for x in cmds] + cmds = [x.replace("%NAME%", str(self.name)) for x in cmds] + + return cmds + + async def _async_shebang_cmd(self, config_key, warn=True): + cmds = self._shebang_prep(config_key) + if not cmds: + return 0 + + rc, o, e = await self.async_cmd_status(cmds, warn=warn) + if not rc and warn and (o or e): + self.logger.info( + f"async_shebang_cmd ({config_key}): %s", cmd_error(rc, o, e) + ) + elif rc and warn: + self.logger.warning( + f"async_shebang_cmd ({config_key}): %s", cmd_error(rc, o, e) + ) + else: + self.logger.debug( + f"async_shebang_cmd ({config_key}): %s", cmd_error(rc, o, e) + ) + + return rc + + def has_run_cmd(self) -> bool: + return bool(self.config.get("cmd", "").strip()) + + async def get_proc_child_pid(self, p): + # commander is right for both unshare inline (our proc pidns) + # and non-inline (root pidns). + + # This doesn't work b/c we can't get back to the root pidns + + rootcmd = self.unet.rootcmd + pgrep = rootcmd.get_exec_path("pgrep") + spid = str(p.pid) + for _ in Timeout(4): + if p.returncode is not None: + self.logger.debug("%s: proc %s exited before getting child", self, p) + return None + + rc, o, e = await rootcmd.async_cmd_status( + [pgrep, "-o", "-P", spid], warn=False + ) + if rc == 0: + return int(o.strip()) + + await asyncio.sleep(0.1) + self.logger.debug( + "%s: no child of proc %s: %s", self, p, cmd_error(rc, o, e) + ) + self.logger.warning("%s: timeout getting child pid of proc %s", self, p) + return None + + async def run_cmd(self): + """Run the configured commands for this node.""" + self.logger.debug( + "[rundir %s exists %s]", self.rundir, os.path.exists(self.rundir) + ) + + cmds = self._shebang_prep("cmd") + if not cmds: + return + + stdout = open(os.path.join(self.rundir, "cmd.out"), "wb") + stderr = open(os.path.join(self.rundir, "cmd.err"), "wb") + self.cmd_pid = None + self.cmd_p = await self.async_popen( + cmds, + stdin=subprocess.DEVNULL, + stdout=stdout, + stderr=stderr, + start_new_session=True, # allows us to signal all children to exit + ) + + # If our process is actually the child of an nsenter fetch its pid. + if self.nsenter_fork: + self.cmd_pid = await self.get_proc_child_pid(self.cmd_p) + + self.logger.debug( + "%s: async_popen %s => %s (cmd_pid %s)", + self, + cmds, + self.cmd_p.pid, + self.cmd_pid, + ) + + self.pytest_hook_run_cmd(stdout, stderr) + + return self.cmd_p + + async def _async_cleanup_cmd(self): + """Run the configured cleanup commands for this node. + + This function is called by subclass' async_cleanup_cmd + """ + self.cleanup_called = True + + return await self._async_shebang_cmd("cleanup-cmd") + + def has_cleanup_cmd(self) -> bool: + return bool(self.config.get("cleanup-cmd", "").strip()) + + async def async_cleanup_cmd(self): + """Run the configured cleanup commands for this node.""" + return await self._async_cleanup_cmd() + + def has_ready_cmd(self) -> bool: + return bool(self.config.get("ready-cmd", "").strip()) + + async def async_ready_cmd(self): + """Run the configured ready commands for this node.""" + return not await self._async_shebang_cmd("ready-cmd", warn=False) + + def cmd_completed(self, future): + self.logger.debug("%s: cmd completed callback", self) + try: + status = future.result() + self.logger.debug( + "%s: node cmd_p completed result: %s cmd: %s", self, status, self.cmd_p + ) + self.cmd_pid = None + self.cmd_p = None + except asyncio.CancelledError: + # Should we stop the container if we have one? + self.logger.debug("%s: node cmd_p.wait() canceled", future) + + def pytest_hook_run_cmd(self, stdout, stderr): + """Handle pytest options related to running the node cmd. + + This function does things such as launch tail'ing windows + on the given files if requested by the user. + + Args: + stdout: file-like object with a ``name`` attribute, or a path to a file. + stderr: file-like object with a ``name`` attribute, or a path to a file. + """ + if not self.unet or not self.unet.pytest_config: + return + + outopt = self.unet.pytest_config.getoption("--stdout") + outopt = outopt if outopt is not None else "" + if outopt == "all" or self.name in outopt.split(","): + outname = stdout.name if hasattr(stdout, "name") else stdout + self.run_in_window(f"tail -F {outname}", title=f"O:{self.name}") + + if stderr: + erropt = self.unet.pytest_config.getoption("--stderr") + erropt = erropt if erropt is not None else "" + if erropt == "all" or self.name in erropt.split(","): + errname = stderr.name if hasattr(stderr, "name") else stderr + self.run_in_window(f"tail -F {errname}", title=f"E:{self.name}") + + def pytest_hook_open_shell(self): + if not self.unet or not self.unet.pytest_config: + return + + gdbcmd = self.config.get("gdb-cmd") + shellopt = self.unet.pytest_config.getoption("--gdb", "") + should_gdb = gdbcmd and (shellopt == "all" or self.name in shellopt.split(",")) + use_emacs = self.unet.pytest_config.getoption("--gdb-use-emacs", False) + + if should_gdb and not use_emacs: + cmds = self.config.get("gdb-target-cmds", []) + for cmd in cmds: + gdbcmd += f" '-ex={cmd}'" + + bps = self.unet.pytest_config.getoption("--gdb-breakpoints", "").split(",") + for bp in bps: + gdbcmd += f" '-ex=b {bp}'" + + cmds = self.config.get("gdb-run-cmd", []) + for cmd in cmds: + gdbcmd += f" '-ex={cmd}'" + + self.run_in_window(gdbcmd) + elif should_gdb and use_emacs: + gdbcmd = gdbcmd.replace("gdb ", "gdb -i=mi ") + ecbin = self.get_exec_path("emacsclient") + # output = self.cmd_raises( + # [ecbin, "--eval", f"(gdb \"{gdbcmd} -ex='p 123456'\")"] + # ) + _ = self.cmd_raises([ecbin, "--eval", f'(gdb "{gdbcmd}")']) + + # can't figure out how to wait until symbols are loaded, until we do we just + # have to wait "long enough" for the symbol load to finish :/ + # for _ in range(100): + # output = self.cmd_raises( + # [ + # ecbin, + # "--eval", + # f"gdb-first-prompt", + # ] + # ) + # if output == "nil\n": + # break + # time.sleep(0.25) + + time.sleep(10) + + cmds = self.config.get("gdb-target-cmds", []) + for cmd in cmds: + # we may want to quote quotes in the cmd string + self.cmd_raises( + [ + ecbin, + "--eval", + f'(gud-gdb-run-command-fetch-lines "{cmd}" "*gud-gdb*")', + ] + ) + + bps = self.unet.pytest_config.getoption("--gdb-breakpoints", "").split(",") + for bp in bps: + cmd = f"br {bp}" + self.cmd_raises( + [ + ecbin, + "--eval", + f'(gud-gdb-run-command-fetch-lines "{cmd}" "*gud-gdb*")', + ] + ) + + cmds = self.config.get("gdb-run-cmds", []) + for cmd in cmds: + # we may want to quote quotes in the cmd string + self.cmd_raises( + [ + ecbin, + "--eval", + f'(gud-gdb-run-command-fetch-lines "{cmd}" "*gud-gdb*")', + ] + ) + gdbcmd += f" '-ex={cmd}'" + + shellopt = self.unet.pytest_config.getoption("--shell") + shellopt = shellopt if shellopt is not None else "" + if shellopt == "all" or self.name in shellopt.split(","): + self.run_in_window("bash") + + async def _async_delete(self): + self.logger.debug("%s: NodeMixin sub-class _async_delete", self) + + if self.cmd_p: + await self.async_cleanup_proc(self.cmd_p, self.cmd_pid) + self.cmd_p = None + + # Next call users "cleanup_cmd:" + try: + if not self.cleanup_called: + await self.async_cleanup_cmd() + except Exception as error: + self.logger.warning( + "Got an error during delete from async_cleanup_cmd: %s", error + ) + + # delete the LinuxNamespace/InterfaceMixin + await super()._async_delete() + + +class SSHRemote(NodeMixin, Commander): + """SSHRemote a node representing an ssh connection to something.""" + + def __init__( + self, + name, + server, + port=22, + user=None, + password=None, + idfile=None, + **kwargs, + ): + super().__init__(name, **kwargs) + + self.logger.debug("%s: creating", self) + + # Things done in LinuxNamepsace we need to replicate here. + self.rundir = self.unet.rundir.joinpath(self.name) + self.unet.cmd_raises(f"rm -rf {self.rundir}") + self.unet.cmd_raises(f"mkdir -p {self.rundir}") + + self.mgmt_ip = None + self.mgmt_ip6 = None + + self.port = port + + if user: + self.user = user + elif "SUDO_USER" in os.environ: + self.user = os.environ["SUDO_USER"] + else: + self.user = getpass.getuser() + self.password = password + self.idfile = idfile + + self.server = f"{self.user}@{server}" + + # Setup our base `pre-cmd` values + # + # We maybe should add environment variable transfer here in particular + # MUNET_NODENAME. The problem is the user has to explicitly approve + # of SendEnv variables. + self.__base_cmd = [ + get_exec_path_host("sudo"), + "-E", + f"-u{self.user}", + get_exec_path_host("ssh"), + ] + if port != 22: + self.__base_cmd.append(f"-p{port}") + self.__base_cmd.append("-q") + self.__base_cmd.append("-oStrictHostKeyChecking=no") + self.__base_cmd.append("-oUserKnownHostsFile=/dev/null") + if self.idfile: + self.__base_cmd.append(f"-i{self.idfile}") + # Would be nice but has to be accepted by server config so not very useful. + # self.__base_cmd.append("-oSendVar='TEST'") + self.__base_cmd_pty = list(self.__base_cmd) + self.__base_cmd_pty.append("-t") + self.__base_cmd.append(self.server) + self.__base_cmd_pty.append(self.server) + # self.set_pre_cmd(pre_cmd, pre_cmd_tty) + + self.logger.info("%s: created", self) + + def has_ready_cmd(self) -> bool: + return bool(self.config.get("ready-cmd", "").strip()) + + def _get_pre_cmd(self, use_str, use_pty, ns_only=False, **kwargs): + pre_cmd = [] + if self.unet: + pre_cmd = self.unet._get_pre_cmd(False, use_pty, ns_only=False, **kwargs) + if ns_only: + return pre_cmd + + # XXX grab the env from kwargs and add to podman exec + # env = kwargs.get("env", {}) + if use_pty: + pre_cmd = pre_cmd + self.__base_cmd_pty + else: + pre_cmd = pre_cmd + self.__base_cmd + return shlex.join(pre_cmd) if use_str else list(pre_cmd) + + def _get_cmd_as_list(self, cmd): + """Given a list or string return a list form for execution. + + If cmd is a string then [cmd] is returned, for most other + node types ["bash", "-c", cmd] is returned but in our case + ssh is the shell. + + Args: + cmd: list or string representing the command to execute. + str_shell: if True and `cmd` is a string then run the + command using bash -c + Returns: + list of commands to execute. + """ + return [cmd] if isinstance(cmd, str) else cmd + + +# Would maybe like to refactor this into L3 and Node +class L3NodeMixin(NodeMixin): + """A linux namespace with IP attributes.""" + + def __init__(self, *args, unet=None, **kwargs): + """Create an L3Node.""" + # logging.warning( + # "L3NodeMixin: config %s unet %s kwargs %s", config, unet, kwargs + # ) + super().__init__(*args, unet=unet, **kwargs) + + self.mgmt_ip = None # set in parser.py + self.mgmt_ip6 = None # set in parser.py + self.host_intfs = {} + self.phy_intfs = {} + self.phycount = 0 + self.phy_odrivers = {} + self.tapmacs = {} + + self.intf_tc_count = 0 + + # super().__init__(name=name, **kwargs) + + self.mount_volumes() + + # ----------------------- + # Setup node's networking + # ----------------------- + if not unet.ipv6_enable: + # Disable IPv6 + self.cmd_raises("sysctl -w net.ipv6.conf.all.autoconf=0") + self.cmd_raises("sysctl -w net.ipv6.conf.all.disable_ipv6=1") + else: + self.cmd_raises("sysctl -w net.ipv6.conf.all.autoconf=1") + self.cmd_raises("sysctl -w net.ipv6.conf.all.disable_ipv6=0") + + self.next_p2p_network = ipaddress.ip_network(f"10.254.{self.id}.0/31") + self.next_p2p_network6 = ipaddress.ip_network(f"fcff:ffff:{self.id:02x}::/127") + + self.loopback_ip = None + self.loopback_ips = get_loopback_ips(self.config, self.id) + self.loopback_ip = self.loopback_ips[0] if self.loopback_ips else None + if self.loopback_ip: + self.cmd_raises_nsonly(f"ip addr add {self.loopback_ip} dev lo") + self.cmd_raises_nsonly("ip link set lo up") + for i, ip in enumerate(self.loopback_ips[1:]): + self.cmd_raises_nsonly(f"ip addr add {ip} dev lo:{i}") + + # ------------------- + # Setup node's rundir + # ------------------- + + # Not host path based, but we assume same + self.set_ns_cwd(self.rundir) + + # Save the namespace pid + with open(os.path.join(self.rundir, "nspid"), "w", encoding="ascii") as f: + f.write(f"{self.pid}\n") + + with open(os.path.join(self.rundir, "nspids"), "w", encoding="ascii") as f: + f.write(f'{" ".join([str(x) for x in self.pids])}\n') + + # Create a hosts file to map our name + hosts_file = os.path.join(self.rundir, "hosts.txt") + with open(hosts_file, "w", encoding="ascii") as hf: + hf.write( + f"""127.0.0.1\tlocalhost {self.name} +::1\tip6-localhost ip6-loopback +fe00::0\tip6-localnet +ff00::0\tip6-mcastprefix +ff02::1\tip6-allnodes +ff02::2\tip6-allrouters +""" + ) + if hasattr(self, "bind_mount"): + self.bind_mount(hosts_file, "/etc/hosts") + + async def console( + self, + concmd, + prompt=r"(^|\r?\n)[^#\$]*[#\$] ", + is_bourne=True, + user=None, + password=None, + expects=None, + sends=None, + use_pty=False, + will_echo=False, + logfile_prefix="console", + trace=True, + **kwargs, + ): + """Create a REPL (read-eval-print-loop) driving a console. + + Args: + concmd: string or list to popen with, or an already open socket + prompt: the REPL prompt to look for, the function returns when seen + is_bourne: True if the console is a bourne shell + user: user name to log in with + password: password to log in with + expects: a list of regex other than the prompt, the standard user, or + password to look for. "ogin:" or "[Pp]assword:"r. + sends: what to send when an element of `expects` matches. Can be the + empty string to send nothing. + use_pty: true for pty based expect, otherwise uses popen (pipes/files) + will_echo: bash is buggy in that it echo's to non-tty unlike any other + sh/ksh, set this value to true if running back + logfile_prefix: prefix for 3 logfiles opened to track the console i/o + trace: trace the send/expect sequence + **kwargs: kwargs passed on the _spawn. + """ + lfname = os.path.join(self.rundir, f"{logfile_prefix}-log.txt") + logfile = open(lfname, "a+", encoding="utf-8") + logfile.write("-- start logging for: '{}' --\n".format(concmd)) + + lfname = os.path.join(self.rundir, f"{logfile_prefix}-read-log.txt") + logfile_read = open(lfname, "a+", encoding="utf-8") + logfile_read.write("-- start read logging for: '{}' --\n".format(concmd)) + + lfname = os.path.join(self.rundir, f"{logfile_prefix}-send-log.txt") + logfile_send = open(lfname, "a+", encoding="utf-8") + logfile_send.write("-- start send logging for: '{}' --\n".format(concmd)) + + expects = [] if expects is None else expects + sends = [] if sends is None else sends + if user: + expects.append("ogin:") + sends.append(user + "\n") + if password is not None: + expects.append("assword:") + sends.append(password + "\n") + repl = await self.shell_spawn( + concmd, + prompt, + expects=expects, + sends=sends, + use_pty=use_pty, + will_echo=will_echo, + is_bourne=is_bourne, + logfile=logfile, + logfile_read=logfile_read, + logfile_send=logfile_send, + trace=trace, + **kwargs, + ) + return repl + + async def monitor( + self, + sockpath, + prompt=r"\(qemu\) ", + ): + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + sock.connect(sockpath) + + pfx = os.path.basename(sockpath) + + lfname = os.path.join(self.rundir, f"{pfx}-log.txt") + logfile = open(lfname, "a+", encoding="utf-8") + logfile.write("-- start logging for: '{}' --\n".format(sock)) + + lfname = os.path.join(self.rundir, f"{pfx}-read-log.txt") + logfile_read = open(lfname, "a+", encoding="utf-8") + logfile_read.write("-- start read logging for: '{}' --\n".format(sock)) + + p = self.spawn(sock, prompt, logfile=logfile, logfile_read=logfile_read) + from .base import ShellWrapper # pylint: disable=C0415 + + p.send("\n") + return ShellWrapper(p, prompt, None, will_echo=True, escape_ansi=True) + + def mount_volumes(self): + for m in self.config.get("volumes", []): + if isinstance(m, str): + s = m.split(":", 1) + if len(s) == 1: + self.tmpfs_mount(s[0]) + else: + spath = s[0] + if spath[0] == ".": + spath = os.path.abspath( + os.path.join(self.unet.config_dirname, spath) + ) + self.bind_mount(spath, s[1]) + continue + raise NotImplementedError("complex mounts for non-containers") + + def get_ifname(self, netname): + for c in self.config["connections"]: + if c["to"] == netname: + return c["name"] + return None + + def set_lan_addr(self, switch, cconf): + if ip := cconf.get("ip"): + ipaddr = ipaddress.ip_interface(ip) + assert ipaddr.version == 4 + elif self.unet.autonumber and "ip" not in cconf: + self.logger.debug( + "%s: prefixlen of switch %s is %s", + self, + switch.name, + switch.ip_network.prefixlen, + ) + n = switch.ip_network + ipaddr = ipaddress.ip_interface((n.network_address + self.id, n.prefixlen)) + else: + ipaddr = None + + if ip := cconf.get("ipv6"): + ip6addr = ipaddress.ip_interface(ip) + assert ipaddr.version == 6 + elif self.unet.ipv6_enable and self.unet.autonumber and "ipv6" not in cconf: + self.logger.debug( + "%s: prefixlen of switch %s is %s", + self, + switch.name, + switch.ip6_network.prefixlen, + ) + n = switch.ip6_network + ip6addr = ipaddress.ip_interface((n.network_address + self.id, n.prefixlen)) + else: + ip6addr = None + + dns_network = self.unet.topoconf.get("dns-network") + for ip in (ipaddr, ip6addr): + if not ip: + continue + ipcmd = "ip " if ip.version == 4 else "ip -6 " + if dns_network and dns_network == switch.name: + if ip.version == 4: + self.mgmt_ip = ip.ip + else: + self.mgmt_ip6 = ip.ip + ifname = cconf["name"] + self.set_intf_addr(ifname, ip) + self.logger.debug("%s: adding %s to lan intf %s", self, ip, ifname) + if not self.is_vm: + self.intf_ip_cmd(ifname, ipcmd + f"addr add {ip} dev {ifname}") + if hasattr(switch, "is_nat") and switch.is_nat: + swaddr = ( + switch.ip_address if ip.version == 4 else switch.ip6_address + ) + self.cmd_raises(ipcmd + f"route add default via {swaddr}") + + def _set_p2p_addr(self, other, cconf, occonf, ipv6=False): + ipkey = "ipv6" if ipv6 else "ip" + ipaddr = ipaddress.ip_interface(cconf[ipkey]) if cconf.get(ipkey) else None + oipaddr = ipaddress.ip_interface(occonf[ipkey]) if occonf.get(ipkey) else None + self.logger.debug( + "%s: set_p2p_addr %s %s %s", self, other.name, ipaddr, oipaddr + ) + + if not ipaddr and not oipaddr: + if self.unet.autonumber: + if ipv6: + n = self.next_p2p_network6 + self.next_p2p_network6 = make_ip_network(n, 1) + else: + n = self.next_p2p_network + self.next_p2p_network = make_ip_network(n, 1) + + ipaddr = ipaddress.ip_interface(n) + oipaddr = ipaddress.ip_interface((ipaddr.ip + 1, n.prefixlen)) + else: + return + + if ipaddr: + ifname = cconf["name"] + self.set_intf_addr(ifname, ipaddr) + self.logger.debug("%s: adding %s to p2p intf %s", self, ipaddr, ifname) + if "physical" not in cconf and not self.is_vm: + self.intf_ip_cmd(ifname, f"ip addr add {ipaddr} dev {ifname}") + + if oipaddr: + oifname = occonf["name"] + other.set_intf_addr(oifname, oipaddr) + self.logger.debug( + "%s: adding %s to other p2p intf %s", other, oipaddr, oifname + ) + if "physical" not in occonf and not other.is_vm: + other.intf_ip_cmd(oifname, f"ip addr add {oipaddr} dev {oifname}") + + def set_p2p_addr(self, other, cconf, occonf): + self._set_p2p_addr(other, cconf, occonf, ipv6=False) + if self.unet.ipv6_enable: + self._set_p2p_addr(other, cconf, occonf, ipv6=True) + + async def add_host_intf(self, hname, lname, mtu=None): + if hname in self.host_intfs: + return + self.host_intfs[hname] = lname + self.unet.rootcmd.cmd_nostatus(f"ip link set {hname} down ") + self.unet.rootcmd.cmd_raises(f"ip link set {hname} netns {self.pid}") + self.cmd_raises(f"ip link set {hname} name {lname}") + if mtu: + self.cmd_raises(f"ip link set {lname} mtu {mtu}") + self.cmd_raises(f"ip link set {lname} up") + + async def rem_host_intf(self, hname): + lname = self.host_intfs[hname] + self.cmd_raises(f"ip link set {lname} down") + self.cmd_raises(f"ip link set {lname} name {hname}") + self.cmd_raises(f"ip link set {hname} netns 1") + del self.host_intfs[hname] + + async def add_phy_intf(self, devaddr, lname): + """Add a physical inteface (i.e. mv it to vfio-pci driver. + + This is primarily useful for Qemu, but also for things like TREX or DPDK + """ + if devaddr in self.phy_intfs: + return + self.phy_intfs[devaddr] = lname + index = len(self.phy_intfs) + + _, _, off, fun = parse_pciaddr(devaddr) + doffset = off * 8 + fun + + is_virtual = self.unet.rootcmd.path_exists( + f"/sys/bus/pci/devices/{devaddr}/physfn" + ) + if is_virtual: + pfname = self.unet.rootcmd.cmd_raises( + f"ls -1 /sys/bus/pci/devices/{devaddr}/physfn/net" + ).strip() + pdevaddr = read_sym_basename(f"/sys/bus/pci/devices/{devaddr}/physfn") + _, _, poff, pfun = parse_pciaddr(pdevaddr) + poffset = poff * 8 + pfun + + offset = read_int_value( + f"/sys/bus/pci/devices/{devaddr}/physfn/sriov_offset" + ) + stride = read_int_value( + f"/sys/bus/pci/devices/{devaddr}/physfn/sriov_stride" + ) + vf = (doffset - offset - poffset) // stride + mac = f"02:cc:cc:cc:{index:02x}:{self.id:02x}" + # Some devices require the parent to be up (e.g., ixbge) + self.unet.rootcmd.cmd_raises(f"ip link set {pfname} up") + self.unet.rootcmd.cmd_raises(f"ip link set {pfname} vf {vf} mac {mac}") + self.unet.rootcmd.cmd_status(f"ip link set {pfname} vf {vf} trust on") + self.tapmacs[devaddr] = mac + + self.logger.info("Adding physical PCI device %s as %s", devaddr, lname) + + # Get interface name and set to down if present + ec, ifname, _ = self.unet.rootcmd.cmd_status( + f"ls /sys/bus/pci/devices/{devaddr}/net/", warn=False + ) + ifname = ifname.strip() + if not ec and ifname: + # XXX Should only do this is the device is up, and then likewise return it + # up on exit self.phy_intfs_hostname[devaddr] = ifname + self.logger.info( + "Setting physical PCI device %s named %s down", devaddr, ifname + ) + self.unet.rootcmd.cmd_status( + f"ip link set {ifname} down 2> /dev/null || true" + ) + + # Get the current bound driver, and unbind + try: + driver = read_sym_basename(f"/sys/bus/pci/devices/{devaddr}/driver") + driver = driver.strip() + except Exception: + driver = "" + if driver: + if driver == "vfio-pci": + self.logger.info( + "Physical PCI device %s already bound to vfio-pci", devaddr + ) + return + self.logger.info( + "Unbinding physical PCI device %s from driver %s", devaddr, driver + ) + self.phy_odrivers[devaddr] = driver + self.unet.rootcmd.cmd_raises( + f"echo {devaddr} > /sys/bus/pci/drivers/{driver}/unbind" + ) + + # Add the device vendor and device id to vfio-pci in case it's the first time + vendor = read_str_value(f"/sys/bus/pci/devices/{devaddr}/vendor") + devid = read_str_value(f"/sys/bus/pci/devices/{devaddr}/device") + self.logger.info("Adding device IDs %s:%s to vfio-pci", vendor, devid) + ec, _, _ = self.unet.rootcmd.cmd_status( + f"echo {vendor} {devid} > /sys/bus/pci/drivers/vfio-pci/new_id", warn=False + ) + + if not self.unet.rootcmd.path_exists(f"/sys/bus/pci/driver/vfio-pci/{devaddr}"): + # Bind to vfio-pci if wasn't added with new_id + self.logger.info("Binding physical PCI device %s to vfio-pci", devaddr) + ec, _, _ = self.unet.rootcmd.cmd_status( + f"echo {devaddr} > /sys/bus/pci/drivers/vfio-pci/bind" + ) + + async def rem_phy_intf(self, devaddr): + """Remove a physical inteface (i.e. mv it away from vfio-pci driver. + + This is primarily useful for Qemu, but also for things like TREX or DPDK + """ + lname = self.phy_intfs.get(devaddr, "") + if lname: + del self.phy_intfs[devaddr] + + # ifname = self.phy_intfs_hostname.get(devaddr, "") + # if ifname + # del self.phy_intfs_hostname[devaddr] + + driver = self.phy_odrivers.get(devaddr, "") + if not driver: + self.logger.info( + "Physical PCI device %s was bound to vfio-pci on entry", devaddr + ) + return + + self.logger.info( + "Unbinding physical PCI device %s from driver vfio-pci", devaddr + ) + self.unet.rootcmd.cmd_status( + f"echo {devaddr} > /sys/bus/pci/drivers/vfio-pci/unbind" + ) + + self.logger.info("Binding physical PCI device %s to driver %s", devaddr, driver) + ec, _, _ = self.unet.rootcmd.cmd_status( + f"echo {devaddr} > /sys/bus/pci/drivers/{driver}/bind" + ) + if not ec: + del self.phy_odrivers[devaddr] + + async def _async_delete(self): + self.logger.debug("%s: L3NodeMixin sub-class _async_delete", self) + + # XXX do we need to run the cleanup command before these infra changes? + + # remove any hostintf interfaces + for hname in list(self.host_intfs): + await self.rem_host_intf(hname) + + # remove any hostintf interfaces + for devaddr in list(self.phy_intfs): + await self.rem_phy_intf(devaddr) + + # delete the LinuxNamespace/InterfaceMixin + await super()._async_delete() + + +class L3NamespaceNode(L3NodeMixin, LinuxNamespace): + """A namespace L3 node.""" + + def __init__(self, name, pid=True, **kwargs): + # logging.warning( + # "L3NamespaceNode: name %s MRO: %s kwargs %s", + # name, + # L3NamespaceNode.mro(), + # kwargs, + # ) + super().__init__(name, pid=pid, **kwargs) + super().pytest_hook_open_shell() + + async def _async_delete(self): + self.logger.debug("%s: deleting", self) + await super()._async_delete() + + +class L3ContainerNode(L3NodeMixin, LinuxNamespace): + """An container (podman) based L3 node.""" + + def __init__(self, name, config, **kwargs): + """Create a Container Node.""" + self.cont_exec_paths = {} + self.container_id = None + self.container_image = config["image"] + self.extra_mounts = [] + assert self.container_image + + self.cmd_p = None + self.__base_cmd = [] + self.__base_cmd_pty = [] + + # don't we have a mutini or cat process? + super().__init__( + name=name, + config=config, + # pid=True, + # cgroup=True, + # private_mounts=["/sys/fs/cgroup:/sys/fs/cgroup"], + **kwargs, + ) + + @property + def is_container(self): + return True + + def get_exec_path(self, binary): + """Return the full path to the binary executable inside the image. + + `binary` :: binary name or list of binary names + """ + return _get_exec_path(binary, self.cmd_status, self.cont_exec_paths) + + async def async_get_exec_path(self, binary): + """Return the full path to the binary executable inside the image. + + `binary` :: binary name or list of binary names + """ + path = await _async_get_exec_path( + binary, self.async_cmd_status, self.cont_exec_paths + ) + return path + + def get_exec_path_host(self, binary): + """Return the full path to the binary executable on the host. + + `binary` :: binary name or list of binary names + """ + return get_exec_path_host(binary) + + def _get_pre_cmd(self, use_str, use_pty, ns_only=False, root_level=False, **kwargs): + if ns_only: + return super()._get_pre_cmd( + use_str, use_pty, ns_only=True, root_level=root_level, **kwargs + ) + if not self.cmd_p: + if self.container_id: + s = f"{self}: Running command in namespace b/c container exited" + self.logger.warning("%s", s) + raise L3ContainerNotRunningError(s) + self.logger.debug("%s: Running command in namespace b/c no container", self) + return super()._get_pre_cmd( + use_str, use_pty, ns_only=True, root_level=root_level, **kwargs + ) + + # We need to enter our namespaces when running the podman command + pre_cmd = super()._get_pre_cmd( + False, use_pty, ns_only=True, root_level=root_level, **kwargs + ) + + # XXX grab the env from kwargs and add to podman exec + # env = kwargs.get("env", {}) + if use_pty: + pre_cmd = pre_cmd + self.__base_cmd_pty + else: + pre_cmd = pre_cmd + self.__base_cmd + return shlex.join(pre_cmd) if use_str else pre_cmd + + def tmpfs_mount(self, inner): + # eventually would be nice to support live mounting + assert not self.container_id + self.logger.debug("Mounting tmpfs on %s", inner) + self.extra_mounts.append(f"--mount=type=tmpfs,destination={inner}") + + def bind_mount(self, outer, inner): + # eventually would be nice to support live mounting + assert not self.container_id + # First bind the mount in the parent this allows things like /etc/hosts to work + # correctly when running "nsonly" commands + super().bind_mount(outer, inner) + # Then arrange for binding in the container as well. + self.logger.debug("Bind mounting %s on %s", outer, inner) + if not self.test_nsonly("-e", outer): + self.cmd_raises_nsonly(f"mkdir -p {outer}") + self.extra_mounts.append(f"--mount=type=bind,src={outer},dst={inner}") + + def mount_volumes(self): + args = [] + for m in self.config.get("volumes", []): + if isinstance(m, str): + s = m.split(":", 1) + if len(s) == 1: + args.append("--mount=type=tmpfs,destination=" + m) + else: + spath = s[0] + spath = os.path.abspath( + os.path.join( + os.path.dirname(self.unet.config["config_pathname"]), spath + ) + ) + if not self.test_nsonly("-e", spath): + self.cmd_raises_nsonly(f"mkdir -p {spath}") + args.append(f"--mount=type=bind,src={spath},dst={s[1]}") + continue + + for m in self.config.get("mounts", []): + margs = ["type=" + m["type"]] + for k, v in m.items(): + if k == "type": + continue + if v: + if k in ("src", "source"): + v = os.path.abspath( + os.path.join( + os.path.dirname(self.unet.config["config_pathname"]), v + ) + ) + if not self.test_nsonly("-e", v): + self.cmd_raises_nsonly(f"mkdir -p {v}") + margs.append(f"{k}={v}") + else: + margs.append(f"{k}") + args.append("--mount=" + ",".join(margs)) + + if args: + # Need to work on a way to mount into live container too + self.extra_mounts += args + + def has_run_cmd(self) -> bool: + return True + + async def run_cmd(self): + """Run the configured commands for this node.""" + self.logger.debug("%s: starting container", self.name) + self.logger.debug( + "[rundir %s exists %s]", self.rundir, os.path.exists(self.rundir) + ) + + self.container_id = f"{self.name}-{os.getpid()}" + proc_path = self.unet.proc_path if self.unet else "/proc" + cmds = [ + get_exec_path_host("podman"), + "run", + f"--name={self.container_id}", + # f"--net=ns:/proc/{self.pid}/ns/net", + f"--net=ns:{proc_path}/{self.pid}/ns/net", + f"--hostname={self.name}", + f"--add-host={self.name}:127.0.0.1", + # We can't use --rm here b/c podman fails on "stop". + # u"--rm", + ] + + if self.config.get("init", True): + cmds.append("--init") + + if self.config.get("privileged", False): + cmds.append("--privileged") + # If we don't do this then the host file system is remounted read-only on + # exit! + cmds.append("--systemd=false") + else: + cmds.extend( + [ + # "--cap-add=SYS_ADMIN", + "--cap-add=NET_ADMIN", + "--cap-add=NET_RAW", + ] + ) + + # Add volumes: + if self.extra_mounts: + cmds += self.extra_mounts + + # Add environment variables: + envdict = self.config.get("env", {}) + if envdict is None: + envdict = {} + for k, v in envdict.items(): + cmds.append(f"--env={k}={v}") + + # Update capabilities + cmds += [f"--cap-add={x}" for x in self.config.get("cap-add", [])] + cmds += [f"--cap-drop={x}" for x in self.config.get("cap-drop", [])] + # cmds += [f"--expose={x.split(':')[0]}" for x in self.config.get("ports", [])] + cmds += [f"--publish={x}" for x in self.config.get("ports", [])] + + # Add extra flags from user: + if "podman" in self.config: + for x in self.config["podman"].get("extra-args", []): + cmds.append(x.strip()) + + # shell_cmd is a union and can be boolean or string + shell_cmd = self.config.get("shell", "/bin/bash") + if not isinstance(shell_cmd, str): + if shell_cmd: + shell_cmd = "/bin/bash" + else: + shell_cmd = "" + + # Create shebang files, filled later on + for key in ("cleanup-cmd", "ready-cmd"): + shebang_cmd = self.config.get(key, "").strip() + if shell_cmd and shebang_cmd: + script_name = fsafe_name(key) + # Will write the file contents out when the command is run + shebang_cmdpath = os.path.join(self.rundir, f"{script_name}.shebang") + await self.async_cmd_raises_nsonly(f"touch {shebang_cmdpath}") + await self.async_cmd_raises_nsonly(f"chmod 755 {shebang_cmdpath}") + cmds += [ + # How can we override this? + # u'--entrypoint=""', + f"--volume={shebang_cmdpath}:/tmp/{script_name}.shebang", + ] + + cmd = self.config.get("cmd", "").strip() + + # See if we have a custom update for this `kind` + if kind := self.config.get("kind", None): + if kind in kind_run_cmd_update: + cmds, cmd = await kind_run_cmd_update[kind](self, shell_cmd, cmds, cmd) + + # Create running command file + if shell_cmd and cmd: + assert isinstance(cmd, str) + # make cmd \n terminated for script + cmd = cmd.rstrip() + cmd = cmd.replace("%CONFIGDIR%", str(self.unet.config_dirname)) + cmd = cmd.replace("%RUNDIR%", str(self.rundir)) + cmd = cmd.replace("%NAME%", str(self.name)) + cmd += "\n" + cmdpath = os.path.join(self.rundir, "cmd.shebang") + with open(cmdpath, mode="w+", encoding="utf-8") as cmdfile: + cmdfile.write(f"#!{shell_cmd}\n") + cmdfile.write(cmd) + cmdfile.flush() + self.cmd_raises_nsonly(f"chmod 755 {cmdpath}") + cmds += [ + # How can we override this? + # u'--entrypoint=""', + f"--volume={cmdpath}:/tmp/cmds.shebang", + self.container_image, + "/tmp/cmds.shebang", + ] + else: + # `cmd` is a direct run (no shell) cmd + cmds.append(self.container_image) + if cmd: + if isinstance(cmd, str): + cmds.extend(shlex.split(cmd)) + else: + cmds.extend(cmd) + + cmds = [ + x.replace("%CONFIGDIR%", str(self.unet.config_dirname)) for x in cmds + ] + cmds = [x.replace("%RUNDIR%", str(self.rundir)) for x in cmds] + cmds = [x.replace("%NAME%", str(self.name)) for x in cmds] + + stdout = open(os.path.join(self.rundir, "cmd.out"), "wb") + stderr = open(os.path.join(self.rundir, "cmd.err"), "wb") + # Using nsonly avoids using `podman exec` to execute the cmds. + self.cmd_p = await self.async_popen_nsonly( + cmds, + stdin=subprocess.DEVNULL, + stdout=stdout, + stderr=stderr, + start_new_session=True, # keeps main tty signals away from podman + ) + + self.logger.debug("%s: async_popen => %s", self, self.cmd_p.pid) + + self.pytest_hook_run_cmd(stdout, stderr) + + # --------------------------------------- + # Now let's wait until container shows up + # --------------------------------------- + timeout = Timeout(30) + while self.cmd_p.returncode is None and not timeout.is_expired(): + o = await self.async_cmd_raises_nsonly( + f"podman ps -q -f name={self.container_id}" + ) + if o.strip(): + break + elapsed = int(timeout.elapsed()) + if elapsed <= 3: + await asyncio.sleep(0.1) + else: + self.logger.info("%s: run_cmd taking more than %ss", self, elapsed) + await asyncio.sleep(1) + if self.cmd_p.returncode is not None: + # leave self.container_id set to cause exception on use + self.logger.warning( + "%s: run_cmd exited quickly (%ss) rc: %s", + self, + timeout.elapsed(), + self.cmd_p.returncode, + ) + elif timeout.is_expired(): + self.logger.critical( + "%s: timeout (%ss) waiting for container to start", + self.name, + timeout.elapsed(), + ) + assert not timeout.is_expired() + + # + # Set our precmd for executing in the container + # + self.__base_cmd = [ + get_exec_path_host("podman"), + "exec", + f"-eMUNET_RUNDIR={self.unet.rundir}", + f"-eMUNET_NODENAME={self.name}", + "-i", + ] + self.__base_cmd_pty = list(self.__base_cmd) # copy list to pty + self.__base_cmd.append(self.container_id) # end regular list + self.__base_cmd_pty.append("-t") # add pty flags + self.__base_cmd_pty.append(self.container_id) # end pty list + # self.set_pre_cmd(self.__base_cmd, self.__base_cmd_pty) # set both pre_cmd + + self.logger.info("%s: started container", self.name) + + self.pytest_hook_open_shell() + + return self.cmd_p + + async def async_cleanup_cmd(self): + """Run the configured cleanup commands for this node.""" + self.cleanup_called = True + + if "cleanup-cmd" not in self.config: + return + + if not self.cmd_p: + self.logger.warning("async_cleanup_cmd: container no longer running") + return + + return await self._async_cleanup_cmd() + + def cmd_completed(self, future): + try: + log = self.logger.debug if self.deleting else self.logger.warning + n = future.result() + if self.deleting: + log("contianer `cmd:` result: %s", n) + else: + log( + "contianer `cmd:` exited early, " + "try adding `tail -f /dev/null` to `cmd:`, result: %s", + n, + ) + except asyncio.CancelledError as error: + # Should we stop the container if we have one? or since we are canceled + # we know we will be deleting soon? + self.logger.warning( + "node container cmd wait() canceled: %s:%s", future, error + ) + self.cmd_p = None + + async def _async_delete(self): + self.logger.debug("%s: deleting", self) + + if contid := self.container_id: + try: + if not self.cleanup_called: + self.logger.debug("calling user cleanup cmd") + await self.async_cleanup_cmd() + except Exception as error: + self.logger.warning( + "Got an error during delete from async_cleanup_cmd: %s", error + ) + + # Clear the container_id field we want to act like a namespace now. + self.container_id = None + + o = "" + e = "" + if self.cmd_p: + self.logger.debug("podman stop on container: %s", contid) + if (rc := self.cmd_p.returncode) is None: + rc, o, e = await self.async_cmd_status_nsonly( + [get_exec_path_host("podman"), "stop", "--time=2", contid] + ) + if rc and rc < 128: + self.logger.warning( + "%s: podman stop on cmd failed: %s", + self, + cmd_error(rc, o, e), + ) + else: + # It's gone + self.cmd_p = None + + # now remove the container + self.logger.debug("podman rm on container: %s", contid) + rc, o, e = await self.async_cmd_status_nsonly( + [get_exec_path_host("podman"), "rm", contid] + ) + if rc: + self.logger.warning( + "%s: podman rm failed: %s", self, cmd_error(rc, o, e) + ) + else: + self.logger.debug( + "podman removed container %s: %s", contid, cmd_error(rc, o, e) + ) + + await super()._async_delete() + + +class L3QemuVM(L3NodeMixin, LinuxNamespace): + """An VM (qemu) based L3 node.""" + + def __init__(self, name, config, **kwargs): + """Create a Container Node.""" + self.cont_exec_paths = {} + self.launch_p = None + self.qemu_config = config["qemu"] + self.extra_mounts = [] + assert self.qemu_config + self.cmdrepl = None + self.conrepl = None + self.is_kvm = False + self.monrepl = None + self.tapfds = {} + self.cpu_thread_map = {} + + self.tapnames = {} + + self.use_ssh = False + self.__base_cmd = [] + self.__base_cmd_pty = [] + + super().__init__(name=name, config=config, pid=False, **kwargs) + + self.sockdir = self.rundir.joinpath("s") + self.cmd_raises(f"mkdir -p {self.sockdir}") + + self.qemu_config = config_subst( + self.qemu_config, + name=self.name, + rundir=os.path.join(self.rundir, self.name), + configdir=self.unet.config_dirname, + ) + self.ssh_keyfile = self.qemu_config.get("sshkey") + + @property + def is_vm(self): + return True + + def __setup_ssh(self): + if not self.ssh_keyfile: + self.logger.warning("%s: No sshkey config", self) + return False + if not self.mgmt_ip and not self.mgmt_ip6: + self.logger.warning("%s: No mgmt IP to ssh to", self) + return False + mgmt_ip = self.mgmt_ip if self.mgmt_ip else self.mgmt_ip6 + + # + # Since we have a keyfile shouldn't need to sudo + # self.user = os.environ.get("SUDO_USER", "") + # if not self.user: + # self.user = getpass.getuser() + # self.__base_cmd = [ + # get_exec_path_host("sudo"), + # "-E", + # f"-u{self.user}", + # get_exec_path_host("ssh"), + # ] + # + port = 22 + self.__base_cmd = [get_exec_path_host("ssh")] + if port != 22: + self.__base_cmd.append(f"-p{port}") + self.__base_cmd.append("-i") + self.__base_cmd.append(self.ssh_keyfile) + self.__base_cmd.append("-q") + self.__base_cmd.append("-oStrictHostKeyChecking=no") + self.__base_cmd.append("-oUserKnownHostsFile=/dev/null") + # Would be nice but has to be accepted by server config so not very useful. + # self.__base_cmd.append("-oSendVar='TEST'") + self.__base_cmd_pty = list(self.__base_cmd) + self.__base_cmd_pty.append("-t") + + user = self.qemu_config.get("sshuser", "root") + self.__base_cmd.append(f"{user}@{mgmt_ip}") + self.__base_cmd.append("--") + self.__base_cmd_pty.append(f"{user}@{mgmt_ip}") + # self.__base_cmd_pty.append("--") + return True + + def _get_cmd_as_list(self, cmd): + """Given a list or string return a list form for execution. + + If cmd is a string then [cmd] is returned, for most other + node types ["bash", "-c", cmd] is returned but in our case + ssh is the shell. + + Args: + cmd: list or string representing the command to execute. + str_shell: if True and `cmd` is a string then run the + command using bash -c + Returns: + list of commands to execute. + """ + if self.use_ssh and self.launch_p: + return [cmd] if isinstance(cmd, str) else cmd + return super()._get_cmd_as_list(cmd) + + def _get_pre_cmd(self, use_str, use_pty, ns_only=False, root_level=False, **kwargs): + if ns_only: + return super()._get_pre_cmd( + use_str, use_pty, ns_only=True, root_level=root_level, **kwargs + ) + + if not self.launch_p: + self.logger.debug("%s: Running command in namespace b/c no VM", self) + return super()._get_pre_cmd( + use_str, use_pty, ns_only=True, root_level=root_level, **kwargs + ) + + if not self.use_ssh: + self.logger.debug( + "%s: Running command in namespace b/c no SSH configured", self + ) + return super()._get_pre_cmd( + use_str, use_pty, ns_only=True, root_level=root_level, **kwargs + ) + + pre_cmd = self.unet._get_pre_cmd(use_str, use_pty, ns_only=True) + + # This is going to run in the process namespaces. + # We really want it to run in the munet namespace which will + # be different unless unshare_inline was used. + # + # XXX grab the env from kwargs and add to podman exec + # env = kwargs.get("env", {}) + if use_pty: + pre_cmd = pre_cmd + self.__base_cmd_pty + else: + pre_cmd = pre_cmd + self.__base_cmd + return shlex.join(pre_cmd) if use_str else pre_cmd + + async def moncmd(self): + """Uses internal REPL to send cmmand to qemu monitor and get reply.""" + + def tmpfs_mount(self, inner): + # eventually would be nice to support live mounting + self.logger.debug("Mounting tmpfs on %s", inner) + self.extra_mounts.append(("", inner, "")) + + # + # bind_mount is actually being used to mount into the namespace + # + # def bind_mount(self, outer, inner): + # # eventually would be nice to support live mounting + # assert not self.container_id + # if self.test_host("-f", outer): + # self.logger.warning("Can't bind mount files with L3QemuVM: %s", outer) + # return + # self.logger.debug("Bind mounting %s on %s", outer, inner) + # if not self.test_host("-e", outer): + # self.cmd_raises(f"mkdir -p {outer}") + # self.extra_mounts.append((outer, inner, "")) + + def mount_volumes(self): + """Mount volumes from the config.""" + args = [] + for m in self.config.get("volumes", []): + if not isinstance(m, str): + continue + s = m.split(":", 1) + if len(s) == 1: + args.append(("", s[0], "")) + else: + spath = s[0] + spath = os.path.abspath( + os.path.join( + os.path.dirname(self.unet.config["config_pathname"]), spath + ) + ) + if not self.test_nsonly("-e", spath): + self.cmd_raises_nsonly(f"mkdir -p {spath}") + args.append((spath, s[1], "")) + + for m in self.config.get("mounts", []): + src = m.get("src", m.get("source", "")) + if src: + src = os.path.abspath( + os.path.join( + os.path.dirname(self.unet.config["config_pathname"]), src + ) + ) + if not self.test_nsonly("-e", src): + self.cmd_raises_nsonly(f"mkdir -p {src}") + dst = m.get("dst", m.get("destination")) + assert dst, "destination path required for mount" + + margs = [] + for k, v in m.items(): + if k in ["destination", "dst", "source", "src"]: + continue + if k == "type": + assert v in ["bind", "tmpfs"] + continue + if not v: + margs.append(k) + else: + margs.append(f"{k}={v}") + args.append((src, dst, ",".join(margs))) + + if args: + self.extra_mounts += args + + async def run_cmd(self): + """Run the configured commands for this node inside VM.""" + self.logger.debug( + "[rundir %s exists %s]", self.rundir, os.path.exists(self.rundir) + ) + + cmd = self.config.get("cmd", "").strip() + if not cmd: + self.logger.debug("%s: no `cmd` to run", self) + return None + + shell_cmd = self.config.get("shell", "/bin/bash") + if not isinstance(shell_cmd, str): + if shell_cmd: + shell_cmd = "/bin/bash" + else: + shell_cmd = "" + + if shell_cmd: + cmd = cmd.rstrip() + cmd = f"#!{shell_cmd}\n" + cmd + cmd = cmd.replace("%CONFIGDIR%", str(self.unet.config_dirname)) + cmd = cmd.replace("%RUNDIR%", str(self.rundir)) + cmd = cmd.replace("%NAME%", str(self.name)) + cmd += "\n" + + # Write a copy to the rundir + cmdpath = os.path.join(self.rundir, "cmd.shebang") + with open(cmdpath, mode="w+", encoding="utf-8") as cmdfile: + cmdfile.write(cmd) + commander.cmd_raises(f"chmod 755 {cmdpath}") + + # Now write a copy inside the VM + self.conrepl.cmd_status("cat > /tmp/cmd.shebang << EOF\n" + cmd + "\nEOF") + self.conrepl.cmd_status("chmod 755 /tmp/cmd.shebang") + cmds = "/tmp/cmd.shebang" + else: + cmd = cmd.replace("%CONFIGDIR%", str(self.unet.config_dirname)) + cmd = cmd.replace("%RUNDIR%", str(self.rundir)) + cmd = cmd.replace("%NAME%", str(self.name)) + cmds = cmd + + # class future_proc: + # """Treat awaitable minimally as a proc.""" + # def __init__(self, aw): + # self.aw = aw + # # XXX would be nice to have a real value here + # self.returncode = 0 + # async def wait(self): + # if self.aw: + # return await self.aw + # return None + + class now_proc: + """Treat awaitable minimally as a proc.""" + + def __init__(self, output): + self.output = output + self.returncode = 0 + + async def wait(self): + return self.output + + if self.cmdrepl: + # self.cmd_p = future_proc( + # # We need our own console here b/c this is async and not returning + # # immediately + # # self.cmdrepl.run_command(cmds, timeout=120, async_=True) + # self.cmdrepl.run_command(cmds, timeout=120) + # ) + + # When run_command supports async_ arg we can use the above... + self.cmd_p = now_proc(self.cmdrepl.run_command(cmds, timeout=120)) + + # stdout and err both combined into logfile from the spawned repl + stdout = os.path.join(self.rundir, "_cmdcon-log.txt") + self.pytest_hook_run_cmd(stdout, None) + else: + # If we only have a console we can't run in parallel, so run to completion + self.cmd_p = now_proc(self.conrepl.run_command(cmds, timeout=120)) + + return self.cmd_p + + # InterfaceMixin override + # We need a name unique in the shared namespace. + def get_ns_ifname(self, ifname): + return self.name + ifname + + async def add_host_intf(self, hname, lname, mtu=None): + # L3QemuVM needs it's own add_host_intf for macvtap, We need to create the tap + # in the host then move that interface so that the ifindex/devfile are + # different. + + if hname in self.host_intfs: + return + + self.host_intfs[hname] = lname + index = len(self.host_intfs) + + tapindex = self.unet.tapcount + self.unet.tapcount = self.unet.tapcount + 1 + + tapname = f"tap{tapindex}" + self.tapnames[hname] = tapname + + mac = f"02:bb:bb:bb:{index:02x}:{self.id:02x}" + self.tapmacs[hname] = mac + + self.unet.rootcmd.cmd_raises( + f"ip link add link {hname} name {tapname} type macvtap" + ) + if mtu: + self.unet.rootcmd.cmd_raises(f"ip link set {tapname} mtu {mtu}") + self.unet.rootcmd.cmd_raises(f"ip link set {tapname} address {mac} up") + ifindex = self.unet.rootcmd.cmd_raises( + f"cat /sys/class/net/{tapname}/ifindex" + ).strip() + # self.unet.rootcmd.cmd_raises(f"ip link set {tapname} netns {self.pid}") + + tapfile = f"/dev/tap{ifindex}" + fd = os.open(tapfile, os.O_RDWR) + self.tapfds[hname] = fd + self.logger.info( + "%s: Add host intf: created macvtap interface %s (%s) on %s fd %s", + self, + tapname, + tapfile, + hname, + fd, + ) + + async def rem_host_intf(self, hname): + tapname = self.tapnames[hname] + self.unet.rootcmd.cmd_raises(f"ip link set {tapname} down") + self.unet.rootcmd.cmd_raises(f"ip link delete {tapname} type macvtap") + del self.tapnames[hname] + del self.host_intfs[hname] + + async def create_tap(self, index, ifname, mtu=None, driver="virtio-net-pci"): + # XXX we shouldn't be doign a tap on a bridge with a veth + # we should just be using a tap created earlier which was connected to the + # bridge. Except we need to handle the case of p2p qemu <-> namespace + # + ifname = self.get_ns_ifname(ifname) + brname = f"{self.name}br{index}" + + tapindex = self.unet.tapcount + self.unet.tapcount += 1 + + mac = f"02:aa:aa:aa:{index:02x}:{self.id:02x}" + # nic = "tap,model=virtio-net-pci" + # qemu -net nic,model=virtio,addr=1a:46:0b:ca:bc:7b -net tap,fd=3 3<>/dev/tap11 + self.cmd_raises(f"ip address flush dev {ifname}") + self.cmd_raises(f"ip tuntap add tap{tapindex} mode tap") + self.cmd_raises(f"ip link add name {brname} type bridge") + self.cmd_raises(f"ip link set dev {ifname} master {brname}") + self.cmd_raises(f"ip link set dev tap{tapindex} master {brname}") + if mtu: + self.cmd_raises(f"ip link set dev tap{tapindex} mtu {mtu}") + self.cmd_raises(f"ip link set dev {ifname} mtu {mtu}") + self.cmd_raises(f"ip link set dev tap{tapindex} up") + self.cmd_raises(f"ip link set dev {ifname} up") + self.cmd_raises(f"ip link set dev {brname} up") + dev = f"{driver},netdev=n{index},mac={mac}" + return [ + "-netdev", + f"tap,id=n{index},ifname=tap{tapindex},script=no,downscript=no", + "-device", + dev, + ] + + async def mount_mounts(self): + """Mount any shared directories.""" + self.logger.info("Mounting shared directories") + con = self.conrepl + for i, m in enumerate(self.extra_mounts): + outer, mp, uargs = m + if not outer: + con.cmd_raises(f"mkdir -p {mp}") + margs = f"-o {uargs}" if uargs else "" + con.cmd_raises(f"mount {margs} -t tmpfs tmpfs {mp}") + continue + + uargs = "" if uargs is None else uargs + margs = "trans=virtio" + if uargs: + margs += f",{uargs}" + self.logger.info("Mounting %s on %s with %s", outer, mp, margs) + con.cmd_raises(f"mkdir -p {mp}") + con.cmd_raises(f"mount -t 9p -o {margs} shared{i} {mp}") + + async def renumber_interfaces(self): + """Re-number the interfaces. + + After VM comes up need to renumber the interfaces now on the inside. + """ + self.logger.info("Renumbering interfaces") + con = self.conrepl + con.cmd_raises("sysctl -w net.ipv4.ip_forward=1") + if self.unet.ipv6_enable: + self.cmd_raises("sysctl -w net.ipv6.conf.all.forwarding=1") + for ifname in sorted(self.intfs): + conn = find_with_kv(self.config.get("connections"), "name", ifname) + to = conn["to"] + switch = self.unet.switches.get(to) + mtu = conn.get("mtu") + if not mtu and switch: + mtu = switch.config.get("mtu") + if mtu: + con.cmd_raises(f"ip link set {ifname} mtu {mtu}") + con.cmd_raises(f"ip link set {ifname} up") + # In case there was some preconfig e.g., cloud-init + con.cmd_raises(f"ip -4 addr flush dev {ifname}") + sw_is_nat = switch and hasattr(switch, "is_nat") and switch.is_nat + if ifaddr := self.get_intf_addr(ifname, ipv6=False): + con.cmd_raises(f"ip addr add {ifaddr} dev {ifname}") + if sw_is_nat: + # In case there was some preconfig e.g., cloud-init + con.cmd_raises("ip route flush exact default") + con.cmd_raises(f"ip route add default via {switch.ip_address}") + if ifaddr := self.get_intf_addr(ifname, ipv6=True): + con.cmd_raises(f"ip -6 addr add {ifaddr} dev {ifname}") + if sw_is_nat: + # In case there was some preconfig e.g., cloud-init + con.cmd_raises("ip -6 route flush exact default") + con.cmd_raises(f"ip -6 route add default via {switch.ip6_address}") + con.cmd_raises("ip link set lo up") + + if self.unet.pytest_config and self.unet.pytest_config.getoption("--coverage"): + con.cmd_raises("mount -t debugfs none /sys/kernel/debug") + + async def gather_coverage_data(self): + con = self.conrepl + + gcda = "/sys/kernel/debug/gcov" + tmpdir = con.cmd_raises("mktemp -d").strip() + dest = "/gcov-data.tgz" + con.cmd_raises(rf"find {gcda} -type d -exec mkdir -p {tmpdir}/{{}} \;") + con.cmd_raises( + rf"find {gcda} -name '*.gcda' -exec sh -c 'cat < $0 > {tmpdir}/$0' {{}} \;" + ) + con.cmd_raises( + rf"find {gcda} -name '*.gcno' -exec sh -c 'cp -d $0 {tmpdir}/$0' {{}} \;" + ) + con.cmd_raises(rf"tar cf - -C {tmpdir} sys | gzip -c > {dest}") + con.cmd_raises(rf"rm -rf {tmpdir}") + self.logger.info("Saved coverage data in VM at %s", dest) + if self.use_ssh: + ldest = os.path.join(self.rundir, "gcov-data.tgz") + self.cmd_raises(["/bin/cat", dest], stdout=open(ldest, "wb")) + self.logger.info("Saved coverage data on host at %s", ldest) + + async def _opencons( + self, + *cnames, + prompt=None, + is_bourne=True, + user="root", + password="", + expects=None, + sends=None, + timeout=-1, + ): + """Open consoles based on socket file names.""" + timeo = Timeout(timeout) + cons = [] + for cname in cnames: + sockpath = os.path.join(self.sockdir, cname) + connected = False + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + while self.launch_p.returncode is None and not timeo.is_expired(): + try: + sock.connect(sockpath) + connected = True + break + except OSError as error: + if error.errno == errno.ENOENT: + self.logger.debug("waiting for console socket: %s", sockpath) + else: + self.logger.warning( + "can't open console socket: %s", error.strerror + ) + raise + elapsed = int(timeo.elapsed()) + if elapsed <= 3: + await asyncio.sleep(0.25) + else: + self.logger.info( + "%s: launch (qemu) taking more than %ss", self, elapsed + ) + await asyncio.sleep(1) + + if connected: + if prompt is None: + prompt = r"(^|\r\n)[^#\$]*[#\$] " + cons.append( + await self.console( + sock, + prompt=prompt, + is_bourne=is_bourne, + user=user, + password=password, + use_pty=False, + logfile_prefix=cname, + will_echo=True, + expects=expects, + sends=sends, + timeout=timeout, + trace=True, + ) + ) + elif self.launch_p.returncode is not None: + self.logger.warning( + "%s: launch (qemu) exited quickly (%ss) rc: %s", + self, + timeo.elapsed(), + self.launch_p.returncode, + ) + raise Exception("Qemu launch exited early") + elif timeo.is_expired(): + self.logger.critical( + "%s: timeout (%ss) waiting for qemu to start", + self, + timeo.elapsed(), + ) + assert not timeo.is_expired() + + return cons + + async def set_cpu_affinity(self, afflist): + for i, aff in enumerate(afflist): + if not aff: + continue + # affmask = convert_ranges_to_bitmask(aff) + if i not in self.cpu_thread_map: + logging.warning("affinity %s given for missing vcpu %s", aff, i) + continue + logging.info("setting vcpu %s affinity to %s", i, aff) + tid = self.cpu_thread_map[i] + self.cmd_raises_nsonly(f"taskset -cp {aff} {tid}") + + async def launch(self): + """Launch qemu.""" + self.logger.info("%s: Launch Qemu", self) + + qc = self.qemu_config + cc = qc.get("console", {}) + bootd = "d" if "iso" in qc else "c" + # args = [get_exec_path_host("qemu-system-x86_64"), + # "-nodefaults", "-boot", bootd] + args = [get_exec_path_host("qemu-system-x86_64"), "-boot", bootd] + + args += ["-machine", "q35"] + + if qc.get("kvm"): + rc, _, e = await self.async_cmd_status_nsonly("ls -l /dev/kvm") + if rc: + self.logger.warning("Can't enable KVM no /dev/kvm: %s", e) + else: + # [args += ["-enable-kvm", "-cpu", "host"] + # uargs += ["-accel", "kvm", "-cpu", "Icelake-Server-v5"] + args += ["-accel", "kvm", "-cpu", "host"] + + if ncpu := qc.get("ncpu"): + # args += ["-smp", f"sockets={ncpu}"] + args += ["-smp", f"cores={ncpu}"] + # args += ["-smp", f"{ncpu},sockets={ncpu},cores=1,threads=1"] + + args.extend(["-m", str(qc.get("memory", "512M"))]) + + if "bios" in qc: + if qc["bios"] == "open-firmware": + args.extend(["-bios", "/usr/share/qemu/OVMF.fd"]) + else: + args.extend(["-bios", qc["bios"]]) + if "kernel" in qc: + args.extend(["-kernel", qc["kernel"]]) + if "initrd" in qc: + args.extend(["-initrd", qc["initrd"]]) + if "iso" in qc: + args.extend(["-cdrom", qc["iso"]]) + + # we only have append if we have a kernel + if "kernel" in qc: + args.append("-append") + root = qc.get("root", "/dev/ram0") + # Only 1 serial console the other ports (ttyS[123] hvc[01]) should have + # gettys in inittab + append = f"root={root} rw console=ttyS0" + if "cmdline-extra" in qc: + append += f" {qc['cmdline-extra']}" + args.append(append) + + if "extra-args" in qc: + if isinstance(qc["extra-args"], list): + args.extend(qc["extra-args"]) + else: + args.extend(shlex.split(qc["extra-args"])) + + # Walk the list of connections in order so we attach them the same way + pass_fds = [] + nnics = 0 + pciaddr = 3 + for index, conn in enumerate(self.config["connections"]): + devaddr = conn.get("physical", "") + hostintf = conn.get("hostintf", "") + if devaddr: + # if devaddr in self.tapmacs: + # mac = f",mac={self.tapmacs[devaddr]}" + # else: + # mac = "" + args += ["-device", f"vfio-pci,host={devaddr},addr={pciaddr}"] + elif hostintf: + fd = self.tapfds[hostintf] + mac = self.tapmacs[hostintf] + args += [ + "-nic", + f"tap,model=virtio-net-pci,mac={mac},fd={fd},addr={pciaddr}", + ] + pass_fds.append(fd) + nnics += 1 + elif not hostintf: + driver = conn.get("driver", "virtio-net-pci") + mtu = conn.get("mtu") + if not mtu and conn["to"] in self.unet.switches: + mtu = self.unet.switches[conn["to"]].config.get("mtu") + tapargs = await self.create_tap( + index, conn["name"], mtu=mtu, driver=driver + ) + tapargs[-1] += f",addr={pciaddr}" + args += tapargs + nnics += 1 + pciaddr += 1 + if not nnics: + args += ["-nic", "none"] + + dtpl = qc.get("disk-template") + diskpath = disk = qc.get("disk") + if dtpl and not disk: + disk = qc["disk"] = f"{self.name}-{os.path.basename(dtpl)}" + diskpath = os.path.join(self.rundir, disk) + if self.path_exists(diskpath): + logging.debug("Disk '%s' file exists, using.", diskpath) + else: + dtplpath = os.path.abspath( + os.path.join( + os.path.dirname(self.unet.config["config_pathname"]), dtpl + ) + ) + logging.info("Create disk '%s' from template '%s'", diskpath, dtplpath) + self.cmd_raises( + f"qemu-img create -f qcow2 -F qcow2 -b {dtplpath} {diskpath}" + ) + + if diskpath: + args.extend( + ["-drive", f"file={diskpath},if=none,id=sata-disk0,format=qcow2"] + ) + args.extend(["-device", "ahci,id=ahci"]) + args.extend(["-device", "ide-hd,bus=ahci.0,drive=sata-disk0"]) + + use_stdio = cc.get("stdio", True) + has_cmd = self.config.get("cmd") + use_cmdcon = has_cmd and use_stdio + + # + # Any extra serial/console ports beyond thw first, require entries in + # inittab to have getty running on them, modify inittab + # + # Use -serial stdio for output only, and as the first serial console + # which kernel uses for printk, as it has serious issues with dropped + # input chars for some reason. + # + # 4 serial ports (max), we'll add extra ports using virtual consoles. + _sd = self.sockdir + if use_stdio: + args += ["-serial", "stdio"] + args += ["-serial", f"unix:{_sd}/_console,server,nowait"] + if use_cmdcon: + args += [ + "-serial", + f"unix:{_sd}/_cmdcon,server,nowait", + ] + args += [ + "-serial", + f"unix:{_sd}/console,server,nowait", + # A 2 virtual consoles - /dev/hvc[01] + # Requires CONFIG_HVC_DRIVER=y CONFIG_VIRTIO_CONSOLE=y + "-device", + "virtio-serial", # serial console bus + "-chardev", + f"socket,path={_sd}/vcon0,server=on,wait=off,id=vcon0", + "-chardev", + f"socket,path={_sd}/vcon1,server=on,wait=off,id=vcon1", + "-device", + "virtconsole,chardev=vcon0", + "-device", + "virtconsole,chardev=vcon1", + # 2 monitors + "-monitor", + f"unix:{_sd}/_monitor,server,nowait", + "-monitor", + f"unix:{_sd}/monitor,server,nowait", + "-gdb", + f"unix:{_sd}/gdbserver,server,nowait", + ] + + for i, m in enumerate(self.extra_mounts): + args += [ + "-virtfs", + f"local,path={m[0]},mount_tag=shared{i},security_model=passthrough", + ] + + args += ["-nographic"] + + # + # Launch Qemu + # + + stdout = open(os.path.join(self.rundir, "qemu.out"), "wb") + stderr = open(os.path.join(self.rundir, "qemu.err"), "wb") + self.launch_p = await self.async_popen( + args, + stdin=subprocess.DEVNULL, + stdout=stdout, + stderr=stderr, + pass_fds=pass_fds, + # We don't need this here b/c we are only ever running qemu and that's all + # we need to kill for cleanup + # XXX reconcile this + start_new_session=True, # allows us to signal all children to exit + ) + + self.pytest_hook_run_cmd(stdout, stderr) + + # We've passed these on, so don't need these open here anymore. + for fd in pass_fds: + os.close(fd) + + self.logger.debug("%s: async_popen => %s", self, self.launch_p.pid) + + confiles = ["_console"] + if use_cmdcon: + confiles.append("_cmdcon") + + # + # Connect to the console socket, retrying + # + prompt = cc.get("prompt") + cons = await self._opencons( + *confiles, + prompt=prompt, + is_bourne=not bool(prompt), + user=cc.get("user", "root"), + password=cc.get("password", ""), + expects=cc.get("expects"), + sends=cc.get("sends"), + timeout=int(cc.get("timeout", 60)), + ) + self.conrepl = cons[0] + if use_cmdcon: + self.cmdrepl = cons[1] + self.monrepl = await self.monitor(os.path.join(self.sockdir, "_monitor")) + + # the monitor output has super annoying ANSI escapes in it + + output = self.monrepl.cmd_nostatus("info status") + self.logger.info("VM status: %s", output) + + output = self.monrepl.cmd_nostatus("info kvm") + self.logger.info("KVM status: %s", output) + + # + # Set thread affinity + # + output = self.monrepl.cmd_nostatus("info cpus") + matches = re.findall(r"CPU #(\d+): *thread_id=(\d+)", output) + self.cpu_thread_map = {int(k): int(v) for k, v in matches} + if cpuaff := self.qemu_config.get("cpu-affinity"): + await self.set_cpu_affinity(cpuaff) + + self.is_kvm = "disabled" not in output + + if qc.get("unix-os", True): + await self.renumber_interfaces() + + if self.extra_mounts: + await self.mount_mounts() + + self.use_ssh = bool(self.ssh_keyfile) + if self.use_ssh: + self.use_ssh = self.__setup_ssh() + + self.pytest_hook_open_shell() + + return self.launch_p + + def launch_completed(self, future): + self.logger.debug("%s: launch (qemu) completed called", self) + self.use_ssh = False + try: + n = future.result() + self.logger.debug("%s: node launch (qemu) completed result: %s", self, n) + except asyncio.CancelledError as error: + self.logger.debug( + "%s: node launch (qemu) cmd wait() canceled: %s", future, error + ) + + async def cleanup_qemu(self): + """Launch qemu.""" + if self.launch_p: + await self.async_cleanup_proc(self.launch_p) + + async def async_cleanup_cmd(self): + """Run the configured cleanup commands for this node.""" + self.cleanup_called = True + + if "cleanup-cmd" not in self.config: + return + + if not self.launch_p: + self.logger.warning("async_cleanup_cmd: qemu no longer running") + return + + raise NotImplementedError("Needs to be like run_cmd") + # return await self._async_cleanup_cmd() + + async def _async_delete(self): + self.logger.debug("%s: deleting", self) + + # Need to cleanup early b/c it is running on the VM + if self.cmd_p: + await self.async_cleanup_proc(self.cmd_p) + self.cmd_p = None + + try: + # Need to cleanup early b/c it is running on the VM + if not self.cleanup_called: + await self.async_cleanup_cmd() + except Exception as error: + self.logger.warning( + "Got an error during delete from async_cleanup_cmd: %s", error + ) + + try: + if not self.launch_p: + self.logger.warning("async_delete: qemu is not running") + else: + await self.cleanup_qemu() + except Exception as error: + self.logger.warning("%s: failued to cleanup qemu process: %s", self, error) + + await super()._async_delete() + + +class Munet(BaseMunet): + """Munet.""" + + def __init__( + self, + rundir=None, + config=None, + pytestconfig=None, + pid=True, + logger=None, + **kwargs, + ): + # logging.warning("Munet") + + if not rundir: + rundir = "/tmp/munet" + + if logger is None: + logger = logging.getLogger("munet.unet") + + super().__init__("munet", pid=pid, rundir=rundir, logger=logger, **kwargs) + + self.built = False + self.tapcount = 0 + + self.cmd_raises(f"mkdir -p {self.rundir} && chmod 755 {self.rundir}") + self.set_ns_cwd(self.rundir) + + if not config: + config = {} + self.config = config + if "config_pathname" in config: + self.config_pathname = os.path.realpath(config["config_pathname"]) + self.config_dirname = os.path.dirname(self.config_pathname) + else: + self.config_pathname = "" + self.config_dirname = "" + + self.pytest_config = pytestconfig + + # Done in BaseMunet now + # # We need some way to actually get back to the root namespace + # if not self.isolated: + # self.rootcmd = commander + # else: + # spid = str(pid) + # nsflags = (f"--mount={self.proc_path / spid / 'ns/mnt'}", + # f"--net={self.proc_path / spid / 'ns/net'}", + # f"--uts={self.proc_path / spid / 'ns/uts'}", + # f"--ipc={self.proc_path / spid / 'ns/ipc'}", + # f"--cgroup={self.proc_path / spid / 'ns/cgroup'}", + # f"--pid={self.proc_path / spid / 'ns/net'}", + # self.rootcmd = SharedNamespace("host", pid=1, nsflags=nsflags) + + # Save the namespace pid + with open(os.path.join(self.rundir, "nspid"), "w", encoding="ascii") as f: + f.write(f"{self.pid}\n") + + with open(os.path.join(self.rundir, "nspids"), "w", encoding="ascii") as f: + f.write(f'{" ".join([str(x) for x in self.pids])}\n') + + hosts_file = os.path.join(self.rundir, "hosts.txt") + with open(hosts_file, "w", encoding="ascii") as hf: + hf.write( + f"""127.0.0.1\tlocalhost {self.name} +::1\tip6-localhost ip6-loopback +fe00::0\tip6-localnet +ff00::0\tip6-mcastprefix +ff02::1\tip6-allnodes +ff02::2\tip6-allrouters +""" + ) + self.bind_mount(hosts_file, "/etc/hosts") + + # Common CLI commands for any topology + cdict = { + "commands": [ + { + "name": "pcap", + "format": "pcap NETWORK", + "help": ( + "capture packets from NETWORK into file capture-NETWORK.pcap" + " the command is run within a new window which also shows" + " packet summaries. NETWORK can also be an interface specified" + " as HOST:INTF. To capture inside the host namespace." + ), + "exec": "tshark -s 9200 -i {0} -P -w capture-{0}.pcap", + "top-level": True, + "new-window": {"background": True}, + }, + { + "name": "nsterm", + "format": "nsterm HOST [HOST ...]", + "help": ( + "open terminal[s] in the namespace only" + " (outside containers or VM), * for all" + ), + "exec": "bash", + "new-window": {"ns_only": True}, + }, + { + "name": "term", + "format": "term HOST [HOST ...]", + "help": "open terminal[s] (TMUX or XTerm) on HOST[S], * for all", + "exec": "bash", + "new-window": True, + }, + { + "name": "xterm", + "format": "xterm HOST [HOST ...]", + "help": "open XTerm[s] on HOST[S], * for all", + "exec": "bash", + "new-window": { + "forcex": True, + }, + }, + { + "name": "sh", + "format": "[HOST ...] sh <SHELL-COMMAND>", + "help": "execute <SHELL-COMMAND> on hosts", + "exec": "{}", + }, + { + "name": "shi", + "format": "[HOST ...] shi <INTERACTIVE-COMMAND>", + "help": "execute <INTERACTIVE-COMMAND> on HOST[s]", + "exec": "{}", + "interactive": True, + }, + { + "name": "stdout", + "exec": ( + "[ -e %RUNDIR%/qemu.out ] && tail -F %RUNDIR%/qemu.out " + "|| tail -F %RUNDIR%/cmd.out" + ), + "format": "stdout HOST [HOST ...]", + "help": "tail -f on the stdout of the qemu/cmd for this node", + "new-window": True, + }, + { + "name": "stderr", + "exec": ( + "[ -e %RUNDIR%/qemu.err ] && tail -F %RUNDIR%/qemu.err " + "|| tail -F %RUNDIR%/cmd.err" + ), + "format": "stderr HOST [HOST ...]", + "help": "tail -f on the stdout of the qemu/cmd for this node", + "new-window": True, + }, + ] + } + + cli.add_cli_config(self, cdict) + + if "cli" in config: + cli.add_cli_config(self, config["cli"]) + + if "topology" not in self.config: + self.config["topology"] = {} + + self.topoconf = self.config["topology"] + self.ipv6_enable = self.topoconf.get("ipv6-enable", False) + + if self.isolated: + if not self.ipv6_enable: + # Disable IPv6 + self.cmd_raises("sysctl -w net.ipv6.conf.all.autoconf=0") + self.cmd_raises("sysctl -w net.ipv6.conf.all.disable_ipv6=1") + else: + self.cmd_raises("sysctl -w net.ipv6.conf.all.autoconf=1") + self.cmd_raises("sysctl -w net.ipv6.conf.all.disable_ipv6=0") + + # we really need overlay, but overlay-layers (used by overlay-images) + # counts on things being present in overlay so this temp stuff doesn't work. + # if self.isolated: + # # Let's hide podman details + # self.tmpfs_mount("/var/lib/containers/storage/overlay-containers") + + shellopt = ( + self.pytest_config.getoption("--shell") if self.pytest_config else None + ) + shellopt = shellopt if shellopt is not None else "" + if shellopt == "all" or "." in shellopt.split(","): + self.run_in_window("bash") + + def __del__(self): + """Catch case of build object but not async_deleted.""" + if hasattr(self, "built"): + if not self.deleting: + logging.critical( + "Munet object deleted without calling `async_delete` for cleanup." + ) + s = super() + if hasattr(s, "__del__"): + s.__del__(self) + + async def _async_build(self, logger=None): + """Build the topology based on config.""" + if self.built: + self.logger.warning("%s: is already built", self) + return + + self.built = True + + # Allow for all networks to be auto-numbered + topoconf = self.topoconf + autonumber = self.autonumber + ipv6_enable = self.ipv6_enable + + # --------------------------------------------- + # Merge Kinds and perform variable substitution + # --------------------------------------------- + + kinds = self.config.get("kinds", {}) + + for name, conf in config_to_dict_with_key(topoconf, "networks", "name").items(): + if kind := conf.get("kind"): + if kconf := kinds[kind]: + conf = merge_kind_config(kconf, conf) + conf = config_subst( + conf, name=name, rundir=self.rundir, configdir=self.config_dirname + ) + if "ip" not in conf and autonumber: + conf["ip"] = "auto" + if "ipv6" not in conf and autonumber and ipv6_enable: + conf["ipv6"] = "auto" + topoconf["networks"][name] = conf + self.add_network(name, conf, logger=logger) + + for name, conf in config_to_dict_with_key(topoconf, "nodes", "name").items(): + if kind := conf.get("kind"): + if kconf := kinds[kind]: + conf = merge_kind_config(kconf, conf) + + config_to_dict_with_key( + conf, "env", "name" + ) # convert list of env objects to dict + + conf = config_subst( + conf, + name=name, + rundir=os.path.join(self.rundir, name), + configdir=self.config_dirname, + ) + topoconf["nodes"][name] = conf + self.add_l3_node(name, conf, logger=logger) + + # ------------------ + # Create connections + # ------------------ + + # Go through all connections and name them so they are sane to the user + # otherwise when we do p2p links the names/ords skip around based oddly + for name, node in self.hosts.items(): + nconf = node.config + if "connections" not in nconf: + continue + nconns = [] + for cconf in nconf["connections"]: + # Replace string only with a dictionary + if isinstance(cconf, str): + splitconf = cconf.split(":", 1) + cconf = {"to": splitconf[0]} + if len(splitconf) == 2: + cconf["name"] = splitconf[1] + # Allocate a name if not already assigned + if "name" not in cconf: + cconf["name"] = node.get_next_intf_name() + nconns.append(cconf) + nconf["connections"] = nconns + + for name, node in self.hosts.items(): + nconf = node.config + if "connections" not in nconf: + continue + for cconf in nconf["connections"]: + # Eventually can add support for unconnected intf here. + if "to" not in cconf: + continue + to = cconf["to"] + if to in self.switches: + switch = self.switches[to] + swconf = find_matching_net_config(name, cconf, switch.config) + await self.add_native_link(switch, node, swconf, cconf) + elif cconf["name"] not in node.intfs: + # Only add the p2p interface if not already there. + other = self.hosts[to] + oconf = find_matching_net_config(name, cconf, other.config) + await self.add_native_link(node, other, cconf, oconf) + + @property + def autonumber(self): + return self.topoconf.get("networks-autonumber", False) + + @autonumber.setter + def autonumber(self, value): + self.topoconf["networks-autonumber"] = bool(value) + + async def add_native_link(self, node1, node2, c1=None, c2=None): + """Add a link between switch and node or 2 nodes.""" + isp2p = False + + c1 = {} if c1 is None else c1 + c2 = {} if c2 is None else c2 + + if node1.name in self.switches: + assert node2.name in self.hosts + elif node2.name in self.switches: + assert node1.name in self.hosts + node1, node2 = node2, node1 + c1, c2 = c2, c1 + else: + # p2p link + assert node1.name in self.hosts + assert node1.name in self.hosts + isp2p = True + + if "name" not in c1: + c1["name"] = node1.get_next_intf_name() + if1 = c1["name"] + + if "name" not in c2: + c2["name"] = node2.get_next_intf_name() + if2 = c2["name"] + + do_add_link = True + for n, c in ((node1, c1), (node2, c2)): + if "hostintf" in c: + await n.add_host_intf(c["hostintf"], c["name"], mtu=c.get("mtu")) + do_add_link = False + elif "physical" in c: + await n.add_phy_intf(c["physical"], c["name"]) + do_add_link = False + if do_add_link: + assert "hostintf" not in c1 + assert "hostintf" not in c2 + assert "physical" not in c1 + assert "physical" not in c2 + + if isp2p: + mtu1 = c1.get("mtu") + mtu2 = c2.get("mtu") + mtu = mtu1 if mtu1 else mtu2 + if mtu1 and mtu2 and mtu1 != mtu2: + self.logger.error("mtus differ for add_link %s != %s", mtu1, mtu2) + else: + mtu = c2.get("mtu") + + super().add_link(node1, node2, if1, if2, mtu=mtu) + + if isp2p: + node1.set_p2p_addr(node2, c1, c2) + else: + node2.set_lan_addr(node1, c2) + + if "physical" not in c1 and not node1.is_vm: + node1.set_intf_constraints(if1, **c1) + if "physical" not in c2 and not node2.is_vm: + node2.set_intf_constraints(if2, **c2) + + def add_l3_node(self, name, config=None, **kwargs): + """Add a node to munet.""" + if config and config.get("image"): + cls = L3ContainerNode + elif config and config.get("qemu"): + cls = L3QemuVM + elif config and config.get("server"): + cls = SSHRemote + kwargs["server"] = config["server"] + kwargs["port"] = int(config.get("server-port", 22)) + if "ssh-identity-file" in config: + kwargs["idfile"] = config.get("ssh-identity-file") + if "ssh-user" in config: + kwargs["user"] = config.get("ssh-user") + if "ssh-password" in config: + kwargs["password"] = config.get("ssh-password") + else: + cls = L3NamespaceNode + return super().add_host(name, cls=cls, config=config, **kwargs) + + def add_network(self, name, config=None, **kwargs): + """Add a l2 or l3 switch to munet.""" + if config is None: + config = {} + + cls = L3Bridge if config.get("ip") else L2Bridge + mtu = kwargs.get("mtu", config.get("mtu")) + return super().add_switch(name, cls=cls, config=config, mtu=mtu, **kwargs) + + async def run(self): + tasks = [] + + hosts = self.hosts.values() + launch_nodes = [x for x in hosts if hasattr(x, "launch")] + launch_nodes = [x for x in launch_nodes if x.config.get("qemu")] + run_nodes = [x for x in hosts if hasattr(x, "has_run_cmd") and x.has_run_cmd()] + ready_nodes = [ + x for x in hosts if hasattr(x, "has_ready_cmd") and x.has_ready_cmd() + ] + + if not self.pytest_config: + pcapopt = "" + else: + pcapopt = self.pytest_config.getoption("--pcap") + pcapopt = pcapopt if pcapopt else "" + if pcapopt == "all": + pcapopt = self.switches.keys() + if pcapopt: + for pcap in pcapopt.split(","): + if ":" in pcap: + host, intf = pcap.split(":") + pcap = f"{host}-{intf}" + host = self.hosts[host] + else: + host = self + intf = pcap + host.run_in_window( + f"tshark -s 9200 -i {intf} -P -w capture-{pcap}.pcap", + background=True, + title=f"cap:{pcap}", + ) + + if launch_nodes: + # would like a info when verbose here. + logging.debug("Launching nodes") + await asyncio.gather(*[x.launch() for x in launch_nodes]) + + # Watch for launched processes to exit + for node in launch_nodes: + task = asyncio.create_task( + node.launch_p.wait(), name=f"Node-{node.name}-launch" + ) + task.add_done_callback(node.launch_completed) + tasks.append(task) + + if run_nodes: + # would like a info when verbose here. + logging.debug("Running `cmd` on nodes") + await asyncio.gather(*[x.run_cmd() for x in run_nodes]) + + # Watch for run_cmd processes to exit + for node in run_nodes: + task = asyncio.create_task(node.cmd_p.wait(), name=f"Node-{node.name}-cmd") + task.add_done_callback(node.cmd_completed) + tasks.append(task) + + # Wait for nodes to be ready + if ready_nodes: + + async def wait_until_ready(x): + while not await x.async_ready_cmd(): + logging.debug("Waiting for ready on: %s", x) + await asyncio.sleep(0.25) + logging.debug("%s is ready!", x) + + logging.debug("Waiting for ready on nodes: %s", ready_nodes) + _, pending = await asyncio.wait( + [wait_until_ready(x) for x in ready_nodes], timeout=30 + ) + if pending: + logging.warning("Timeout waiting for ready: %s", pending) + for nr in pending: + nr.cancel() + raise asyncio.TimeoutError() + logging.debug("All nodes ready") + + return tasks + + async def _async_delete(self): + from .testing.util import async_pause_test # pylint: disable=C0415 + + self.logger.debug("%s: deleting.", self) + + if self.pytest_config and self.pytest_config.getoption("--coverage"): + nodes = ( + x for x in self.hosts.values() if hasattr(x, "gather_coverage_data") + ) + try: + await asyncio.gather(*(x.gather_coverage_data() for x in nodes)) + except Exception as error: + logging.warning("Error gathering coverage data: %s", error) + + if not self.pytest_config: + pause = False + else: + pause = bool(self.pytest_config.getoption("--pause-at-end")) + pause = pause or bool(self.pytest_config.getoption("--pause")) + if pause: + try: + await async_pause_test("Before MUNET delete") + except KeyboardInterrupt: + print("^C...continuing") + except Exception as error: + self.logger.error("\n...continuing after error: %s", error) + + # XXX should we cancel launch and run tasks? + + try: + await super()._async_delete() + except Exception as error: + self.logger.error("Error cleaning up: %s", error, exc_info=True) + raise + + +async def run_cmd_update_ceos(node, shell_cmd, cmds, cmd): + cmd = cmd.strip() + if shell_cmd or cmd != "/sbin/init": + return cmds, cmd + + # + # Add flash dir and mount it + # + flashdir = os.path.join(node.rundir, "flash") + node.cmd_raises_nsonly(f"mkdir -p {flashdir} && chmod 775 {flashdir}") + cmds += [f"--volume={flashdir}:/mnt/flash"] + + # + # Startup config (if not present already) + # + if startup_config := node.config.get("startup-config", None): + dest = os.path.join(flashdir, "startup-config") + if os.path.exists(dest): + node.logger.info("Skipping copy of startup-config, already present") + else: + source = os.path.join(node.unet.config_dirname, startup_config) + node.cmd_raises_nsonly(f"cp {source} {dest} && chmod 664 {dest}") + + # + # system mac address (if not present already + # + dest = os.path.join(flashdir, "system_mac_address") + if os.path.exists(dest): + node.logger.info("Skipping system-mac generation, already present") + else: + random_arista_mac = "00:1c:73:%02x:%02x:%02x" % ( + random.randint(0, 255), + random.randint(0, 255), + random.randint(0, 255), + ) + system_mac = node.config.get("system-mac", random_arista_mac) + with open(dest, "w", encoding="ascii") as f: + f.write(system_mac + "\n") + node.cmd_raises_nsonly(f"chmod 664 {dest}") + + args = [] + + # Pass special args for the environment variables + if "env" in node.config: + args += [f"systemd.setenv={k}={v}" for k, v in node.config["env"].items()] + + return cmds, [cmd] + args + + +# XXX this is only used by the container code +kind_run_cmd_update = {"ceos": run_cmd_update_ceos} diff --git a/tests/topotests/munet/parser.py b/tests/topotests/munet/parser.py new file mode 100644 index 0000000000..f92138d7b8 --- /dev/null +++ b/tests/topotests/munet/parser.py @@ -0,0 +1,374 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# September 30 2021, Christian Hopps <chopps@labn.net> +# +# Copyright 2021, LabN Consulting, L.L.C. +# +"""A module that implements the standalone parser.""" +import asyncio +import importlib.resources +import json +import logging +import logging.config +import os +import subprocess +import sys +import tempfile + +from pathlib import Path + + +try: + import jsonschema # pylint: disable=C0415 + import jsonschema.validators # pylint: disable=C0415 + + from jsonschema.exceptions import ValidationError # pylint: disable=C0415 +except ImportError: + jsonschema = None + +from .config import list_to_dict_with_key +from .native import Munet + + +def get_schema(): + if get_schema.schema is None: + with importlib.resources.path("munet", "munet-schema.json") as datapath: + search = [str(datapath.parent)] + get_schema.schema = get_config(basename="munet-schema", search=search) + return get_schema.schema + + +get_schema.schema = None + +project_root_contains = [ + ".git", + "pyproject.toml", + "tox.ini", + "setup.cfg", + "setup.py", + "pytest.ini", + ".projectile", +] + + +def is_project_root(path: Path) -> bool: + + for contains in project_root_contains: + if path.joinpath(contains).exists(): + return True + return False + + +def find_project_root(config_path: Path, project_root=None): + if project_root is not None: + project_root = Path(project_root) + if project_root in config_path.parents: + return project_root + logging.warning( + "project_root %s is not a common ancestor of config file %s", + project_root, + config_path, + ) + return config_path.parent + for ppath in config_path.parents: + if is_project_root(ppath): + return ppath + return config_path.parent + + +def get_config(pathname=None, basename="munet", search=None, logf=logging.debug): + + cwd = os.getcwd() + + if not search: + search = [cwd] + elif isinstance(search, (str, Path)): + search = [search] + + if pathname: + pathname = os.path.join(cwd, pathname) + if not os.path.exists(pathname): + raise FileNotFoundError(pathname) + else: + for d in search: + logf("%s", f'searching in "{d}" for "{basename}".{{yaml, toml, json}}') + for ext in ("yaml", "toml", "json"): + pathname = os.path.join(d, basename + "." + ext) + if os.path.exists(pathname): + logf("%s", f'Found "{pathname}"') + break + else: + continue + break + else: + raise FileNotFoundError(basename + ".{json,toml,yaml} in " + f"{search}") + + _, ext = pathname.rsplit(".", 1) + + if ext == "json": + config = json.load(open(pathname, encoding="utf-8")) + elif ext == "toml": + import toml # pylint: disable=C0415 + + config = toml.load(pathname) + elif ext == "yaml": + import yaml # pylint: disable=C0415 + + config = yaml.safe_load(open(pathname, encoding="utf-8")) + else: + raise ValueError("Filename does not end with (.json|.toml|.yaml)") + + config["config_pathname"] = os.path.realpath(pathname) + return config + + +def setup_logging(args, config_base="logconf"): + # Create rundir and arrange for future commands to run in it. + + # Change CWD to the rundir prior to parsing config + old = os.getcwd() + os.chdir(args.rundir) + try: + search = [old] + with importlib.resources.path("munet", config_base + ".yaml") as datapath: + search.append(str(datapath.parent)) + + def logf(msg, *p, **k): + if args.verbose: + print("PRELOG: " + msg % p, **k, file=sys.stderr) + + config = get_config(args.log_config, config_base, search, logf=logf) + pathname = config["config_pathname"] + del config["config_pathname"] + + if "info_console" in config["handlers"]: + # mutest case + if args.verbose > 1: + config["handlers"]["console"]["level"] = "DEBUG" + config["handlers"]["info_console"]["level"] = "DEBUG" + elif args.verbose: + config["handlers"]["console"]["level"] = "INFO" + config["handlers"]["info_console"]["level"] = "DEBUG" + elif args.verbose: + # munet case + config["handlers"]["console"]["level"] = "DEBUG" + + # add the rundir path to the filenames + for v in config["handlers"].values(): + filename = v.get("filename") + if not filename: + continue + v["filename"] = os.path.join(args.rundir, filename) + + logging.config.dictConfig(dict(config)) + logging.info("Loaded logging config %s", pathname) + + return config + finally: + os.chdir(old) + + +def append_hosts_files(unet, netname): + if not netname: + return + + entries = [] + for name in ("munet", *list(unet.hosts)): + if name == "munet": + node = unet.switches[netname] + ifname = None + else: + node = unet.hosts[name] + if not hasattr(node, "_intf_addrs"): + continue + ifname = node.get_ifname(netname) + + for b in (False, True): + ifaddr = node.get_intf_addr(ifname, ipv6=b) + if ifaddr and hasattr(ifaddr, "ip"): + entries.append((name, ifaddr.ip)) + + for name in ("munet", *list(unet.hosts)): + node = unet if name == "munet" else unet.hosts[name] + if not hasattr(node, "rundir"): + continue + with open(os.path.join(node.rundir, "hosts.txt"), "a+", encoding="ascii") as hf: + hf.write("\n") + for e in entries: + hf.write(f"{e[1]}\t{e[0]}\n") + + +def validate_config(config, logger, args): + if jsonschema is None: + logger.debug("No validation w/o jsonschema module") + return True + + old = os.getcwd() + if args: + os.chdir(args.rundir) + + try: + validator = jsonschema.validators.Draft202012Validator(get_schema()) + validator.validate(instance=config) + logger.debug("Validated %s", config["config_pathname"]) + return True + except FileNotFoundError as error: + logger.info("No schema found: %s", error) + return False + except ValidationError as error: + logger.info("Validation failed: %s", error) + return False + finally: + if args: + os.chdir(old) + + +def load_kinds(args, search=None): + # Change CWD to the rundir prior to parsing config + cwd = os.getcwd() + if args: + os.chdir(args.rundir) + + args_config = args.kinds_config if args else None + try: + if search is None: + search = [cwd] + with importlib.resources.path("munet", "kinds.yaml") as datapath: + search.append(str(datapath.parent)) + + configs = [] + if args_config: + configs.append(get_config(args_config, "kinds", search=[])) + else: + # prefer directories at the front of the list + for kdir in search: + try: + configs.append(get_config(basename="kinds", search=[kdir])) + except FileNotFoundError: + continue + + kinds = {} + for config in configs: + # XXX need to fix the issue with `connections: ["net0"]` not validating + # if jsonschema is not None: + # validator = jsonschema.validators.Draft202012Validator(get_schema()) + # validator.validate(instance=config) + + kinds_list = config.get("kinds", []) + kinds_dict = list_to_dict_with_key(kinds_list, "name") + if kinds_dict: + logging.info("Loading kinds config from %s", config["config_pathname"]) + if "kinds" in kinds: + kinds["kinds"].update(**kinds_dict) + else: + kinds["kinds"] = kinds_dict + + cli_list = config.get("cli", {}).get("commands", []) + if cli_list: + logging.info("Loading cli comands from %s", config["config_pathname"]) + if "cli" not in kinds: + kinds["cli"] = {} + if "commands" not in kinds["cli"]: + kinds["cli"]["commands"] = [] + kinds["cli"]["commands"].extend(cli_list) + + return kinds + except FileNotFoundError as error: + # if we have kinds in args but the file doesn't exist, raise the error + if args_config is not None: + raise error + return {} + finally: + if args: + os.chdir(cwd) + + +async def async_build_topology( + config=None, + logger=None, + rundir=None, + args=None, + unshare_inline=False, + pytestconfig=None, + search_root=None, + top_level_pidns=True, +): + + if not rundir: + rundir = tempfile.mkdtemp(prefix="unet") + subprocess.run(f"mkdir -p {rundir} && chmod 755 {rundir}", check=True, shell=True) + + isolated = not args.host if args else True + if not config: + config = get_config(basename="munet") + + # create search directories from common root if given + cpath = Path(config["config_pathname"]).absolute() + project_root = args.project_root if args else None + if not search_root: + search_root = find_project_root(cpath, project_root) + if not search_root: + search = [cpath.parent] + else: + search_root = Path(search_root).absolute() + if search_root in cpath.parents: + search = list(cpath.parents) + if remcount := len(search_root.parents): + search = search[0:-remcount] + + # load kinds along search path and merge into config + kinds = load_kinds(args, search=search) + config_kinds_dict = list_to_dict_with_key(config.get("kinds", []), "name") + config["kinds"] = {**kinds.get("kinds", {}), **config_kinds_dict} + + # mere CLI command from kinds into config as well. + kinds_cli_list = kinds.get("cli", {}).get("commands", []) + config_cli_list = config.get("cli", {}).get("commands", []) + if config_cli_list: + if kinds_cli_list: + config_cli_list.extend(list(kinds_cli_list)) + elif kinds_cli_list: + if "cli" not in config: + config["cli"] = {} + if "commands" not in config["cli"]: + config["cli"]["commands"] = [] + config["cli"]["commands"].extend(list(kinds_cli_list)) + + unet = Munet( + rundir=rundir, + config=config, + pytestconfig=pytestconfig, + isolated=isolated, + pid=top_level_pidns, + unshare_inline=args.unshare_inline if args else unshare_inline, + logger=logger, + ) + + try: + await unet._async_build(logger) # pylint: disable=W0212 + except Exception as error: + logging.critical("Failure building munet topology: %s", error, exc_info=True) + await unet.async_delete() + raise + except KeyboardInterrupt: + await unet.async_delete() + raise + + topoconf = config.get("topology") + if not topoconf: + return unet + + dns_network = topoconf.get("dns-network") + if dns_network: + append_hosts_files(unet, dns_network) + + # Write our current config to the run directory + with open(f"{unet.rundir}/config.json", "w", encoding="utf-8") as f: + json.dump(unet.config, f, indent=2) + + return unet + + +def build_topology(config=None, logger=None, rundir=None, args=None, pytestconfig=None): + return asyncio.run(async_build_topology(config, logger, rundir, args, pytestconfig)) diff --git a/tests/topotests/munet/testing/__init__.py b/tests/topotests/munet/testing/__init__.py new file mode 100644 index 0000000000..63cbfabda9 --- /dev/null +++ b/tests/topotests/munet/testing/__init__.py @@ -0,0 +1 @@ +"""Sub-package supporting munet use in pytest.""" diff --git a/tests/topotests/munet/testing/fixtures.py b/tests/topotests/munet/testing/fixtures.py new file mode 100644 index 0000000000..25039df541 --- /dev/null +++ b/tests/topotests/munet/testing/fixtures.py @@ -0,0 +1,447 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# April 22 2022, Christian Hopps <chopps@gmail.com> +# +# Copyright (c) 2022, LabN Consulting, L.L.C +# +"""A module that implements pytest fixtures. + +To use in your project, in your conftest.py add: + + from munet.testing.fixtures import * +""" +import contextlib +import logging +import os + +from pathlib import Path +from typing import Union + +import pytest +import pytest_asyncio + +from ..base import BaseMunet +from ..base import Bridge +from ..base import get_event_loop +from ..cleanup import cleanup_current +from ..cleanup import cleanup_previous +from ..native import L3NodeMixin +from ..parser import async_build_topology +from ..parser import get_config +from .util import async_pause_test +from .util import pause_test + + +@contextlib.asynccontextmanager +async def achdir(ndir: Union[str, Path], desc=""): + odir = os.getcwd() + os.chdir(ndir) + if desc: + logging.debug("%s: chdir from %s to %s", desc, odir, ndir) + try: + yield + finally: + if desc: + logging.debug("%s: chdir back from %s to %s", desc, ndir, odir) + os.chdir(odir) + + +@contextlib.contextmanager +def chdir(ndir: Union[str, Path], desc=""): + odir = os.getcwd() + os.chdir(ndir) + if desc: + logging.debug("%s: chdir from %s to %s", desc, odir, ndir) + try: + yield + finally: + if desc: + logging.debug("%s: chdir back from %s to %s", desc, ndir, odir) + os.chdir(odir) + + +def get_test_logdir(nodeid=None, module=False): + """Get log directory relative pathname.""" + xdist_worker = os.getenv("PYTEST_XDIST_WORKER", "") + mode = os.getenv("PYTEST_XDIST_MODE", "no") + + # nodeid: all_protocol_startup/test_all_protocol_startup.py::test_router_running + # may be missing "::testname" if module is True + if not nodeid: + nodeid = os.environ["PYTEST_CURRENT_TEST"].split(" ")[0] + + cur_test = nodeid.replace("[", "_").replace("]", "_") + if module: + idx = cur_test.rfind("::") + path = cur_test if idx == -1 else cur_test[:idx] + testname = "" + else: + path, testname = cur_test.split("::") + testname = testname.replace("/", ".") + path = path[:-3].replace("/", ".") + + # We use different logdir paths based on how xdist is running. + if mode == "each": + if module: + return os.path.join(path, "worker-logs", xdist_worker) + return os.path.join(path, testname, xdist_worker) + assert mode in ("no", "load", "loadfile", "loadscope"), f"Unknown dist mode {mode}" + return path if module else os.path.join(path, testname) + + +def _push_log_handler(desc, logpath): + logpath = os.path.abspath(logpath) + logging.debug("conftest: adding %s logging at %s", desc, logpath) + root_logger = logging.getLogger() + handler = logging.FileHandler(logpath, mode="w") + fmt = logging.Formatter("%(asctime)s %(levelname)5s: %(message)s") + handler.setFormatter(fmt) + root_logger.addHandler(handler) + return handler + + +def _pop_log_handler(handler): + root_logger = logging.getLogger() + logging.debug("conftest: removing logging handler %s", handler) + root_logger.removeHandler(handler) + + +@contextlib.contextmanager +def log_handler(desc, logpath): + handler = _push_log_handler(desc, logpath) + try: + yield + finally: + _pop_log_handler(handler) + + +# ================= +# Sessions Fixtures +# ================= + + +@pytest.fixture(autouse=True, scope="session") +def session_autouse(): + if "PYTEST_TOPOTEST_WORKER" not in os.environ: + is_worker = False + elif not os.environ["PYTEST_TOPOTEST_WORKER"]: + is_worker = False + else: + is_worker = True + + if not is_worker: + # This is unfriendly to multi-instance + cleanup_previous() + + # We never pop as we want to keep logging + _push_log_handler("session", "/tmp/unet-test/pytest-session.log") + + yield + + if not is_worker: + cleanup_current() + + +# =============== +# Module Fixtures +# =============== + + +@pytest.fixture(autouse=True, scope="module") +def module_autouse(request): + logpath = get_test_logdir(request.node.name, True) + logpath = os.path.join("/tmp/unet-test", logpath, "pytest-exec.log") + with log_handler("module", logpath): + sdir = os.path.dirname(os.path.realpath(request.fspath)) + with chdir(sdir, "module autouse fixture"): + yield + + if BaseMunet.g_unet: + raise Exception("Base Munet was not cleaned up/deleted") + + +@pytest.fixture(scope="module") +def event_loop(): + """Create an instance of the default event loop for the session.""" + loop = get_event_loop() + try: + logging.info("event_loop_fixture: yielding with new event loop watcher") + yield loop + finally: + loop.close() + + +@pytest.fixture(scope="module") +def rundir_module(): + d = os.path.join("/tmp/unet-test", get_test_logdir(module=True)) + logging.debug("conftest: test module rundir %s", d) + return d + + +async def _unet_impl( + _rundir, _pytestconfig, unshare=None, top_level_pidns=None, param=None +): + try: + # Default is not to unshare inline if not specified otherwise + unshare_default = False + pidns_default = True + if isinstance(param, (tuple, list)): + pidns_default = bool(param[2]) if len(param) > 2 else True + unshare_default = bool(param[1]) if len(param) > 1 else False + param = str(param[0]) + elif isinstance(param, bool): + unshare_default = param + param = None + if unshare is None: + unshare = unshare_default + if top_level_pidns is None: + top_level_pidns = pidns_default + + logging.info("unet fixture: basename=%s unshare_inline=%s", param, unshare) + _unet = await async_build_topology( + config=get_config(basename=param) if param else None, + rundir=_rundir, + unshare_inline=unshare, + top_level_pidns=top_level_pidns, + pytestconfig=_pytestconfig, + ) + except Exception as error: + logging.debug( + "unet fixture: unet build failed: %s\nparam: %s", + error, + param, + exc_info=True, + ) + pytest.skip( + f"unet fixture: unet build failed: {error}", allow_module_level=True + ) + raise + + try: + tasks = await _unet.run() + except Exception as error: + logging.debug("unet fixture: unet run failed: %s", error, exc_info=True) + await _unet.async_delete() + pytest.skip(f"unet fixture: unet run failed: {error}", allow_module_level=True) + raise + + logging.debug("unet fixture: containers running") + + # Pytest is supposed to always return even if exceptions + try: + yield _unet + except Exception as error: + logging.error("unet fixture: yield unet unexpected exception: %s", error) + + logging.debug("unet fixture: module done, deleting unet") + await _unet.async_delete() + + # No one ever awaits these so cancel them + logging.debug("unet fixture: cleanup") + for task in tasks: + task.cancel() + + # Reset the class variables so auto number is predictable + logging.debug("unet fixture: resetting ords to 1") + L3NodeMixin.next_ord = 1 + Bridge.next_ord = 1 + + +@pytest.fixture(scope="module") +async def unet(request, rundir_module, pytestconfig): # pylint: disable=W0621 + """A unet creating fixutre. + + The request param is either the basename of the config file or a tuple of the form: + (basename, unshare, top_level_pidns), with the second and third elements boolean and + optional, defaulting to False, True. + """ + param = request.param if hasattr(request, "param") else None + sdir = os.path.dirname(os.path.realpath(request.fspath)) + async with achdir(sdir, "unet fixture"): + async for x in _unet_impl(rundir_module, pytestconfig, param=param): + yield x + + +@pytest.fixture(scope="module") +async def unet_share(request, rundir_module, pytestconfig): # pylint: disable=W0621 + """A unet creating fixutre. + + This share variant keeps munet from unsharing the process to a new namespace so that + root level commands and actions are execute on the host, normally they are executed + in the munet namespace which allowing things like scapy inline in tests to work. + + The request param is either the basename of the config file or a tuple of the form: + (basename, top_level_pidns), the second value is a boolean. + """ + param = request.param if hasattr(request, "param") else None + if isinstance(param, (tuple, list)): + param = (param[0], False, param[1]) + sdir = os.path.dirname(os.path.realpath(request.fspath)) + async with achdir(sdir, "unet_share fixture"): + async for x in _unet_impl( + rundir_module, pytestconfig, unshare=False, param=param + ): + yield x + + +@pytest.fixture(scope="module") +async def unet_unshare(request, rundir_module, pytestconfig): # pylint: disable=W0621 + """A unet creating fixutre. + + This unshare variant has the top level munet unshare the process inline so that + root level commands and actions are execute in a new namespace. This allows things + like scapy inline in tests to work. + + The request param is either the basename of the config file or a tuple of the form: + (basename, top_level_pidns), the second value is a boolean. + """ + param = request.param if hasattr(request, "param") else None + if isinstance(param, (tuple, list)): + param = (param[0], True, param[1]) + sdir = os.path.dirname(os.path.realpath(request.fspath)) + async with achdir(sdir, "unet_unshare fixture"): + async for x in _unet_impl( + rundir_module, pytestconfig, unshare=True, param=param + ): + yield x + + +# ================= +# Function Fixtures +# ================= + + +@pytest.fixture(autouse=True, scope="function") +async def function_autouse(request): + async with achdir( + os.path.dirname(os.path.realpath(request.fspath)), "func.fixture" + ): + yield + + +@pytest.fixture(autouse=True) +async def check_for_pause(request, pytestconfig): + # When we unshare inline we can't pause in the pytest_runtest_makereport hook + # so do it here. + if BaseMunet.g_unet and BaseMunet.g_unet.unshare_inline: + pause = bool(pytestconfig.getoption("--pause")) + if pause: + await async_pause_test(f"XXX before test '{request.node.name}'") + yield + + +@pytest.fixture(scope="function") +def stepf(pytestconfig): + class Stepnum: + """Track the stepnum in closure.""" + + num = 0 + + def inc(self): + self.num += 1 + + pause = pytestconfig.getoption("pause") + stepnum = Stepnum() + + def stepfunction(desc=""): + desc = f": {desc}" if desc else "" + if pause: + pause_test(f"before step {stepnum.num}{desc}") + logging.info("STEP %s%s", stepnum.num, desc) + stepnum.inc() + + return stepfunction + + +@pytest_asyncio.fixture(scope="function") +async def astepf(pytestconfig): + class Stepnum: + """Track the stepnum in closure.""" + + num = 0 + + def inc(self): + self.num += 1 + + pause = pytestconfig.getoption("pause") + stepnum = Stepnum() + + async def stepfunction(desc=""): + desc = f": {desc}" if desc else "" + if pause: + await async_pause_test(f"before step {stepnum.num}{desc}") + logging.info("STEP %s%s", stepnum.num, desc) + stepnum.inc() + + return stepfunction + + +@pytest.fixture(scope="function") +def rundir(): + d = os.path.join("/tmp/unet-test", get_test_logdir(module=False)) + logging.debug("conftest: test function rundir %s", d) + return d + + +# Configure logging +@pytest.hookimpl(hookwrapper=True, tryfirst=True) +def pytest_runtest_setup(item): + d = os.path.join( + "/tmp/unet-test", get_test_logdir(nodeid=item.nodeid, module=False) + ) + config = item.config + logging_plugin = config.pluginmanager.get_plugin("logging-plugin") + filename = Path(d, "pytest-exec.log") + logging_plugin.set_log_path(str(filename)) + logging.debug("conftest: test function setup: rundir %s", d) + yield + + +@pytest.fixture +async def unet_perfunc(request, rundir, pytestconfig): # pylint: disable=W0621 + param = request.param if hasattr(request, "param") else None + async for x in _unet_impl(rundir, pytestconfig, param=param): + yield x + + +@pytest.fixture +async def unet_perfunc_unshare(request, rundir, pytestconfig): # pylint: disable=W0621 + """Build unet per test function with an optional topology basename parameter. + + The fixture can be parameterized to choose different config files. + For example, use as follows to run the test with unet_perfunc configured + first with a config file named `cfg1.yaml` then with config file `cfg2.yaml` + (where the actual files could end with `json` or `toml` rather than `yaml`). + + @pytest.mark.parametrize( + "unet_perfunc", ["cfg1", "cfg2]", indirect=["unet_perfunc"] + ) + def test_example(unet_perfunc) + """ + param = request.param if hasattr(request, "param") else None + async for x in _unet_impl(rundir, pytestconfig, unshare=True, param=param): + yield x + + +@pytest.fixture +async def unet_perfunc_share(request, rundir, pytestconfig): # pylint: disable=W0621 + """Build unet per test function with an optional topology basename parameter. + + This share variant keeps munet from unsharing the process to a new namespace so that + root level commands and actions are execute on the host, normally they are executed + in the munet namespace which allowing things like scapy inline in tests to work. + + The fixture can be parameterized to choose different config files. For example, use + as follows to run the test with unet_perfunc configured first with a config file + named `cfg1.yaml` then with config file `cfg2.yaml` (where the actual files could + end with `json` or `toml` rather than `yaml`). + + @pytest.mark.parametrize( + "unet_perfunc", ["cfg1", "cfg2]", indirect=["unet_perfunc"] + ) + def test_example(unet_perfunc) + """ + param = request.param if hasattr(request, "param") else None + async for x in _unet_impl(rundir, pytestconfig, unshare=False, param=param): + yield x diff --git a/tests/topotests/munet/testing/hooks.py b/tests/topotests/munet/testing/hooks.py new file mode 100644 index 0000000000..9b6a49a18c --- /dev/null +++ b/tests/topotests/munet/testing/hooks.py @@ -0,0 +1,225 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# April 22 2022, Christian Hopps <chopps@gmail.com> +# +# Copyright (c) 2022, LabN Consulting, L.L.C +# +"""A module that implements pytest hooks. + +To use in your project, in your conftest.py add: + + from munet.testing.hooks import * +""" +import logging +import os +import sys +import traceback + +import pytest + +from ..base import BaseMunet # pylint: disable=import-error +from ..cli import cli # pylint: disable=import-error +from .util import pause_test + + +# =================== +# Hooks (non-fixture) +# =================== + + +def pytest_addoption(parser): + parser.addoption( + "--cli-on-error", + action="store_true", + help="CLI on test failure", + ) + + parser.addoption( + "--coverage", + action="store_true", + help="Enable coverage gathering if supported", + ) + + parser.addoption( + "--gdb", + default="", + metavar="HOST[,HOST...]", + help="Comma-separated list of nodes to launch gdb on, or 'all'", + ) + parser.addoption( + "--gdb-breakpoints", + default="", + metavar="BREAKPOINT[,BREAKPOINT...]", + help="Comma-separated list of breakpoints", + ) + parser.addoption( + "--gdb-use-emacs", + action="store_true", + help="Use emacsclient to run gdb instead of a shell", + ) + + parser.addoption( + "--pcap", + default="", + metavar="NET[,NET...]", + help="Comma-separated list of networks to capture packets on, or 'all'", + ) + + parser.addoption( + "--pause", + action="store_true", + help="Pause after each test", + ) + parser.addoption( + "--pause-at-end", + action="store_true", + help="Pause before taking munet down", + ) + parser.addoption( + "--pause-on-error", + action="store_true", + help="Pause after (disables default when --shell or -vtysh given)", + ) + parser.addoption( + "--no-pause-on-error", + dest="pause_on_error", + action="store_false", + help="Do not pause after (disables default when --shell or -vtysh given)", + ) + + parser.addoption( + "--shell", + default="", + metavar="NODE[,NODE...]", + help="Comma-separated list of nodes to spawn shell on, or 'all'", + ) + + parser.addoption( + "--stdout", + default="", + metavar="NODE[,NODE...]", + help="Comma-separated list of nodes to open tail-f stdout window on, or 'all'", + ) + + parser.addoption( + "--stderr", + default="", + metavar="NODE[,NODE...]", + help="Comma-separated list of nodes to open tail-f stderr window on, or 'all'", + ) + + +def pytest_configure(config): + if "PYTEST_XDIST_WORKER" not in os.environ: + os.environ["PYTEST_XDIST_MODE"] = config.getoption("dist", "no") + os.environ["PYTEST_IS_WORKER"] = "" + is_xdist = os.environ["PYTEST_XDIST_MODE"] != "no" + is_worker = False + else: + os.environ["PYTEST_IS_WORKER"] = os.environ["PYTEST_XDIST_WORKER"] + is_xdist = True + is_worker = True + + # Turn on live logging if user specified verbose and the config has a CLI level set + if config.getoption("--verbose") and not is_xdist and not config.getini("log_cli"): + if config.getoption("--log-cli-level", None) is None: + # By setting the CLI option to the ini value it enables log_cli=1 + cli_level = config.getini("log_cli_level") + if cli_level is not None: + config.option.log_cli_level = cli_level + + have_tmux = bool(os.getenv("TMUX", "")) + have_screen = not have_tmux and bool(os.getenv("STY", "")) + have_xterm = not have_tmux and not have_screen and bool(os.getenv("DISPLAY", "")) + have_windows = have_tmux or have_screen or have_xterm + have_windows_pause = have_tmux or have_xterm + xdist_no_windows = is_xdist and not is_worker and not have_windows_pause + + for winopt in ["--shell", "--stdout", "--stderr"]: + b = config.getoption(winopt) + if b and xdist_no_windows: + pytest.exit( + f"{winopt} use requires byobu/TMUX/XTerm " + f"under dist {os.environ['PYTEST_XDIST_MODE']}" + ) + elif b and not is_xdist and not have_windows: + pytest.exit(f"{winopt} use requires byobu/TMUX/SCREEN/XTerm") + + +def pytest_runtest_makereport(item, call): + """Pause or invoke CLI as directed by config.""" + isatty = sys.stdout.isatty() + + pause = bool(item.config.getoption("--pause")) + skipped = False + + if call.excinfo is None: + error = False + elif call.excinfo.typename == "Skipped": + skipped = True + error = False + pause = False + else: + error = True + modname = item.parent.module.__name__ + exval = call.excinfo.value + logging.error( + "test %s/%s failed: %s: stdout: '%s' stderr: '%s'", + modname, + item.name, + exval, + exval.stdout if hasattr(exval, "stdout") else "NA", + exval.stderr if hasattr(exval, "stderr") else "NA", + ) + if not pause: + pause = item.config.getoption("--pause-on-error") + + if error and isatty and item.config.getoption("--cli-on-error"): + if not BaseMunet.g_unet: + logging.error("Could not launch CLI b/c no munet exists yet") + else: + print(f"\nCLI-ON-ERROR: {call.excinfo.typename}") + print(f"CLI-ON-ERROR:\ntest {modname}/{item.name} failed: {exval}") + if hasattr(exval, "stdout") and exval.stdout: + print("stdout: " + exval.stdout.replace("\n", "\nstdout: ")) + if hasattr(exval, "stderr") and exval.stderr: + print("stderr: " + exval.stderr.replace("\n", "\nstderr: ")) + cli(BaseMunet.g_unet) + + if pause: + if skipped: + item.skip_more_pause = True + elif hasattr(item, "skip_more_pause"): + pass + elif call.when == "setup": + if error: + item.skip_more_pause = True + + # we can't asyncio.run() (which pause does) if we are unhsare_inline + # at this point, count on an autouse fixture to pause instead in this + # case + if not BaseMunet.g_unet or not BaseMunet.g_unet.unshare_inline: + pause_test(f"before test '{item.nodeid}'") + + # check for a result to try and catch setup (or module setup) failure + # e.g., after a module level fixture fails, we do not want to pause on every + # skipped test. + elif call.when == "teardown" and call.excinfo: + logging.warning( + "Caught exception during teardown: %s\n:Traceback:\n%s", + call.excinfo, + "".join(traceback.format_tb(call.excinfo.tb)), + ) + pause_test(f"after teardown after test '{item.nodeid}'") + elif call.when == "teardown" and call.result: + pause_test(f"after test '{item.nodeid}'") + elif error: + item.skip_more_pause = True + print(f"\nPAUSE-ON-ERROR: {call.excinfo.typename}") + print(f"PAUSE-ON-ERROR:\ntest {modname}/{item.name} failed: {exval}") + if hasattr(exval, "stdout") and exval.stdout: + print("stdout: " + exval.stdout.replace("\n", "\nstdout: ")) + if hasattr(exval, "stderr") and exval.stderr: + print("stderr: " + exval.stderr.replace("\n", "\nstderr: ")) + pause_test(f"PAUSE-ON-ERROR: '{item.nodeid}'") diff --git a/tests/topotests/munet/testing/util.py b/tests/topotests/munet/testing/util.py new file mode 100644 index 0000000000..a1a94bcd1b --- /dev/null +++ b/tests/topotests/munet/testing/util.py @@ -0,0 +1,110 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# April 22 2022, Christian Hopps <chopps@gmail.com> +# +# Copyright (c) 2022, LabN Consulting, L.L.C +# +"""Utility functions useful when using munet testing functionailty in pytest.""" +import asyncio +import datetime +import functools +import logging +import sys +import time + +from ..base import BaseMunet +from ..cli import async_cli + + +# ================= +# Utility Functions +# ================= + + +async def async_pause_test(desc=""): + isatty = sys.stdout.isatty() + if not isatty: + desc = f" for {desc}" if desc else "" + logging.info("NO PAUSE on non-tty terminal%s", desc) + return + + while True: + if desc: + print(f"\n== PAUSING: {desc} ==") + try: + user = input('PAUSED, "cli" for CLI, "pdb" to debug, "Enter" to continue: ') + except EOFError: + print("^D...continuing") + break + user = user.strip() + if user == "cli": + await async_cli(BaseMunet.g_unet) + elif user == "pdb": + breakpoint() # pylint: disable=W1515 + elif user: + print(f'Unrecognized input: "{user}"') + else: + break + + +def pause_test(desc=""): + asyncio.run(async_pause_test(desc)) + + +def retry(retry_timeout, initial_wait=0, expected=True): + """decorator: retry while functions return is not None or raises an exception. + + * `retry_timeout`: Retry for at least this many seconds; after waiting + initial_wait seconds + * `initial_wait`: Sleeps for this many seconds before first executing function + * `expected`: if False then the return logic is inverted, except for exceptions, + (i.e., a non None ends the retry loop, and returns that value) + """ + + def _retry(func): + @functools.wraps(func) + def func_retry(*args, **kwargs): + retry_sleep = 2 + + # Allow the wrapped function's args to override the fixtures + _retry_timeout = kwargs.pop("retry_timeout", retry_timeout) + _expected = kwargs.pop("expected", expected) + _initial_wait = kwargs.pop("initial_wait", initial_wait) + retry_until = datetime.datetime.now() + datetime.timedelta( + seconds=_retry_timeout + _initial_wait + ) + + if initial_wait > 0: + logging.info("Waiting for [%s]s as initial delay", initial_wait) + time.sleep(initial_wait) + + while True: + seconds_left = (retry_until - datetime.datetime.now()).total_seconds() + try: + ret = func(*args, **kwargs) + if _expected and ret is None: + logging.debug("Function succeeds") + return ret + logging.debug("Function returned %s", ret) + except Exception as error: + logging.info("Function raised exception: %s", str(error)) + ret = error + + if seconds_left < 0: + logging.info("Retry timeout of %ds reached", _retry_timeout) + if isinstance(ret, Exception): + raise ret + return ret + + logging.info( + "Sleeping %ds until next retry with %.1f retry time left", + retry_sleep, + seconds_left, + ) + time.sleep(retry_sleep) + + func_retry._original = func # pylint: disable=W0212 + return func_retry + + return _retry diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_lan.py b/tests/topotests/ospf_basic_functionality/test_ospf_lan.py index c166702958..e4a032018c 100644 --- a/tests/topotests/ospf_basic_functionality/test_ospf_lan.py +++ b/tests/topotests/ospf_basic_functionality/test_ospf_lan.py @@ -165,9 +165,9 @@ def test_ospf_lan_tc1_p0(request): "r0": { "ospf": { "neighbors": { - "r1": {"state": "Full", "role": "DR"}, - "r2": {"state": "Full", "role": "DROther"}, - "r3": {"state": "Full", "role": "DROther"}, + "r1": {"nbrState": "Full", "role": "DR"}, + "r2": {"nbrState": "Full", "role": "DROther"}, + "r3": {"nbrState": "Full", "role": "DROther"}, } } } @@ -185,9 +185,9 @@ def test_ospf_lan_tc1_p0(request): "r1": { "ospf": { "neighbors": { - "r0": {"state": "Full", "role": "Backup"}, - "r2": {"state": "Full", "role": "DROther"}, - "r3": {"state": "Full", "role": "DROther"}, + "r0": {"nbrState": "Full", "role": "Backup"}, + "r2": {"nbrState": "Full", "role": "DROther"}, + "r3": {"nbrState": "Full", "role": "DROther"}, } } } @@ -196,7 +196,8 @@ def test_ospf_lan_tc1_p0(request): result = verify_ospf_neighbor(tgen, topo, dut, input_dict, lan=True) assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) - step("Configure DR pririty 100 on R0 and clear ospf neighbors on all the routers.") + step("Configure DR priority 100 on R0 and clear ospf neighbors " + "on all the routers.") input_dict = { "r0": { @@ -221,9 +222,9 @@ def test_ospf_lan_tc1_p0(request): "r0": { "ospf": { "neighbors": { - "r1": {"state": "Full", "role": "Backup"}, - "r2": {"state": "Full", "role": "DROther"}, - "r3": {"state": "Full", "role": "DROther"}, + "r1": {"nbrState": "Full", "role": "Backup"}, + "r2": {"nbrState": "Full", "role": "DROther"}, + "r3": {"nbrState": "Full", "role": "DROther"}, } } } @@ -232,7 +233,8 @@ def test_ospf_lan_tc1_p0(request): result = verify_ospf_neighbor(tgen, topo, dut, input_dict, lan=True) assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) - step("Configure DR pririty 150 on R0 and clear ospf neighbors on all the routers.") + step("Configure DR priority 150 on R0 and clear ospf neighbors " + "on all the routers.") input_dict = { "r0": { @@ -257,9 +259,9 @@ def test_ospf_lan_tc1_p0(request): "r0": { "ospf": { "neighbors": { - "r1": {"state": "Full", "role": "Backup"}, - "r2": {"state": "Full", "role": "DROther"}, - "r3": {"state": "Full", "role": "DROther"}, + "r1": {"nbrState": "Full", "role": "Backup"}, + "r2": {"nbrState": "Full", "role": "DROther"}, + "r3": {"nbrState": "Full", "role": "DROther"}, } } } @@ -293,9 +295,9 @@ def test_ospf_lan_tc1_p0(request): "r0": { "ospf": { "neighbors": { - "r1": {"state": "Full", "role": "DR"}, - "r2": {"state": "2-Way", "role": "DROther"}, - "r3": {"state": "2-Way", "role": "DROther"}, + "r1": {"nbrState": "Full", "role": "DR"}, + "r2": {"nbrState": "2-Way", "role": "DROther"}, + "r3": {"nbrState": "2-Way", "role": "DROther"}, } } } @@ -332,9 +334,9 @@ def test_ospf_lan_tc1_p0(request): "r0": { "ospf": { "neighbors": { - "r1": {"state": "Full", "role": "Backup"}, - "r2": {"state": "Full", "role": "DROther"}, - "r3": {"state": "Full", "role": "DROther"}, + "r1": {"nbrState": "Full", "role": "Backup"}, + "r2": {"nbrState": "Full", "role": "DROther"}, + "r3": {"nbrState": "Full", "role": "DROther"}, } } } @@ -364,9 +366,9 @@ def test_ospf_lan_tc1_p0(request): "r0": { "ospf": { "neighbors": { - "r1": {"state": "Full", "role": "DR"}, - "r2": {"state": "Full", "role": "DROther"}, - "r3": {"state": "Full", "role": "DROther"}, + "r1": {"nbrState": "Full", "role": "DR"}, + "r2": {"nbrState": "Full", "role": "DROther"}, + "r3": {"nbrState": "Full", "role": "DROther"}, } } } @@ -419,9 +421,9 @@ def test_ospf_lan_tc1_p0(request): "r1": { "ospf": { "neighbors": { - "r0": {"state": "Full", "role": "Backup"}, - "r2": {"state": "Full", "role": "DROther"}, - "r3": {"state": "Full", "role": "DROther"}, + "r0": {"nbrState": "Full", "role": "Backup"}, + "r2": {"nbrState": "Full", "role": "DROther"}, + "r3": {"nbrState": "Full", "role": "DROther"}, } } } @@ -445,9 +447,9 @@ def test_ospf_lan_tc1_p0(request): "r1": { "ospf": { "neighbors": { - "r0": {"state": "Full", "role": "Backup"}, - "r2": {"state": "Full", "role": "DROther"}, - "r3": {"state": "Full", "role": "DROther"}, + "r0": {"nbrState": "Full", "role": "Backup"}, + "r2": {"nbrState": "Full", "role": "DROther"}, + "r3": {"nbrState": "Full", "role": "DROther"}, } } } diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_p2mp.py b/tests/topotests/ospf_basic_functionality/test_ospf_p2mp.py index 858412f1d3..4f797743e7 100644 --- a/tests/topotests/ospf_basic_functionality/test_ospf_p2mp.py +++ b/tests/topotests/ospf_basic_functionality/test_ospf_p2mp.py @@ -411,17 +411,17 @@ def test_ospf_nbrs(tgen): "neighbors": { "100.1.1.1": [ { - "state": "Full/DROther", + "nbrState": "Full/DROther", } ], "100.1.1.2": [ { - "state": "Full/DROther", + "nbrState": "Full/DROther", } ], "100.1.1.3": [ { - "state": "Full/DROther", + "nbrState": "Full/DROther", } ], } @@ -434,17 +434,17 @@ def test_ospf_nbrs(tgen): "neighbors": { "100.1.1.0": [ { - "state": "Full/DROther", + "nbrState": "Full/DROther", } ], "100.1.1.2": [ { - "state": "Full/DROther", + "nbrState": "Full/DROther", } ], "100.1.1.3": [ { - "state": "Full/DROther", + "nbrState": "Full/DROther", } ], } @@ -457,17 +457,17 @@ def test_ospf_nbrs(tgen): "neighbors": { "100.1.1.0": [ { - "state": "Full/DROther", + "nbrState": "Full/DROther", } ], "100.1.1.1": [ { - "state": "Full/DROther", + "nbrState": "Full/DROther", } ], "100.1.1.3": [ { - "state": "Full/DROther", + "nbrState": "Full/DROther", } ], } @@ -480,17 +480,17 @@ def test_ospf_nbrs(tgen): "neighbors": { "100.1.1.0": [ { - "state": "Full/DROther", + "nbrState": "Full/DROther", } ], "100.1.1.1": [ { - "state": "Full/DROther", + "nbrState": "Full/DROther", } ], "100.1.1.2": [ { - "state": "Full/DROther", + "nbrState": "Full/DROther", } ], } diff --git a/tests/topotests/ospf_gr_helper/test_ospf_gr_helper1.py b/tests/topotests/ospf_gr_helper/test_ospf_gr_helper1.py index 9dce5a976d..79374281cb 100644 --- a/tests/topotests/ospf_gr_helper/test_ospf_gr_helper1.py +++ b/tests/topotests/ospf_gr_helper/test_ospf_gr_helper1.py @@ -188,7 +188,7 @@ def test_ospf_gr_helper_tc1_p0(request): input_dict = { "helperSupport": "Disabled", "strictLsaCheck": "Enabled", - "restartSupoort": "Planned and Unplanned Restarts", + "restartSupport": "Planned and Unplanned Restarts", "supportedGracePeriod": 1800, } dut = "r0" @@ -220,7 +220,7 @@ def test_ospf_gr_helper_tc1_p0(request): input_dict = { "helperSupport": "Enabled", "strictLsaCheck": "Enabled", - "restartSupoort": "Planned and Unplanned Restarts", + "restartSupport": "Planned and Unplanned Restarts", "supportedGracePeriod": 1800, } dut = "r0" diff --git a/tests/topotests/ospf_gr_helper/test_ospf_gr_helper2.py b/tests/topotests/ospf_gr_helper/test_ospf_gr_helper2.py index 202afe5485..46c0da309f 100644 --- a/tests/topotests/ospf_gr_helper/test_ospf_gr_helper2.py +++ b/tests/topotests/ospf_gr_helper/test_ospf_gr_helper2.py @@ -189,7 +189,9 @@ def test_ospf_gr_helper_tc3_p1(request): assert ( ospf_covergence is True ), "OSPF is not after reset config \n Error: {}".format(ospf_covergence) - step("Configure DR pririty 100 on R0 and clear ospf neighbors on all the routers.") + + step("Configure DR priority 100 on R0 and clear ospf neighbors " + "on all the routers.") input_dict = { "r0": { @@ -214,9 +216,9 @@ def test_ospf_gr_helper_tc3_p1(request): "r0": { "ospf": { "neighbors": { - "r1": {"state": "Full", "role": "Backup"}, - "r2": {"state": "Full", "role": "DROther"}, - "r3": {"state": "Full", "role": "DROther"}, + "r1": {"nbrState": "Full", "role": "Backup"}, + "r2": {"nbrState": "Full", "role": "DROther"}, + "r3": {"nbrState": "Full", "role": "DROther"}, } } } @@ -281,7 +283,7 @@ def test_ospf_gr_helper_tc4_p1(request): assert ( ospf_covergence is True ), "OSPF is not after reset config \n Error: {}".format(ospf_covergence) - step("Configure DR pririty 100 on R0 and clear ospf neighbors on all the routers.") + step("Configure DR priority 0 on R0 and clear ospf neighbors on all the routers.") input_dict = { "r0": { @@ -306,9 +308,9 @@ def test_ospf_gr_helper_tc4_p1(request): "r0": { "ospf": { "neighbors": { - "r1": {"state": "Full", "role": "DR"}, - "r2": {"state": "2-Way", "role": "DROther"}, - "r3": {"state": "2-Way", "role": "DROther"}, + "r1": {"nbrState": "Full", "role": "DR"}, + "r2": {"nbrState": "2-Way", "role": "DROther"}, + "r3": {"nbrState": "2-Way", "role": "DROther"}, } } } diff --git a/tests/topotests/ospf_gr_topo1/rt1/show_ip_ospf_neighbor.json b/tests/topotests/ospf_gr_topo1/rt1/show_ip_ospf_neighbor.json index f82758101c..0212f9da9c 100644 --- a/tests/topotests/ospf_gr_topo1/rt1/show_ip_ospf_neighbor.json +++ b/tests/topotests/ospf_gr_topo1/rt1/show_ip_ospf_neighbor.json @@ -3,7 +3,7 @@ "2.2.2.2":[ { "converged":"Full", - "address":"10.0.1.2", + "ifaceAddress":"10.0.1.2", "ifaceName":"eth-rt2:10.0.1.1" } ] diff --git a/tests/topotests/ospf_gr_topo1/rt2/show_ip_ospf_neighbor.json b/tests/topotests/ospf_gr_topo1/rt2/show_ip_ospf_neighbor.json index 5a0b092702..3114660483 100644 --- a/tests/topotests/ospf_gr_topo1/rt2/show_ip_ospf_neighbor.json +++ b/tests/topotests/ospf_gr_topo1/rt2/show_ip_ospf_neighbor.json @@ -3,14 +3,14 @@ "1.1.1.1":[ { "converged":"Full", - "address":"10.0.1.1", + "ifaceAddress":"10.0.1.1", "ifaceName":"eth-rt1:10.0.1.2" } ], "3.3.3.3":[ { "converged":"Full", - "address":"10.0.2.3", + "ifaceAddress":"10.0.2.3", "ifaceName":"eth-rt3:10.0.2.2" } ] diff --git a/tests/topotests/ospf_gr_topo1/rt3/show_ip_ospf_neighbor.json b/tests/topotests/ospf_gr_topo1/rt3/show_ip_ospf_neighbor.json index ab5e78414d..49a019d36d 100644 --- a/tests/topotests/ospf_gr_topo1/rt3/show_ip_ospf_neighbor.json +++ b/tests/topotests/ospf_gr_topo1/rt3/show_ip_ospf_neighbor.json @@ -3,21 +3,21 @@ "2.2.2.2":[ { "converged":"Full", - "address":"10.0.2.2", + "ifaceAddress":"10.0.2.2", "ifaceName":"eth-rt2:10.0.2.3" } ], "4.4.4.4":[ { "converged":"Full", - "address":"10.0.3.4", + "ifaceAddress":"10.0.3.4", "ifaceName":"eth-rt4:10.0.3.3" } ], "6.6.6.6":[ { "converged":"Full", - "address":"10.0.4.6", + "ifaceAddress":"10.0.4.6", "ifaceName":"eth-rt6:10.0.4.3" } ] diff --git a/tests/topotests/ospf_gr_topo1/rt4/show_ip_ospf_neighbor.json b/tests/topotests/ospf_gr_topo1/rt4/show_ip_ospf_neighbor.json index 405679c10e..9ab49d7266 100644 --- a/tests/topotests/ospf_gr_topo1/rt4/show_ip_ospf_neighbor.json +++ b/tests/topotests/ospf_gr_topo1/rt4/show_ip_ospf_neighbor.json @@ -3,14 +3,14 @@ "3.3.3.3":[ { "converged":"Full", - "address":"10.0.3.3", + "ifaceAddress":"10.0.3.3", "ifaceName":"eth-rt3:10.0.3.4" } ], "5.5.5.5":[ { "converged":"Full", - "address":"10.0.5.5", + "ifaceAddress":"10.0.5.5", "ifaceName":"eth-rt5:10.0.5.4" } ] diff --git a/tests/topotests/ospf_gr_topo1/rt5/show_ip_ospf_neighbor.json b/tests/topotests/ospf_gr_topo1/rt5/show_ip_ospf_neighbor.json index 893d454368..7d3d589772 100644 --- a/tests/topotests/ospf_gr_topo1/rt5/show_ip_ospf_neighbor.json +++ b/tests/topotests/ospf_gr_topo1/rt5/show_ip_ospf_neighbor.json @@ -3,7 +3,7 @@ "4.4.4.4":[ { "converged":"Full", - "address":"10.0.5.4", + "ifaceAddress":"10.0.5.4", "ifaceName":"eth-rt4:10.0.5.5" } ] diff --git a/tests/topotests/ospf_gr_topo1/rt6/show_ip_ospf_neighbor.json b/tests/topotests/ospf_gr_topo1/rt6/show_ip_ospf_neighbor.json index 564a513ac6..506eb4086b 100644 --- a/tests/topotests/ospf_gr_topo1/rt6/show_ip_ospf_neighbor.json +++ b/tests/topotests/ospf_gr_topo1/rt6/show_ip_ospf_neighbor.json @@ -3,14 +3,14 @@ "3.3.3.3":[ { "converged":"Full", - "address":"10.0.4.3", + "ifaceAddress":"10.0.4.3", "ifaceName":"eth-rt3:10.0.4.6" } ], "7.7.7.7":[ { "converged":"Full", - "address":"10.0.6.7", + "ifaceAddress":"10.0.6.7", "ifaceName":"eth-rt7:10.0.6.6" } ] diff --git a/tests/topotests/ospf_gr_topo1/rt7/show_ip_ospf_neighbor.json b/tests/topotests/ospf_gr_topo1/rt7/show_ip_ospf_neighbor.json index bc6b60697c..6429148004 100644 --- a/tests/topotests/ospf_gr_topo1/rt7/show_ip_ospf_neighbor.json +++ b/tests/topotests/ospf_gr_topo1/rt7/show_ip_ospf_neighbor.json @@ -3,7 +3,7 @@ "6.6.6.6":[ { "converged":"Full", - "address":"10.0.6.6", + "ifaceAddress":"10.0.6.6", "ifaceName":"eth-rt6:10.0.6.7" } ] diff --git a/tests/topotests/ospf_metric_propagation/__init__.py b/tests/topotests/ospf_metric_propagation/__init__.py new file mode 100755 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/ospf_metric_propagation/__init__.py diff --git a/tests/topotests/ospf_metric_propagation/h1/frr.conf b/tests/topotests/ospf_metric_propagation/h1/frr.conf new file mode 100644 index 0000000000..1196a192dd --- /dev/null +++ b/tests/topotests/ospf_metric_propagation/h1/frr.conf @@ -0,0 +1,10 @@ +! +hostname h1 +password zebra +log file /tmp/h1-frr.log +! +ip route 0.0.0.0/0 10.0.91.1 +! +interface h1-eth0 + ip address 10.0.91.2/24 +!
\ No newline at end of file diff --git a/tests/topotests/ospf_metric_propagation/h2/frr.conf b/tests/topotests/ospf_metric_propagation/h2/frr.conf new file mode 100644 index 0000000000..f951fe6ba1 --- /dev/null +++ b/tests/topotests/ospf_metric_propagation/h2/frr.conf @@ -0,0 +1,10 @@ +! +hostname h2 +password zebra +log file /tmp/h2-frr.log +! +ip route 0.0.0.0/0 10.0.94.4 +! +interface h2-eth0 + ip address 10.0.94.2/24 +!
\ No newline at end of file diff --git a/tests/topotests/ospf_metric_propagation/r1/frr.conf b/tests/topotests/ospf_metric_propagation/r1/frr.conf new file mode 100644 index 0000000000..9f388dbd0f --- /dev/null +++ b/tests/topotests/ospf_metric_propagation/r1/frr.conf @@ -0,0 +1,96 @@ +! +hostname r1 +password zebra +log file /tmp/r1-frr.log +ip forwarding +! +interface r1-eth0 + ip address 10.0.1.1/24 + ip ospf cost 100 + ip ospf hello-interval 1 + ip ospf dead-interval 3 +! +interface r1-eth1 vrf blue + ip address 10.0.10.1/24 + ip ospf hello-interval 1 + ip ospf dead-interval 3 +! +! +interface r1-eth2 vrf green + ip address 10.0.91.1/24 + ip ospf hello-interval 1 + ip ospf dead-interval 3 +! +! +router ospf + ospf router-id 10.0.255.1 + distance 20 + redistribute bgp route-map costplus + network 10.0.1.0/24 area 0 +! +router ospf vrf blue + ospf router-id 10.0.255.1 + distance 20 + redistribute bgp route-map costplus + network 10.0.10.0/24 area 0 +! +router ospf vrf green + ospf router-id 10.0.255.1 + distance 20 + redistribute bgp route-map costplus + network 10.0.91.0/24 area 0 +! +router bgp 99 + no bgp ebgp-requires-policy + address-family ipv4 unicast + redistribute connected + redistribute ospf route-map rmap + import vrf route-map rmap + import vrf blue + import vrf green + ! +! +router bgp 99 vrf blue + no bgp ebgp-requires-policy + address-family ipv4 unicast + redistribute connected + redistribute ospf route-map rmap + import vrf route-map rmap + import vrf default + import vrf green + ! +router bgp 99 vrf green + no bgp ebgp-requires-policy + address-family ipv4 unicast + redistribute connected + redistribute ospf + import vrf route-map rmap + import vrf default + import vrf blue + ! +! +route-map rmap permit 10 + set metric-type type-1 + set metric +1 + exit +! +ip prefix-list min seq 5 permit 10.0.80.0/24 +route-map costmax permit 20 + set metric-type type-1 + set metric +1 + set metric-min 713 + match ip address prefix-list min + exit +! +ip prefix-list max seq 10 permit 10.0.70.0/24 +route-map costplus permit 30 + set metric-type type-1 + set metric +1 + set metric-max 13 + match ip address prefix-list max + exit +! +route-map costplus permit 40 + set metric-type type-1 + set metric +1 + exit
\ No newline at end of file diff --git a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-1.json b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-1.json new file mode 100644 index 0000000000..e3a5cc410f --- /dev/null +++ b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-1.json @@ -0,0 +1,37 @@ +{ + "10.0.94.0/24":[ + { + "prefix":"10.0.94.0/24", + "prefixLen":24, + "protocol":"bgp", + "vrfId":9, + "vrfName":"green", + "selected":true, + "destSelected":true, + "distance":20, + "metric":34, + "installed":true, + "table":12, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthopGroupId":"*", + "installedNexthopGroupId":"*", + "uptime":"*", + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"10.0.10.5", + "afi":"ipv4", + "interfaceIndex":6, + "interfaceName":"r1-eth1", + "vrf":"blue", + "active":true, + "weight":1 + } + ] + } + ] +} diff --git a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-2.json b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-2.json new file mode 100644 index 0000000000..f3597bf458 --- /dev/null +++ b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-2.json @@ -0,0 +1,37 @@ +{ + "10.0.94.0/24":[ + { + "prefix":"10.0.94.0/24", + "prefixLen":24, + "protocol":"bgp", + "vrfId":9, + "vrfName":"green", + "selected":true, + "destSelected":true, + "distance":20, + "metric":136, + "installed":true, + "table":12, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthopGroupId":"*", + "installedNexthopGroupId":"*", + "uptime":"*", + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceIndex":5, + "interfaceName":"r1-eth0", + "vrf":"default", + "active":true, + "weight":1 + } + ] + } + ] +} diff --git a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-3.json b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-3.json new file mode 100644 index 0000000000..eebcab83e4 --- /dev/null +++ b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-3.json @@ -0,0 +1,37 @@ +{ + "10.0.94.0/24":[ + { + "prefix":"10.0.94.0/24", + "prefixLen":24, + "protocol":"bgp", + "vrfId":9, + "vrfName":"green", + "selected":true, + "destSelected":true, + "distance":20, + "metric":1138, + "installed":true, + "table":12, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthopGroupId":"*", + "installedNexthopGroupId":"*", + "uptime":"*", + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceIndex":5, + "interfaceName":"r1-eth0", + "vrf":"default", + "active":true, + "weight":1 + } + ] + } + ] +} diff --git a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-4.json b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-4.json new file mode 100644 index 0000000000..d0e3d816d3 --- /dev/null +++ b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-4.json @@ -0,0 +1,37 @@ +{ + "10.0.94.0/24":[ + { + "prefix":"10.0.94.0/24", + "prefixLen":24, + "protocol":"bgp", + "vrfId":9, + "vrfName":"green", + "selected":true, + "destSelected":true, + "distance":20, + "metric":1218, + "installed":true, + "table":12, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthopGroupId":"*", + "installedNexthopGroupId":"*", + "uptime":"*", + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceIndex":5, + "interfaceName":"r1-eth0", + "vrf":"default", + "active":true, + "weight":1 + } + ] + } + ] +} diff --git a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-5.json b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-5.json new file mode 100644 index 0000000000..989ccf7798 --- /dev/null +++ b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-5.json @@ -0,0 +1,37 @@ +{ + "10.0.94.0/24":[ + { + "prefix":"10.0.94.0/24", + "prefixLen":24, + "protocol":"bgp", + "vrfId":9, + "vrfName":"green", + "selected":true, + "destSelected":true, + "distance":20, + "metric":238, + "installed":true, + "table":12, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthopGroupId":"*", + "installedNexthopGroupId":"*", + "uptime":"*", + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceIndex":5, + "interfaceName":"r1-eth0", + "vrf":"default", + "active":true, + "weight":1 + } + ] + } + ] +} diff --git a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-6.json b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-6.json new file mode 100644 index 0000000000..84b11886e4 --- /dev/null +++ b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-6.json @@ -0,0 +1,37 @@ +{ + "10.0.94.0/24":[ + { + "prefix":"10.0.94.0/24", + "prefixLen":24, + "protocol":"bgp", + "vrfId":9, + "vrfName":"green", + "selected":true, + "destSelected":true, + "distance":20, + "metric":136, + "installed":true, + "table":12, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthopGroupId":"*", + "installedNexthopGroupId":"*", + "uptime":"*", + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"10.0.10.5", + "afi":"ipv4", + "interfaceIndex":6, + "interfaceName":"r1-eth1", + "vrf":"blue", + "active":true, + "weight":1 + } + ] + } + ] +} diff --git a/tests/topotests/ospf_metric_propagation/r2/frr.conf b/tests/topotests/ospf_metric_propagation/r2/frr.conf new file mode 100644 index 0000000000..469ae5da88 --- /dev/null +++ b/tests/topotests/ospf_metric_propagation/r2/frr.conf @@ -0,0 +1,81 @@ +! +hostname r2 +password zebra +log file /tmp/r2-frr.log +ip forwarding +! +interface r2-eth0 + ip address 10.0.1.2/24 + ip ospf cost 100 + ip ospf hello-interval 1 + ip ospf dead-interval 3 +! +interface r2-eth1 vrf blue + ip address 10.0.20.2/24 + ip ospf hello-interval 1 + ip ospf dead-interval 3 +! +interface r2-eth2 vrf green + ip address 10.0.70.2/24 + ip ospf cost 1000 + ip ospf hello-interval 1 + ip ospf dead-interval 3 +! +router ospf + ospf router-id 10.0.255.2 + distance 20 + redistribute bgp route-map costplus + network 10.0.1.0/24 area 0 +! +router ospf vrf blue + ospf router-id 10.0.255.2 + distance 20 + redistribute bgp route-map costplus + network 10.0.20.0/24 area 0 +! + +router ospf vrf green + ospf router-id 10.0.255.2 + distance 20 + redistribute bgp route-map costplus + network 10.0.70.0/24 area 0 +! + +router bgp 99 + no bgp ebgp-requires-policy + address-family ipv4 unicast + redistribute connected + redistribute ospf + import vrf route-map rmap + import vrf blue + import vrf green + ! +! +router bgp 99 vrf blue + no bgp ebgp-requires-policy + address-family ipv4 unicast + redistribute connected + redistribute ospf + import vrf route-map rmap + import vrf default + import vrf green + ! +router bgp 99 vrf green + no bgp ebgp-requires-policy + address-family ipv4 unicast + redistribute connected + redistribute ospf + import vrf route-map rmap + import vrf default + import vrf blue + ! +! +route-map rmap permit 10 + set metric-type type-1 + set metric +1 + exit +! +route-map costplus permit 1 + set metric-type type-1 + set metric +1 + exit diff --git a/tests/topotests/ospf_metric_propagation/r3/frr.conf b/tests/topotests/ospf_metric_propagation/r3/frr.conf new file mode 100644 index 0000000000..8dbbaf0fc4 --- /dev/null +++ b/tests/topotests/ospf_metric_propagation/r3/frr.conf @@ -0,0 +1,79 @@ +! +hostname r3 +password zebra +log file /tmp/r3-frr.log +ip forwarding +! +interface r3-eth0 + ip address 10.0.3.3/24 + ip ospf cost 100 + ip ospf hello-interval 1 + ip ospf dead-interval 3 +! +interface r3-eth1 vrf blue + ip address 10.0.30.3/24 + ip ospf hello-interval 1 + ip ospf dead-interval 3 +! +interface r3-eth2 vrf green + ip address 10.0.80.3/24 + ip ospf cost 1000 + ip ospf hello-interval 1 + ip ospf dead-interval 3 +! +router ospf + ospf router-id 10.0.255.3 + distance 20 + redistribute bgp route-map costplus + network 10.0.3.0/24 area 0 +! +router ospf vrf blue + ospf router-id 10.0.255.3 + distance 20 + redistribute bgp route-map costplus + network 10.0.30.0/24 area 0 +! +router ospf vrf green + ospf router-id 10.0.255.3 + distance 20 + redistribute bgp route-map costplus + network 10.0.80.0/24 area 0 +! +router bgp 99 + no bgp ebgp-requires-policy + address-family ipv4 unicast + redistribute connected + redistribute ospf + import vrf route-map rmap + import vrf blue + import vrf green + ! +! +router bgp 99 vrf blue + no bgp ebgp-requires-policy + address-family ipv4 unicast + redistribute connected + redistribute ospf + import vrf route-map rmap + import vrf default + import vrf green + ! +router bgp 99 vrf green + no bgp ebgp-requires-policy + address-family ipv4 unicast + redistribute connected + redistribute ospf + import vrf route-map rmap + import vrf default + import vrf blue + ! +! +route-map rmap permit 10 + set metric-type type-1 + set metric +1 + exit +! +route-map costplus permit 1 + set metric-type type-1 + set metric +1 + exit diff --git a/tests/topotests/ospf_metric_propagation/r4/frr.conf b/tests/topotests/ospf_metric_propagation/r4/frr.conf new file mode 100644 index 0000000000..af1005063b --- /dev/null +++ b/tests/topotests/ospf_metric_propagation/r4/frr.conf @@ -0,0 +1,78 @@ +! +hostname r4 +password zebra +log file /tmp/r4-frr.log +ip forwarding +! +interface r4-eth0 + ip address 10.0.3.4/24 + ip ospf cost 100 + ip ospf hello-interval 1 + ip ospf dead-interval 3 +! +interface r4-eth1 vrf blue + ip address 10.0.40.4/24 + ip ospf hello-interval 1 + ip ospf dead-interval 3 +! +interface r4-eth2 vrf green + ip address 10.0.94.4/24 + ip ospf hello-interval 1 + ip ospf dead-interval 3 +! +router ospf + ospf router-id 10.0.255.4 + distance 20 + redistribute bgp route-map costplus + network 10.0.3.0/24 area 0 +! +router ospf vrf blue + ospf router-id 10.0.255.4 + distance 20 + redistribute bgp route-map costplus + network 10.0.40.0/24 area 0 +! +router ospf vrf green + ospf router-id 10.0.255.1 + distance 20 + redistribute bgp route-map costplus + network 10.0.94.0/24 area 0 +! +router bgp 99 + no bgp ebgp-requires-policy + address-family ipv4 unicast + redistribute connected + redistribute ospf route-map costplus + import vrf route-map rmap + import vrf blue + import vrf green + ! +! +router bgp 99 vrf blue + no bgp ebgp-requires-policy + address-family ipv4 unicast + redistribute connected + redistribute ospf + import vrf route-map rmap + import vrf default + import vrf green + ! +router bgp 99 vrf green + no bgp ebgp-requires-policy + address-family ipv4 unicast + redistribute connected + redistribute ospf + import vrf route-map rmap + import vrf default + import vrf blue + ! +! +route-map rmap permit 10 + set metric-type type-1 + set metric +1 + exit +! +route-map costplus permit 1 + set metric-type type-1 + set metric +1 + exit diff --git a/tests/topotests/ospf_metric_propagation/ra/frr.conf b/tests/topotests/ospf_metric_propagation/ra/frr.conf new file mode 100644 index 0000000000..0bc2ec5679 --- /dev/null +++ b/tests/topotests/ospf_metric_propagation/ra/frr.conf @@ -0,0 +1,27 @@ +! +hostname ra +password zebra +log file /tmp/ra-frr.log +ip forwarding +! +interface ra-eth0 + ip address 10.0.50.5/24 + ip ospf hello-interval 1 + ip ospf dead-interval 3 +! +interface ra-eth1 + ip address 10.0.10.5/24 + ip ospf hello-interval 1 + ip ospf dead-interval 3 +! +interface ra-eth2 + ip address 10.0.20.5/24 + ip ospf hello-interval 1 + ip ospf dead-interval 3 +! +router ospf + ospf router-id 10.0.255.5 + network 10.0.10.0/24 area 0 + network 10.0.20.0/24 area 0 + network 10.0.50.0/24 area 0 +! diff --git a/tests/topotests/ospf_metric_propagation/rb/frr.conf b/tests/topotests/ospf_metric_propagation/rb/frr.conf new file mode 100644 index 0000000000..6f540d125e --- /dev/null +++ b/tests/topotests/ospf_metric_propagation/rb/frr.conf @@ -0,0 +1,27 @@ +! +hostname rb +password zebra +log file /tmp/rb-frr.log +ip forwarding +! +interface rb-eth0 + ip address 10.0.50.6/24 + ip ospf hello-interval 1 + ip ospf dead-interval 3 +! +interface rb-eth1 + ip address 10.0.30.6/24 + ip ospf hello-interval 1 + ip ospf dead-interval 3 +! +interface rb-eth2 + ip address 10.0.40.6/24 + ip ospf hello-interval 1 + ip ospf dead-interval 3 +! +router ospf + ospf router-id 10.0.255.6 + network 10.0.30.0/24 area 0 + network 10.0.40.0/24 area 0 + network 10.0.50.0/24 area 0 +! diff --git a/tests/topotests/ospf_metric_propagation/rc/frr.conf b/tests/topotests/ospf_metric_propagation/rc/frr.conf new file mode 100644 index 0000000000..9fc0ef718f --- /dev/null +++ b/tests/topotests/ospf_metric_propagation/rc/frr.conf @@ -0,0 +1,21 @@ +! +hostname rc +password zebra +log file /tmp/rc-frr.log +ip forwarding +! +interface rc-eth0 + ip address 10.0.70.7/24 + ip ospf hello-interval 1 + ip ospf dead-interval 3 +! +interface rc-eth1 + ip address 10.0.80.7/24 + ip ospf hello-interval 1 + ip ospf dead-interval 3 +! +router ospf + ospf router-id 10.0.255.7 + network 10.0.70.0/24 area 0 + network 10.0.80.0/24 area 0 +! diff --git a/tests/topotests/ospf_metric_propagation/test_ospf_metric_propagation.py b/tests/topotests/ospf_metric_propagation/test_ospf_metric_propagation.py new file mode 100644 index 0000000000..709e07649b --- /dev/null +++ b/tests/topotests/ospf_metric_propagation/test_ospf_metric_propagation.py @@ -0,0 +1,374 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_ospf_metric_propagation.py +# +# Copyright (c) 2023 ATCorp +# Jafar Al-Gharaibeh +# + +import os +import sys +import json +from time import sleep +from functools import partial +import pytest + +# 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 + + +""" +test_ospf_metric_propagation.py: Test OSPF/BGP metric propagation +""" + +TOPOLOGY = """ + +-----+ +-----+ + eth1 | | eth0 | | eth2 + +-------------+ rA +---------------------------+ rB +---------------+ + | .5 | | .5 .6 | | .6 | + | +--+--+ 10.0.50.0/24 +--+--+ .6 | + | |.5 |.6 | + | eth2| eth1| | + 10.0.10.0/24 | | | + | 10.0.20.0/24 10.0.30.0/24 10.0.40.0/24 + |blue |blue |blue |blue + | | | | + eth1|.1 eth1|.2 eth1|.3 eth1|.4 + +-----+ +--+--+ +--+--+ +-----+ +-+---+ +-+---+ +------+ + | |eth0 eth2| | eth0 | |eth2 eth1| |eth2 eth3| | eth0 | |eth2 eth0| | + | h1 +----------+ R1 +----------+ R2 +-----------+ rC +----------+ R3 +------------+ R4 +---------+ h2 | + | | | | | | | | | | | | | | + +-----+.2 .1 +-----+.1 .2+-----+.2 .7 +-----+.7 .3+-----+.3 .4+-----+.4 .2+------+ + green green green green + + 10.0.91.0/24 10.0.1.0/24 10.0.70.0/24 10.0.80.0/24 10.0.3.0/24 10.0.94.0/24 +""" + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# Required to instantiate the topology builder class. + +pytestmark = [pytest.mark.ospfd, pytest.mark.bgpd] + + +def build_topo(tgen): + "Build function" + + # Create 4 routers + for routern in range(1, 5): + tgen.add_router("r{}".format(routern)) + + tgen.add_router("ra") + tgen.add_router("rb") + tgen.add_router("rc") + tgen.add_router("h1") + tgen.add_router("h2") + + + # Interconect router 1, 2 + switch = tgen.add_switch("s1-2") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + # Interconect router 3, 4 + switch = tgen.add_switch("s3-4") + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["r4"]) + + # Interconect router a, b + switch = tgen.add_switch("sa-b") + switch.add_link(tgen.gears["ra"]) + switch.add_link(tgen.gears["rb"]) + + # Interconect router 1, a + switch = tgen.add_switch("s1-a") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["ra"]) + + # Interconect router 2, a + switch = tgen.add_switch("s2-a") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["ra"]) + + # Interconect router 3, b + switch = tgen.add_switch("s3-b") + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["rb"]) + + # Interconect router 4, b + switch = tgen.add_switch("s4-b") + switch.add_link(tgen.gears["r4"]) + switch.add_link(tgen.gears["rb"]) + + # Interconect router 1, h1 + switch = tgen.add_switch("s1-h1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["h1"]) + + # Interconect router 4, h2 + switch = tgen.add_switch("s4-h2") + switch.add_link(tgen.gears["r4"]) + switch.add_link(tgen.gears["h2"]) + + # Interconect router 2, c + switch = tgen.add_switch("s2-c") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["rc"]) + + # Interconect router 3, c + switch = tgen.add_switch("s3-c") + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["rc"]) + +def setup_module(mod): + logger.info("OSPF Metric Propagation:\n {}".format(TOPOLOGY)) + + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + vrf_setup_cmds = [ + "ip link add name blue type vrf table 11", + "ip link set dev blue up", + "ip link add name green type vrf table 12", + "ip link set dev green up", + ] + + # Starting Routers + router_list = tgen.routers() + + # Create VRFs and bind to interfaces + for routern in range(1, 5): + for cmd in vrf_setup_cmds: + tgen.net["r{}".format(routern)].cmd(cmd) + for routern in range(1, 5): + tgen.net["r{}".format(routern)].cmd("ip link set dev r{}-eth1 vrf blue up".format(routern)) + tgen.net["r{}".format(routern)].cmd("ip link set dev r{}-eth2 vrf green up".format(routern)) + + for rname, router in router_list.items(): + logger.info("Loading router %s" % rname) + router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname))) + + # Initialize all routers. + tgen.start_router() + for router in router_list.values(): + if router.has_version("<", "4.20"): + tgen.set_error("unsupported version") + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + tgen.stop_topology() + + +def test_all_links_up(): + "Test path R1 -> Ra -> Rb -> R4" + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + r1 = tgen.gears["r1"] + json_file = "{}/r1/show_ip_route-1.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=40, wait=1) + + assertmsg = "r1 JSON output mismatches" + assert result is None, assertmsg + + +def test_link_1_down(): + "Test path R1 -> R2 -> Ra -> Rb -> R4" + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + tgen.net["r1"].cmd("ip link set dev r1-eth1 down") + r1 = tgen.gears["r1"] + + json_file = "{}/r1/show_ip_route-2.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=20, wait=1) + + assertmsg = "r1 JSON output mismatches" + assert result is None, assertmsg + + +def test_link_1_2_down(): + "Test path R1 -> R2 -> Rc -> R3 -> Rb -> R4" + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + tgen.net["r2"].cmd("ip link set dev r2-eth1 down") + r1 = tgen.gears["r1"] + + json_file = "{}/r1/show_ip_route-3.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=20, wait=1) + + assertmsg = "r1 JSON output mismatches" + assert result is None, assertmsg + + +def test_link_1_2_3_down(): + "Test path R1 -> R2 -> Rc -> R3 -> R4" + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + tgen.net["r3"].cmd("ip link set dev r3-eth0 down") + tgen.net["r3"].cmd("ip link set dev r3-eth1 down") + # ospf dead-interval is set to 3 seconds, wait 5 seconds to clear the neighbor + sleep(5) + tgen.net["r3"].cmd("ip link set dev r3-eth0 up") + r1 = tgen.gears["r1"] + + json_file = "{}/r1/show_ip_route-4.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=20, wait=1) + + assertmsg = "r1 JSON output mismatches" + assert result is None, assertmsg + +def test_link_1_2_3_4_down(): + "Test path R1 -> R2 -> Rc -> R3 -> R4" + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + tgen.net["r4"].cmd("ip link set dev r4-eth1 down") + r1 = tgen.gears["r1"] + + json_file = "{}/r1/show_ip_route-4.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=20, wait=1) + + assertmsg = "r1 JSON output mismatches" + assert result is None, assertmsg + +def test_link_1_2_4_down(): + "Test path R1 -> R2 -> Rc -> R3 -> R4" + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + # bring link 3 back up + tgen.net["r3"].cmd("ip link set dev r3-eth1 up") + r1 = tgen.gears["r1"] + + json_file = "{}/r1/show_ip_route-4.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=20, wait=1) + + assertmsg = "r1 JSON output mismatches" + assert result is None, assertmsg + +def test_link_1_4_down(): + "Test path R1 -> R2 -> Ra -> Rb -> R3 -> R4" + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + # bring back link 2 up + tgen.net["r2"].cmd("ip link set dev r2-eth1 up") + r1 = tgen.gears["r1"] + + json_file = "{}/r1/show_ip_route-5.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=20, wait=1) + + assertmsg = "r1 JSON output mismatches" + assert result is None, assertmsg + + +def test_link_4_down(): + "Test path R1 -> Ra -> Rb -> R3 -> R4" + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + # bring back link 1 up + tgen.net["r1"].cmd("ip link set dev r1-eth1 up") + r1 = tgen.gears["r1"] + + json_file = "{}/r1/show_ip_route-6.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=20, wait=1) + + assertmsg = "r1 JSON output mismatches" + assert result is None, assertmsg + + +def test_link_1_2_3_4_up(): + "Test path R1 -> Ra -> Rb -> R4" + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + # bring back link 4 up + tgen.net["r4"].cmd("ip link set dev r4-eth1 up") + r1 = tgen.gears["r1"] + + json_file = "{}/r1/show_ip_route-1.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=20, wait=1) + + assertmsg = "r1 JSON output mismatches" + assert result is None, assertmsg + + +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/ospf_nssa_topo1/__init__.py b/tests/topotests/ospf_nssa_topo1/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/__init__.py diff --git a/tests/topotests/ospf_nssa_topo1/rt1/ospfd.conf b/tests/topotests/ospf_nssa_topo1/rt1/ospfd.conf new file mode 100644 index 0000000000..6d23c84806 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt1/ospfd.conf @@ -0,0 +1,22 @@ +password 1 +hostname rt1 +log file ospfd.log +! +! debug ospf sr +! debug ospf te +! debug ospf event +! debug ospf lsa +! debug ospf zebra +! +interface lo + ip ospf area 0 +! +interface eth-rt2 + ip ospf network point-to-point + ip ospf area 0 + ip ospf hello-interval 3 + ip ospf dead-interval 12 +! +router ospf + router-id 1.1.1.1 +! diff --git a/tests/topotests/ospf_nssa_topo1/rt1/staticd.conf b/tests/topotests/ospf_nssa_topo1/rt1/staticd.conf new file mode 100644 index 0000000000..7ba3dc7591 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt1/staticd.conf @@ -0,0 +1,6 @@ +log file staticd.log +! +hostname rt1 +! +line vty +! diff --git a/tests/topotests/ospf_nssa_topo1/rt1/step1/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt1/step1/show_ip_ospf_route.ref new file mode 100644 index 0000000000..ac34417bda --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt1/step1/show_ip_ospf_route.ref @@ -0,0 +1,115 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.0", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt1/step10/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt1/step10/show_ip_ospf_route.ref new file mode 100644 index 0000000000..ac34417bda --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt1/step10/show_ip_ospf_route.ref @@ -0,0 +1,115 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.0", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt1/step2/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt1/step2/show_ip_ospf_route.ref new file mode 100644 index 0000000000..ac34417bda --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt1/step2/show_ip_ospf_route.ref @@ -0,0 +1,115 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.0", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt1/step3/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt1/step3/show_ip_ospf_route.ref new file mode 100644 index 0000000000..ac34417bda --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt1/step3/show_ip_ospf_route.ref @@ -0,0 +1,115 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.0", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt1/step4/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt1/step4/show_ip_ospf_route.ref new file mode 100644 index 0000000000..6a05555eec --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt1/step4/show_ip_ospf_route.ref @@ -0,0 +1,103 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.0", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt1/step5/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt1/step5/show_ip_ospf_route.ref new file mode 100644 index 0000000000..6a05555eec --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt1/step5/show_ip_ospf_route.ref @@ -0,0 +1,103 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.0", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt1/step6/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt1/step6/show_ip_ospf_route.ref new file mode 100644 index 0000000000..2d3c8c485f --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt1/step6/show_ip_ospf_route.ref @@ -0,0 +1,91 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.0", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt1/step7/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt1/step7/show_ip_ospf_route.ref new file mode 100644 index 0000000000..6a05555eec --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt1/step7/show_ip_ospf_route.ref @@ -0,0 +1,103 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.0", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt1/step8/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt1/step8/show_ip_ospf_route.ref new file mode 100644 index 0000000000..f41ee3b698 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt1/step8/show_ip_ospf_route.ref @@ -0,0 +1,103 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.0", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":1000, + "tag":0, + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt1/step9/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt1/step9/show_ip_ospf_route.ref new file mode 100644 index 0000000000..2d3c8c485f --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt1/step9/show_ip_ospf_route.ref @@ -0,0 +1,91 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.0", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt1/zebra.conf b/tests/topotests/ospf_nssa_topo1/rt1/zebra.conf new file mode 100644 index 0000000000..1df100564e --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt1/zebra.conf @@ -0,0 +1,18 @@ +log file zebra.log +! +hostname rt1 +! +! debug zebra kernel +! debug zebra packet +! debug zebra mpls +! +interface lo + ip address 1.1.1.1/32 +! +interface eth-rt2 + ip address 10.0.1.1/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ospf_nssa_topo1/rt2/ospfd.conf b/tests/topotests/ospf_nssa_topo1/rt2/ospfd.conf new file mode 100644 index 0000000000..12884d2ea9 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt2/ospfd.conf @@ -0,0 +1,35 @@ +password 1 +hostname rt2 +log file ospfd.log +! +! debug ospf sr +! debug ospf te +! debug ospf event +! debug ospf lsa +! debug ospf zebra +! +interface lo + ip ospf area 0 +! +interface eth-rt1 + ip ospf network point-to-point + ip ospf area 0 + ip ospf hello-interval 3 + ip ospf dead-interval 12 +! +interface eth-rt3 + ip ospf network point-to-point + ip ospf area 1 + ip ospf hello-interval 3 + ip ospf dead-interval 12 +! +interface eth-rt4 + ip ospf network point-to-point + ip ospf area 1 + ip ospf hello-interval 3 + ip ospf dead-interval 12 +! +router ospf + router-id 2.2.2.2 + area 1 nssa +! diff --git a/tests/topotests/ospf_nssa_topo1/rt2/staticd.conf b/tests/topotests/ospf_nssa_topo1/rt2/staticd.conf new file mode 100644 index 0000000000..b6d4233a4f --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt2/staticd.conf @@ -0,0 +1,6 @@ +log file staticd.log +! +hostname rt2 +! +line vty +! diff --git a/tests/topotests/ospf_nssa_topo1/rt2/step1/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt2/step1/show_ip_ospf_route.ref new file mode 100644 index 0000000000..c09411741a --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt2/step1/show_ip_ospf_route.ref @@ -0,0 +1,127 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.1", + "via":"eth-rt1" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt1" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt3" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt4" + } + ] + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt2/step10/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt2/step10/show_ip_ospf_route.ref new file mode 100644 index 0000000000..c09411741a --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt2/step10/show_ip_ospf_route.ref @@ -0,0 +1,127 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.1", + "via":"eth-rt1" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt1" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt3" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt4" + } + ] + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt2/step2/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt2/step2/show_ip_ospf_route.ref new file mode 100644 index 0000000000..a67dfb4685 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt2/step2/show_ip_ospf_route.ref @@ -0,0 +1,139 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.1", + "via":"eth-rt1" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt1" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt3" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt4" + } + ] + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "0.0.0.0\/0":{ + "routeType":"N E2", + "cost":10, + "type2cost":1, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt2/step3/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt2/step3/show_ip_ospf_route.ref new file mode 100644 index 0000000000..c09411741a --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt2/step3/show_ip_ospf_route.ref @@ -0,0 +1,127 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.1", + "via":"eth-rt1" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt1" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt3" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt4" + } + ] + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt2/step4/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt2/step4/show_ip_ospf_route.ref new file mode 100644 index 0000000000..c7dd93c6e9 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt2/step4/show_ip_ospf_route.ref @@ -0,0 +1,129 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.1", + "via":"eth-rt1" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt1" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt3" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt4" + } + ] + }, + "172.16.1.0\/24":{ + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt2/step5/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt2/step5/show_ip_ospf_route.ref new file mode 100644 index 0000000000..9c3cfff6c0 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt2/step5/show_ip_ospf_route.ref @@ -0,0 +1,117 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.1", + "via":"eth-rt1" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt1" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt3" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt4" + } + ] + }, + "172.16.1.0\/24":{ + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt2/step6/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt2/step6/show_ip_ospf_route.ref new file mode 100644 index 0000000000..f6bbdfa594 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt2/step6/show_ip_ospf_route.ref @@ -0,0 +1,103 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.1", + "via":"eth-rt1" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt1" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt3" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt4" + } + ] + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt2/step7/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt2/step7/show_ip_ospf_route.ref new file mode 100644 index 0000000000..c7dd93c6e9 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt2/step7/show_ip_ospf_route.ref @@ -0,0 +1,129 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.1", + "via":"eth-rt1" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt1" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt3" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt4" + } + ] + }, + "172.16.1.0\/24":{ + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt2/step8/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt2/step8/show_ip_ospf_route.ref new file mode 100644 index 0000000000..c7dd93c6e9 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt2/step8/show_ip_ospf_route.ref @@ -0,0 +1,129 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.1", + "via":"eth-rt1" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt1" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt3" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt4" + } + ] + }, + "172.16.1.0\/24":{ + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt2/step9/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt2/step9/show_ip_ospf_route.ref new file mode 100644 index 0000000000..c09411741a --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt2/step9/show_ip_ospf_route.ref @@ -0,0 +1,127 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.1", + "via":"eth-rt1" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt1" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt3" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt4" + } + ] + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt2/zebra.conf b/tests/topotests/ospf_nssa_topo1/rt2/zebra.conf new file mode 100644 index 0000000000..fa274ebade --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt2/zebra.conf @@ -0,0 +1,24 @@ +log file zebra.log +! +hostname rt2 +! +! debug zebra kernel +! debug zebra packet +! debug zebra mpls +! +interface lo + ip address 2.2.2.2/32 +! +interface eth-rt1 + ip address 10.0.1.2/24 +! +interface eth-rt3 + ip address 10.0.2.2/24 +! +interface eth-rt4 + ip address 10.0.3.2/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ospf_nssa_topo1/rt3/ospfd.conf b/tests/topotests/ospf_nssa_topo1/rt3/ospfd.conf new file mode 100644 index 0000000000..9691a7c512 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt3/ospfd.conf @@ -0,0 +1,24 @@ +password 1 +hostname rt3 +log file ospfd.log +! +! debug ospf sr +! debug ospf te +! debug ospf event +! debug ospf lsa +! debug ospf zebra +! +interface lo + ip ospf area 1 +! +interface eth-rt2 + ip ospf network point-to-point + ip ospf area 1 + ip ospf hello-interval 3 + ip ospf dead-interval 12 +! +router ospf + router-id 3.3.3.3 + area 1 nssa + redistribute connected +! diff --git a/tests/topotests/ospf_nssa_topo1/rt3/staticd.conf b/tests/topotests/ospf_nssa_topo1/rt3/staticd.conf new file mode 100644 index 0000000000..f0edd6c9ca --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt3/staticd.conf @@ -0,0 +1,8 @@ +log file staticd.log +! +hostname rt3 +! +ip route 0.0.0.0/0 Null0 +! +line vty +! diff --git a/tests/topotests/ospf_nssa_topo1/rt3/step1/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt3/step1/show_ip_ospf_route.ref new file mode 100644 index 0000000000..a2d078ab13 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt3/step1/show_ip_ospf_route.ref @@ -0,0 +1,138 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt3/step10/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt3/step10/show_ip_ospf_route.ref new file mode 100644 index 0000000000..4619067abd --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt3/step10/show_ip_ospf_route.ref @@ -0,0 +1,150 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":1000, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt3/step2/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt3/step2/show_ip_ospf_route.ref new file mode 100644 index 0000000000..a2d078ab13 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt3/step2/show_ip_ospf_route.ref @@ -0,0 +1,138 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt3/step3/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt3/step3/show_ip_ospf_route.ref new file mode 100644 index 0000000000..a2d078ab13 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt3/step3/show_ip_ospf_route.ref @@ -0,0 +1,138 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt3/step4/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt3/step4/show_ip_ospf_route.ref new file mode 100644 index 0000000000..10387215b2 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt3/step4/show_ip_ospf_route.ref @@ -0,0 +1,150 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt3/step5/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt3/step5/show_ip_ospf_route.ref new file mode 100644 index 0000000000..4f8eaf1eaa --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt3/step5/show_ip_ospf_route.ref @@ -0,0 +1,138 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt3/step6/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt3/step6/show_ip_ospf_route.ref new file mode 100644 index 0000000000..41e9f6718a --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt3/step6/show_ip_ospf_route.ref @@ -0,0 +1,126 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt3/step7/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt3/step7/show_ip_ospf_route.ref new file mode 100644 index 0000000000..10387215b2 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt3/step7/show_ip_ospf_route.ref @@ -0,0 +1,150 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt3/step8/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt3/step8/show_ip_ospf_route.ref new file mode 100644 index 0000000000..4619067abd --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt3/step8/show_ip_ospf_route.ref @@ -0,0 +1,150 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":1000, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt3/step9/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt3/step9/show_ip_ospf_route.ref new file mode 100644 index 0000000000..4619067abd --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt3/step9/show_ip_ospf_route.ref @@ -0,0 +1,150 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":1000, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt3/zebra.conf b/tests/topotests/ospf_nssa_topo1/rt3/zebra.conf new file mode 100644 index 0000000000..d943540f3b --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt3/zebra.conf @@ -0,0 +1,18 @@ +log file zebra.log +! +hostname rt3 +! +! debug zebra kernel +! debug zebra packet +! debug zebra mpls +! +interface lo + ip address 3.3.3.3/32 +! +interface eth-rt2 + ip address 10.0.2.3/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ospf_nssa_topo1/rt4/ospfd.conf b/tests/topotests/ospf_nssa_topo1/rt4/ospfd.conf new file mode 100644 index 0000000000..cba7cf71ed --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt4/ospfd.conf @@ -0,0 +1,24 @@ +password 1 +hostname rt4 +log file ospfd.log +! +! debug ospf sr +! debug ospf te +! debug ospf event +! debug ospf lsa +! debug ospf zebra +! +interface lo + ip ospf area 1 +! +interface eth-rt2 + ip ospf network point-to-point + ip ospf area 1 + ip ospf hello-interval 3 + ip ospf dead-interval 12 +! +router ospf + router-id 4.4.4.4 + area 1 nssa + redistribute static +! diff --git a/tests/topotests/ospf_nssa_topo1/rt4/staticd.conf b/tests/topotests/ospf_nssa_topo1/rt4/staticd.conf new file mode 100644 index 0000000000..e00ee5dfea --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt4/staticd.conf @@ -0,0 +1,9 @@ +log file staticd.log +! +hostname rt4 +! +ip route 172.16.1.1/32 Null0 +ip route 172.16.1.2/32 Null0 +! +line vty +! diff --git a/tests/topotests/ospf_nssa_topo1/rt4/step1/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt4/step1/show_ip_ospf_route.ref new file mode 100644 index 0000000000..e57f542f1c --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt4/step1/show_ip_ospf_route.ref @@ -0,0 +1,114 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt4/step10/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt4/step10/show_ip_ospf_route.ref new file mode 100644 index 0000000000..82a0e1a9b4 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt4/step10/show_ip_ospf_route.ref @@ -0,0 +1,126 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":1000, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt4/step2/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt4/step2/show_ip_ospf_route.ref new file mode 100644 index 0000000000..e57f542f1c --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt4/step2/show_ip_ospf_route.ref @@ -0,0 +1,114 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt4/step3/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt4/step3/show_ip_ospf_route.ref new file mode 100644 index 0000000000..e57f542f1c --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt4/step3/show_ip_ospf_route.ref @@ -0,0 +1,114 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt4/step4/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt4/step4/show_ip_ospf_route.ref new file mode 100644 index 0000000000..5f51b3b3b8 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt4/step4/show_ip_ospf_route.ref @@ -0,0 +1,126 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt4/step5/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt4/step5/show_ip_ospf_route.ref new file mode 100644 index 0000000000..5f51b3b3b8 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt4/step5/show_ip_ospf_route.ref @@ -0,0 +1,126 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt4/step6/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt4/step6/show_ip_ospf_route.ref new file mode 100644 index 0000000000..5f51b3b3b8 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt4/step6/show_ip_ospf_route.ref @@ -0,0 +1,126 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt4/step7/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt4/step7/show_ip_ospf_route.ref new file mode 100644 index 0000000000..5f51b3b3b8 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt4/step7/show_ip_ospf_route.ref @@ -0,0 +1,126 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt4/step8/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt4/step8/show_ip_ospf_route.ref new file mode 100644 index 0000000000..82a0e1a9b4 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt4/step8/show_ip_ospf_route.ref @@ -0,0 +1,126 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":1000, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt4/step9/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt4/step9/show_ip_ospf_route.ref new file mode 100644 index 0000000000..82a0e1a9b4 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt4/step9/show_ip_ospf_route.ref @@ -0,0 +1,126 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":1000, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt4/zebra.conf b/tests/topotests/ospf_nssa_topo1/rt4/zebra.conf new file mode 100644 index 0000000000..588febe70b --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt4/zebra.conf @@ -0,0 +1,18 @@ +log file zebra.log +! +hostname rt4 +! +! debug zebra kernel +! debug zebra packet +! debug zebra mpls +! +interface lo + ip address 4.4.4.4/32 +! +interface eth-rt2 + ip address 10.0.3.4/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ospf_nssa_topo1/test_ospf_nssa_topo1.py b/tests/topotests/ospf_nssa_topo1/test_ospf_nssa_topo1.py new file mode 100644 index 0000000000..432ddf0986 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/test_ospf_nssa_topo1.py @@ -0,0 +1,416 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_ospf_nssa_topo1.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2023 by +# Network Device Education Foundation, Inc. ("NetDEF") +# + +""" +test_ospf_nssa_topo1.py: + + +---------+ + | RT1 | + | 1.1.1.1 | + +---------+ + |eth-rt2 + | + |10.0.1.0/24 + | + |eth-rt1 + +---------+ + | RT2 | + | 2.2.2.2 | + +---------+ + eth-rt3| |eth-rt4 + | | + 10.0.2.0/24 | | 10.0.3.0/24 + +---------+ +--------+ + | | + |eth-rt2 |eth-rt2 + +---------+ +---------+ + | RT3 | | RT4 | + | 3.3.3.3 | | 4.4.4.4 | + +---------+ +---------+ + +""" + +import os +import sys +import pytest +import json +from functools import partial + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. + +pytestmark = [pytest.mark.ospfd] + + +def build_topo(tgen): + "Build function" + + # + # Define FRR Routers + # + for router in ["rt1", "rt2", "rt3", "rt4"]: + tgen.add_router(router) + + # + # Define connections + # + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["rt1"], nodeif="eth-rt2") + switch.add_link(tgen.gears["rt2"], nodeif="eth-rt1") + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["rt2"], nodeif="eth-rt3") + switch.add_link(tgen.gears["rt3"], nodeif="eth-rt2") + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["rt2"], nodeif="eth-rt4") + switch.add_link(tgen.gears["rt4"], nodeif="eth-rt2") + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + # For all registered routers, load the zebra configuration file + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_STATIC, os.path.join(CWD, "{}/staticd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +def print_cmd_result(rname, command): + print(get_topogen().gears[rname].vtysh_cmd(command, isjson=False)) + + +def router_compare_json_output(rname, command, reference): + "Compare router JSON output" + + logger.info('Comparing router "%s" "%s" output', rname, command) + + tgen = get_topogen() + filename = "{}/{}/{}".format(CWD, rname, reference) + expected = json.loads(open(filename).read()) + + # Run test function until we get an result. Wait at most 60 seconds. + test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected) + _, diff = topotest.run_and_expect(test_func, None, count=120, wait=0.5) + assertmsg = '"{}" JSON output mismatches the expected result'.format(rname) + assert diff is None, assertmsg + + +# +# Step 1 +# +# Test initial network convergence +# +def test_rib_step1(): + logger.info("Test (step 1): test initial network convergence") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["rt1", "rt2", "rt3", "rt4"]: + router_compare_json_output( + rname, "show ip ospf route json", "step1/show_ip_ospf_route.ref" + ) + + +# +# Step 2 +# +# Action(s): +# -rt3: configure an NSSA default route +# +# Expected changes: +# -rt2: add NSSA default route pointing to rt3 +# +def test_rib_step2(): + logger.info("Test (step 2): verify OSPF routes") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Adding NSSA default on rt4") + tgen.net["rt3"].cmd( + 'vtysh -c "conf t" -c "router ospf" -c "area 1 nssa default-information-originate"' + ) + + for rname in ["rt1", "rt2", "rt3", "rt4"]: + router_compare_json_output( + rname, "show ip ospf route json", "step2/show_ip_ospf_route.ref" + ) + + +# +# Step 3 +# +# Action(s): +# -rt3: remove NSSA default route +# +# Expected changes: +# -rt2: remove NSSA default route +# +def test_rib_step3(): + logger.info("Test (step 3): verify OSPF routes") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Removing NSSA default on rt4") + tgen.net["rt3"].cmd( + 'vtysh -c "conf t" -c "router ospf" -c "area 1 nssa"' + ) + + for rname in ["rt1", "rt2", "rt3", "rt4"]: + router_compare_json_output( + rname, "show ip ospf route json", "step3/show_ip_ospf_route.ref" + ) + + +# +# Step 4 +# +# Action(s): +# -rt2: configure an NSSA range for 172.16.1.0/24 +# +# Expected changes: +# -rt1: the 172.16.1.1/32 and 172.16.1.2/32 routes should be removed +# -rt1: the 172.16.1.0/24 route should be added +# +def test_rib_step4(): + logger.info("Test (step 4): verify OSPF routes") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Configuring NSSA range on rt2") + tgen.net["rt2"].cmd( + 'vtysh -c "conf t" -c "router ospf" -c "area 1 nssa range 172.16.1.0/24"' + ) + + for rname in ["rt1", "rt2", "rt3", "rt4"]: + router_compare_json_output( + rname, "show ip ospf route json", "step4/show_ip_ospf_route.ref" + ) + + +# +# Step 5 +# +# Action(s): +# -rt4: remove the 172.16.1.1/32 static route +# +# Expected changes: +# -None (the 172.16.1.0/24 range is still active because of 172.16.1.2/32) +# +def test_rib_step5(): + logger.info("Test (step 5): verify OSPF routes") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Removing first static route in rt4") + tgen.net["rt4"].cmd('vtysh -c "conf t" -c "no ip route 172.16.1.1/32 Null0"') + + for rname in ["rt1", "rt2", "rt3", "rt4"]: + router_compare_json_output( + rname, "show ip ospf route json", "step5/show_ip_ospf_route.ref" + ) + + +# +# Step 6 +# +# Action(s): +# -rt4: remove the 172.16.1.2/32 static route +# +# Expected changes: +# -rt1: remove the 172.16.1.0/24 route since the NSSA range is no longer active +# +def test_rib_step6(): + logger.info("Test (step 6): verify OSPF routes") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Removing second static route in rt4") + tgen.net["rt4"].cmd('vtysh -c "conf t" -c "no ip route 172.16.1.2/32 Null0"') + + for rname in ["rt1", "rt2", "rt3", "rt4"]: + router_compare_json_output( + rname, "show ip ospf route json", "step6/show_ip_ospf_route.ref" + ) + + +# +# Step 7 +# +# Action(s): +# -rt4: readd the 172.16.1.1/32 and 172.16.1.2/32 static routes +# +# Expected changes: +# -rt1: readd the 172.16.1.0/24 route since the NSSA range is active again +# +def test_rib_step7(): + logger.info("Test (step 7): verify OSPF routes") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Readding static routes in rt4") + tgen.net["rt4"].cmd('vtysh -c "conf t" -c "ip route 172.16.1.1/32 Null0"') + tgen.net["rt4"].cmd('vtysh -c "conf t" -c "ip route 172.16.1.2/32 Null0"') + + for rname in ["rt1", "rt2", "rt3", "rt4"]: + router_compare_json_output( + rname, "show ip ospf route json", "step7/show_ip_ospf_route.ref" + ) + + +# +# Step 8 +# +# Action(s): +# -rt2: update the NSSA range with a static cost +# +# Expected changes: +# -rt1: update the metric of the 172.16.1.0/24 route from 20 to 1000 +# +def test_rib_step8(): + logger.info("Test (step 8): verify OSPF routes") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Updating the NSSA range cost on rt2") + tgen.net["rt2"].cmd( + 'vtysh -c "conf t" -c "router ospf" -c "area 1 nssa range 172.16.1.0/24 cost 1000"' + ) + + for rname in ["rt1", "rt2", "rt3", "rt4"]: + router_compare_json_output( + rname, "show ip ospf route json", "step8/show_ip_ospf_route.ref" + ) + + +# +# Step 9 +# +# Action(s): +# -rt2: update the NSSA range to not advertise itself +# +# Expected changes: +# -rt1: the 172.16.1.0/24 route should be removed +# +def test_rib_step9(): + logger.info("Test (step 9): verify OSPF routes") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Updating the NSSA range to not advertise itself") + tgen.net["rt2"].cmd( + 'vtysh -c "conf t" -c "router ospf" -c "area 1 nssa range 172.16.1.0/24 not-advertise"' + ) + + for rname in ["rt1", "rt2", "rt3", "rt4"]: + router_compare_json_output( + rname, "show ip ospf route json", "step9/show_ip_ospf_route.ref" + ) + + +# +# Step 10 +# +# Action(s): +# -rt2: remove the NSSA range +# +# Expected changes: +# -rt1: the 172.16.1.1/32 and 172.16.1.2/32 routes should be added +# +def test_rib_step10(): + logger.info("Test (step 10): verify OSPF routes") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Removing NSSA range on rt2") + tgen.net["rt2"].cmd( + 'vtysh -c "conf t" -c "router ospf" -c "no area 1 nssa range 172.16.1.0/24"' + ) + + for rname in ["rt1", "rt2", "rt3", "rt4"]: + router_compare_json_output( + rname, "show ip ospf route json", "step10/show_ip_ospf_route.ref" + ) + + +# Memory leak test template +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.py b/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.py index 5b1a53b895..d5583ac06a 100644 --- a/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.py +++ b/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.py @@ -111,9 +111,7 @@ def ospf_unconfigure_suppress_fa(router_name, area): tgen = get_topogen() router = tgen.gears[router_name] - router.vtysh_cmd( - "conf t\nrouter ospf\nno area {} nssa suppress-fa\nexit\n".format(area) - ) + router.vtysh_cmd("conf t\nrouter ospf\narea {} nssa\nexit\n".format(area)) def ospf_get_lsa_type5(router_name): diff --git a/tests/topotests/pim_acl/r1/ospf_neighbor.json b/tests/topotests/pim_acl/r1/ospf_neighbor.json index af83d6bea3..34ce481a3f 100644 --- a/tests/topotests/pim_acl/r1/ospf_neighbor.json +++ b/tests/topotests/pim_acl/r1/ospf_neighbor.json @@ -2,57 +2,57 @@ "neighbors":{ "192.168.0.11":[ { - "priority":10, + "nbrPriority":10, "converged":"Full", - "address":"192.168.101.11", + "ifaceAddress":"192.168.101.11", "ifaceName":"r1-eth1:192.168.101.1", - "retransmitCounter":0, - "requestCounter":0, - "dbSummaryCounter":0 + "linkStateRetransmissionListCounter":0, + "linkStateRequestListCounter":0, + "databaseSummaryListCounter":0 } ], "192.168.0.12":[ { - "priority":0, + "nbrPriority":0, "converged":"Full", - "address":"192.168.101.12", + "ifaceAddress":"192.168.101.12", "ifaceName":"r1-eth1:192.168.101.1", - "retransmitCounter":0, - "requestCounter":0, - "dbSummaryCounter":0 + "linkStateRetransmissionListCounter":0, + "linkStateRequestListCounter":0, + "databaseSummaryListCounter":0 } ], "192.168.0.13":[ { - "priority":0, + "nbrPriority":0, "converged":"Full", - "address":"192.168.101.13", + "ifaceAddress":"192.168.101.13", "ifaceName":"r1-eth1:192.168.101.1", - "retransmitCounter":0, - "requestCounter":0, - "dbSummaryCounter":0 + "linkStateRetransmissionListCounter":0, + "linkStateRequestListCounter":0, + "databaseSummaryListCounter":0 } ], "192.168.0.14":[ { - "priority":0, + "nbrPriority":0, "converged":"Full", - "address":"192.168.101.14", + "ifaceAddress":"192.168.101.14", "ifaceName":"r1-eth1:192.168.101.1", - "retransmitCounter":0, - "requestCounter":0, - "dbSummaryCounter":0 + "linkStateRetransmissionListCounter":0, + "linkStateRequestListCounter":0, + "databaseSummaryListCounter":0 } ], "192.168.0.15":[ { - "priority":0, + "nbrPriority":0, "converged":"Full", - "address":"192.168.101.15", + "ifaceAddress":"192.168.101.15", "ifaceName":"r1-eth1:192.168.101.1", - "retransmitCounter":0, - "requestCounter":0, - "dbSummaryCounter":0 + "linkStateRetransmissionListCounter":0, + "linkStateRequestListCounter":0, + "databaseSummaryListCounter":0 } ] } diff --git a/tests/topotests/pim_igmp_vrf/r1/ospf_blue_neighbor.json b/tests/topotests/pim_igmp_vrf/r1/ospf_blue_neighbor.json index 1e70fcc36e..198098d3d3 100644 --- a/tests/topotests/pim_igmp_vrf/r1/ospf_blue_neighbor.json +++ b/tests/topotests/pim_igmp_vrf/r1/ospf_blue_neighbor.json @@ -4,9 +4,9 @@ "neighbors":{ "192.168.0.11":[ { - "priority":10, + "nbrPriority":10, "converged":"Full", - "address":"192.168.101.11", + "ifaceAddress":"192.168.101.11", "ifaceName":"r1-eth1:192.168.101.1" } ] diff --git a/tests/topotests/pim_igmp_vrf/r1/ospf_red_neighbor.json b/tests/topotests/pim_igmp_vrf/r1/ospf_red_neighbor.json index 7f2ab248cc..6fce225ffc 100644 --- a/tests/topotests/pim_igmp_vrf/r1/ospf_red_neighbor.json +++ b/tests/topotests/pim_igmp_vrf/r1/ospf_red_neighbor.json @@ -4,9 +4,9 @@ "neighbors":{ "192.168.0.12":[ { - "priority":10, + "nbrPriority":10, "converged":"Full", - "address":"192.168.101.12", + "ifaceAddress":"192.168.101.12", "ifaceName":"r1-eth3:192.168.101.1" } ] diff --git a/tests/topotests/pytest.ini b/tests/topotests/pytest.ini index 6986e3051c..ccbc9d2a16 100644 --- a/tests/topotests/pytest.ini +++ b/tests/topotests/pytest.ini @@ -24,7 +24,7 @@ log_file_date_format = %Y-%m-%d %H:%M:%S junit_logging = all junit_log_passing_tests = true -norecursedirs = .git example_test example_topojson_test lib docker +norecursedirs = .git example_test example_topojson_test lib munet docker # Directory to store test results and run logs in, default shown # rundir = /tmp/topotests diff --git a/tests/topotests/rip_allow_ecmp/__init__.py b/tests/topotests/rip_allow_ecmp/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/rip_allow_ecmp/__init__.py diff --git a/tests/topotests/rip_allow_ecmp/r1/frr.conf b/tests/topotests/rip_allow_ecmp/r1/frr.conf new file mode 100644 index 0000000000..d8eb9a31d3 --- /dev/null +++ b/tests/topotests/rip_allow_ecmp/r1/frr.conf @@ -0,0 +1,9 @@ +! +int r1-eth0 + ip address 192.168.1.1/24 +! +router rip + allow-ecmp + network 192.168.1.0/24 + timers basic 5 15 10 +exit diff --git a/tests/topotests/rip_allow_ecmp/r2/frr.conf b/tests/topotests/rip_allow_ecmp/r2/frr.conf new file mode 100644 index 0000000000..d7ea6f3102 --- /dev/null +++ b/tests/topotests/rip_allow_ecmp/r2/frr.conf @@ -0,0 +1,13 @@ +! +int lo + ip address 10.10.10.1/32 +! +int r2-eth0 + ip address 192.168.1.2/24 +! +router rip + network 192.168.1.0/24 + network 10.10.10.1/32 + timers basic 5 15 10 +exit + diff --git a/tests/topotests/rip_allow_ecmp/r3/frr.conf b/tests/topotests/rip_allow_ecmp/r3/frr.conf new file mode 100644 index 0000000000..2362c47b84 --- /dev/null +++ b/tests/topotests/rip_allow_ecmp/r3/frr.conf @@ -0,0 +1,13 @@ +! +int lo + ip address 10.10.10.1/32 +! +int r3-eth0 + ip address 192.168.1.3/24 +! +router rip + network 192.168.1.0/24 + network 10.10.10.1/32 + timers basic 5 15 10 +exit + diff --git a/tests/topotests/rip_allow_ecmp/test_rip_allow_ecmp.py b/tests/topotests/rip_allow_ecmp/test_rip_allow_ecmp.py new file mode 100644 index 0000000000..acc0aea9e8 --- /dev/null +++ b/tests/topotests/rip_allow_ecmp/test_rip_allow_ecmp.py @@ -0,0 +1,132 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2023 by +# Donatas Abraitis <donatas@opensourcerouting.org> +# + +""" +Test if RIP `allow-ecmp` command works correctly. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + +pytestmark = [pytest.mark.ripd] + + +def setup_module(mod): + topodef = {"s1": ("r1", "r2", "r3")} + tgen = Topogen(topodef, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for _, (rname, router) in enumerate(router_list.items(), 1): + router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname))) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_rip_allow_ecmp(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + + def _show_rip_routes(): + xpath = ( + "/frr-ripd:ripd/instance[vrf='default']" + "/state/routes/route[prefix='10.10.10.1/32']" + ) + try: + output = json.loads( + r1.vtysh_cmd(f"show yang operational-data {xpath} ripd") + ) + except Exception: + return False + + try: + output = output["frr-ripd:ripd"]["instance"][0]["state"]["routes"] + except KeyError: + return False + + expected = { + "route": [ + { + "prefix": "10.10.10.1/32", + "nexthops": { + "nexthop": [ + { + "nh-type": "ip4", + "protocol": "rip", + "rip-type": "normal", + "gateway": "192.168.1.2", + "from": "192.168.1.2", + "tag": 0, + }, + { + "nh-type": "ip4", + "protocol": "rip", + "rip-type": "normal", + "gateway": "192.168.1.3", + "from": "192.168.1.3", + "tag": 0, + }, + ] + }, + "metric": 2, + }, + ] + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_show_rip_routes) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assert result is None, "Can't see 10.10.10.1/32 as multipath in `show ip rip`" + + def _show_routes(): + output = json.loads(r1.vtysh_cmd("show ip route json")) + expected = { + "10.10.10.1/32": [ + { + "nexthops": [ + { + "ip": "192.168.1.2", + "active": True, + }, + { + "ip": "192.168.1.3", + "active": True, + }, + ] + } + ] + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_show_routes) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assert result is None, "Can't see 10.10.10.1/32 as multipath in `show ip route`" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/zebra_rib/test_zebra_rib.py b/tests/topotests/zebra_rib/test_zebra_rib.py index 6137471ea6..e2863218b0 100644 --- a/tests/topotests/zebra_rib/test_zebra_rib.py +++ b/tests/topotests/zebra_rib/test_zebra_rib.py @@ -51,7 +51,8 @@ def config_macvlan(tgen, r_str, device, macvlan): def setup_module(mod): "Sets up the pytest environment" - topodef = {"s1": ("r1", "r1", "r1", "r1", "r1", "r1", "r1", "r1")} + # 8 links to 8 switches on r1 + topodef = {"s{}".format(x): ("r1",) for x in range(1, 9)} tgen = Topogen(topodef, mod.__name__) tgen.start_topology() diff --git a/.dir-locals.el b/tools/emacs.dir-locals.el index b2d7cf376d..b2d7cf376d 100644 --- a/.dir-locals.el +++ b/tools/emacs.dir-locals.el diff --git a/tools/frr-reload.py b/tools/frr-reload.py index 490e519ae9..0e0aec9839 100755 --- a/tools/frr-reload.py +++ b/tools/frr-reload.py @@ -788,6 +788,8 @@ def bgp_delete_nbr_remote_as_line(lines_to_add): # remote-as config. pg_dict = dict() + found_pg_cmd = False + # Find all peer-group commands; create dict of each peer-group # to store assoicated neighbor as value for ctx_keys, line in lines_to_add: @@ -809,6 +811,10 @@ def bgp_delete_nbr_remote_as_line(lines_to_add): } found_pg_cmd = True + # Do nothing if there is no any "peer-group" + if found_pg_cmd is False: + return + # Find peer-group with remote-as command, also search neighbor # associated to peer-group and store into peer-group dict for ctx_keys, line in lines_to_add: @@ -850,7 +856,7 @@ def bgp_delete_nbr_remote_as_line(lines_to_add): for pg in pg_dict[ctx_keys[0]]: if pg_dict[ctx_keys[0]][pg]["remoteas"] == True: for nbr in pg_dict[ctx_keys[0]][pg]["nbr"]: - if re_nbr_rmtas.group(1) in nbr: + if re_nbr_rmtas.group(1) == nbr: lines_to_del_from_add.append((ctx_keys, line)) for ctx_keys, line in lines_to_del_from_add: diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index 4d52bd036d..04f7ff65e9 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -1175,6 +1175,13 @@ static struct cmd_node isis_node = { .parent_node = CONFIG_NODE, .prompt = "%s(config-router)# ", }; + +static struct cmd_node isis_flex_algo_node = { + .name = "isis-flex-algo", + .node = ISIS_FLEX_ALGO_NODE, + .parent_node = ISIS_NODE, + .prompt = "%s(config-router-flex-algo)# ", +}; #endif /* HAVE_ISISD */ #ifdef HAVE_FABRICD @@ -2095,6 +2102,14 @@ DEFUNSH(VTYSH_ISISD, router_isis, router_isis_cmd, vty->node = ISIS_NODE; return CMD_SUCCESS; } + +DEFUNSH(VTYSH_ISISD, isis_flex_algo, isis_flex_algo_cmd, "flex-algo (128-255)", + "Flexible Algorithm\n" + "Flexible Algorithm Number\n") +{ + vty->node = ISIS_FLEX_ALGO_NODE; + return CMD_SUCCESS; +} #endif /* HAVE_ISISD */ #ifdef HAVE_FABRICD @@ -2578,6 +2593,18 @@ DEFUNSH(VTYSH_ISISD, vtysh_quit_isisd, vtysh_quit_isisd_cmd, "quit", { return vtysh_exit_isisd(self, vty, argc, argv); } + +DEFUNSH(VTYSH_ISISD, vtysh_exit_isis_flex_algo, vtysh_exit_isis_flex_algo_cmd, + "exit", "Exit current mode and down to previous mode\n") +{ + return vtysh_exit(vty); +} + +DEFUNSH(VTYSH_ISISD, vtysh_quit_isis_flex_algo, vtysh_quit_isis_flex_algo_cmd, + "quit", "Exit current mode and down to previous mode\n") +{ + return vtysh_exit_isisd(self, vty, argc, argv); +} #endif /* HAVE_ISISD */ #if HAVE_BFDD > 0 @@ -4702,6 +4729,12 @@ void vtysh_init_vty(void) install_element(ISIS_NODE, &vtysh_exit_isisd_cmd); install_element(ISIS_NODE, &vtysh_quit_isisd_cmd); install_element(ISIS_NODE, &vtysh_end_all_cmd); + + install_node(&isis_flex_algo_node); + install_element(ISIS_NODE, &isis_flex_algo_cmd); + install_element(ISIS_FLEX_ALGO_NODE, &vtysh_exit_isis_flex_algo_cmd); + install_element(ISIS_FLEX_ALGO_NODE, &vtysh_quit_isis_flex_algo_cmd); + install_element(ISIS_FLEX_ALGO_NODE, &vtysh_end_all_cmd); #endif /* HAVE_ISISD */ /* fabricd */ diff --git a/yang/frr-if-rmap.yang b/yang/frr-if-rmap.yang new file mode 100644 index 0000000000..0fa2c5eddf --- /dev/null +++ b/yang/frr-if-rmap.yang @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: BSD-2-Clause +module frr-if-rmap { + yang-version 1.1; + namespace "http://frrouting.org/yang/frr-if-rmap"; + prefix frr-if-map; + + import frr-interface { + prefix frr-interface; + } + + import frr-route-map { + prefix frr-route-map; + } + + organization + "FRRouting"; + contact + "FRR Users List: <mailto:frog@lists.frrouting.org> + FRR Development List: <mailto:dev@lists.frrouting.org>"; + description + "This module defines route map settings + + Copyright 2023 LabN Consulting L.L.C + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."; + + revision 2023-04-09 { + description + "Initial revision"; + reference "FRRouting"; + } + + grouping if-route-maps-group { + description "Grouping for interface route maps"; + + container if-route-maps { + description "Collection of interface route-maps"; + + list if-route-map { + must "in-route-map or out-route-map"; + key "interface"; + description "Collection of route-maps for an interface"; + + leaf "interface" { + type frr-interface:interface-ref; + description "The interface the route maps are associated with"; + } + leaf "in-route-map" { + type frr-route-map:route-map-name; + description "Name of the ingress route map"; + } + leaf "out-route-map" { + type frr-route-map:route-map-name; + description "Name of the egress route map"; + } + } + } + } +} diff --git a/yang/frr-isisd.yang b/yang/frr-isisd.yang index 66ec6a410d..ae69d53ccc 100644 --- a/yang/frr-isisd.yang +++ b/yang/frr-isisd.yang @@ -1212,6 +1212,20 @@ module frr-isisd { "Advertise prefixes of passive interfaces only"; } + leaf admin-group-send-zero { + type boolean; + default "false"; + description + "Allow sending the default admin-group value of 0x00000000"; + } + + leaf asla-legacy-flag { + type boolean; + default "false"; + description + "Set the legacy flag (aka. L-FLAG) in the ASLA Sub-TLV."; + } + container lsp { description "Configuration of Link-State Packets (LSP) parameters"; @@ -1594,6 +1608,13 @@ module frr-isisd { "Log changes to the IS-IS adjacencies in this area."; } + leaf log-pdu-drops { + type boolean; + default "false"; + description + "Log any dropped PDUs in this area."; + } + container mpls-te { presence "Present if MPLS-TE is enabled."; description @@ -1616,6 +1637,107 @@ module frr-isisd { } } + container flex-algos { + description + "Flex-Algo Table"; + list flex-algo { + key "flex-algo"; + description + "Configuration for an IS-IS Flex-Algo"; + leaf advertise-definition { + type boolean; + description + "If TRUE, Flex-Algo definition is advertised"; + } + container affinity-include-alls { + description + "Set the include-all affinity"; + leaf-list affinity-include-all { + type string; + max-elements "256"; + description + "Array of Attribute Names"; + } + } + container affinity-include-anies { + description + "Set the include-any affinity"; + leaf-list affinity-include-any { + type string; + max-elements "256"; + description + "Array of Attribute Names"; + } + } + container affinity-exclude-anies { + description + "Set the exclude-any affinity"; + leaf-list affinity-exclude-any { + type string; + max-elements "256"; + description + "Array of Attribute Names"; + } + } + leaf prefix-metric { + type empty; + description + "Use Flex-algo Prefix Metric"; + } + leaf metric-type { + default "igp"; + description + "Set the Flex-Algo metric-type"; + type enumeration { + enum "igp" { + value 0; + description + "IGP Metric"; + } + enum "min-uni-link-delay" { + value 1; + description + "RFC 8570 Sec 4.2 Min Unidirectional Link Delay"; + } + enum "te-default" { + value 2; + description + "RFC 5305 Sec 3.7 Traffic Engineering Default Metric"; + } + } + } + leaf priority { + type uint32 { + range "0..255"; + } + description + "Set the Flex-Algo priority"; + } + leaf dplane-sr-mpls { + type empty; + description + "Advertise and participate in the Flex-Algo Segment-Routing MPLS data-plane"; + } + leaf dplane-srv6 { + type empty; + description + "Advertise and participate in the Flex-Algo Segment-Routing SRv6 data-plane"; + } + leaf dplane-ip { + type empty; + description + "Advertise and participate in the Flex-Algo IP data-plane"; + } + leaf flex-algo { + type uint32 { + range "128..255"; + } + description + "Flex-Algo"; + } + } + } + container segment-routing { description "Segment Routing global configuration."; @@ -1744,6 +1866,87 @@ module frr-isisd { } } } + container algorithm-prefix-sids { + description + "Algorithm SID Table"; + list algorithm-prefix-sid { + key "prefix algo"; + description + "Assign prefix SID for algorithm to an + interface, ISISPHPFlag will be rejected + if set to disable, ISISEXPLICITNULLFlag + will override the value of ISISPHPFlag"; + leaf algo { + type uint32 { + range "128..255"; + } + description + "Algorithm"; + } + leaf prefix { + type inet:ip-prefix; + mandatory true; + description + "Connected prefix sid."; + } + leaf sid-value-type { + type enumeration { + enum "index" { + value 0; + description + "The value will be interpreted as an index."; + } + enum "absolute" { + value 1; + description + "The value will become interpreted as an absolute + value."; + } + } + default "index"; + description + "This leaf defines how value must be interpreted."; + } + leaf sid-value { + type uint32 { + range "0..1048575"; + } + mandatory true; + description + "Value associated with prefix. The value must be + interpreted in the context of sid-value-type."; + } + leaf last-hop-behavior { + type enumeration { + enum "explicit-null" { + value 0; + description + "Use explicit-null for the SID."; + } + enum "no-php" { + value 1; + description + "Do not use Penultimate Hop Popping (PHP) + for the SID."; + } + enum "php" { + value 2; + description + "Use PHP for the SID."; + } + } + default "php"; + description + "Configure last hop behavior."; + } + leaf n-flag-clear { + type boolean; + default "false"; + description + "Not a node SID"; + } + } + } } container mpls { diff --git a/yang/frr-ripd.yang b/yang/frr-ripd.yang index 746bf35d28..d2088e589e 100644 --- a/yang/frr-ripd.yang +++ b/yang/frr-ripd.yang @@ -10,9 +10,15 @@ module frr-ripd { import ietf-yang-types { prefix yang; } + import frr-if-rmap { + prefix frr-if-rmap; + } import frr-interface { prefix frr-interface; } + import frr-nexthop { + prefix frr-nexthop; + } import frr-vrf { prefix frr-vrf; } @@ -60,6 +66,7 @@ module frr-ripd { description "Changed interface references to use frr-interface:interface-ref typedef"; + reference "FRRouting"; } revision 2017-12-06 { description @@ -69,10 +76,35 @@ module frr-ripd { RFC 2453: RIP Version 2."; } + typedef rip-route-type { + type enumeration { + enum normal { + value 0; + description "Normal RIP route type."; + } + enum static { + value 1; + description "Static RIP route type."; + } + enum default { + value 2; + description "Default RIP route type."; + } + enum redistribute { + value 3; + description "Redistribute RIP route type."; + } + enum interface { + value 4; + description "Interface RIP route type."; + } + } + description + "Types of RIP routes."; + } + container ripd { - /* - * Routing instance configuration. - */ + description "rip routing instance data"; list instance { key "vrf"; description @@ -229,9 +261,9 @@ module frr-ripd { "Redistributes routes learned from other routing protocols."; leaf protocol { type frr-route-types:frr-route-types-v4; + must '. != "rip"'; description "Routing protocol."; - must '. != "rip"'; } leaf route-map { type frr-route-map:route-map-ref; @@ -253,6 +285,9 @@ module frr-ripd { is 0."; } } + + uses frr-if-rmap:if-route-maps-group; + leaf-list static-route { type inet:ipv4-prefix; description @@ -291,11 +326,8 @@ module frr-ripd { } } container version { + description "version of rip"; leaf receive { - must - '(. = "1" and ../send = "1") or ' + - '(. = "2" and ../send = "2") or ' + - '(. = "1-2" and ../send = "2")'; type enumeration { enum "1" { value 1; @@ -313,15 +345,15 @@ module frr-ripd { "Accept both RIPv1 and RIPv2 updates."; } } + must + '(. = "1" and ../send = "1") or ' + + '(. = "2" and ../send = "2") or ' + + '(. = "1-2" and ../send = "2")'; default "1-2"; description "Advertisement reception - Version control."; } leaf send { - must - '(../receive = "1" and . = "1") or ' + - '(../receive = "2" and . = "2") or ' + - '(../receive = "1-2" and . = "2")'; type enumeration { enum "1" { value 1; @@ -334,6 +366,10 @@ module frr-ripd { "Send RIPv2 updates only."; } } + must + '(../receive = "1" and . = "1") or ' + + '(../receive = "2" and . = "2") or ' + + '(../receive = "1-2" and . = "2")'; default "2"; description "Advertisement transmission - Version control."; @@ -399,23 +435,81 @@ module frr-ripd { separated by the slash (/) character. The range of values for the prefix-length is 0 to 32."; } + container nexthops { + description "container of nexthops"; + list nexthop { + description "A list of nexthop objects."; + leaf nh-type { + type frr-nexthop:nexthop-type; + mandatory true; + description + "The nexthop type."; + } + leaf protocol { + type frr-route-types:frr-route-types-v4; + description + "The protocol originating this route."; + } + leaf rip-type { + type rip-route-type; + description + "The RIP type of route."; + } + leaf gateway { + type inet:ipv4-address; + description + "The nexthop gateway address."; + } + leaf interface { + type frr-interface:interface-ref; + description + "The nexthop egress interface."; + } + leaf from { + type inet:ipv4-address; + description + "The nexthop gateway address."; + } + leaf tag { + type uint32; + default "0"; + description + "Route tag"; + } + leaf external-metric { + type uint32; + description + "External metric if learned from external protocol."; + } + leaf expire-time { + type uint32; + description + "Seconds before route expires."; + } + } + } + leaf metric { + type uint8 { + range "0..16"; + } + description + "Route metric."; + } + /* + * Replaced by container `nexthops` above. + */ leaf next-hop { type inet:ipv4-address; + status deprecated; description "Next hop IPv4 address."; } leaf interface { type frr-interface:interface-ref; + status deprecated; description "The interface that the route uses."; } - leaf metric { - type uint8 { - range "0..16"; - } - description - "Route metric."; - } } } } @@ -426,6 +520,7 @@ module frr-ripd { * Per-interface configuration data */ augment "/frr-interface:lib/frr-interface:interface" { + description "rip interface data"; container rip { description "RIP interface parameters."; diff --git a/yang/frr-ripngd.yang b/yang/frr-ripngd.yang index d7de4c398a..7b2b135fb5 100644 --- a/yang/frr-ripngd.yang +++ b/yang/frr-ripngd.yang @@ -10,6 +10,9 @@ module frr-ripngd { import ietf-yang-types { prefix yang; } + import frr-if-rmap { + prefix frr-if-rmap; + } import frr-interface { prefix frr-interface; } @@ -196,6 +199,9 @@ module frr-ripngd { is 0."; } } + + uses frr-if-rmap:if-route-maps-group; + leaf-list static-route { type inet:ipv6-prefix; description diff --git a/yang/frr-route-map.yang b/yang/frr-route-map.yang index 224b2caef7..7cb13b60f2 100644 --- a/yang/frr-route-map.yang +++ b/yang/frr-route-map.yang @@ -160,6 +160,18 @@ module frr-route-map { "Set prefix/route metric"; } + identity set-min-metric { + base rmap-set-type; + description + "Set minimum prefix/route metric"; + } + + identity set-max-metric { + base rmap-set-type; + description + "Set maximum prefix/route metric"; + } + identity set-tag { base rmap-set-type; description @@ -346,6 +358,39 @@ module frr-route-map { } } + case set-min-metric { + when "derived-from-or-self(../action, 'set-min-metric')"; + choice minimun-metric-value { + description + "Mimimum metric to set or use"; + case min-metric { + leaf min-metric { + type uint32 { + range "0..4294967295"; + } + description + "Use the following mimumn metric value"; + } + } + } + } + + case set-max-metric { + when "derived-from-or-self(../action, 'set-max-metric')"; + choice maximum-metric-value { + description + "Maximum metric to set or use"; + case max-metric { + leaf max-metric { + type uint32 { + range "0..4294967295"; + } + description + "Use the following maximum metric value"; + } + } + } + } case set-tag { when "derived-from-or-self(../action, 'set-tag')"; leaf tag { diff --git a/yang/subdir.am b/yang/subdir.am index 82a6a01474..eb17c38dbc 100644 --- a/yang/subdir.am +++ b/yang/subdir.am @@ -24,6 +24,7 @@ dist_yangmodels_DATA += yang/frr-filter.yang dist_yangmodels_DATA += yang/frr-module-translator.yang dist_yangmodels_DATA += yang/frr-nexthop.yang dist_yangmodels_DATA += yang/frr-test-module.yang +dist_yangmodels_DATA += yang/frr-if-rmap.yang dist_yangmodels_DATA += yang/frr-interface.yang dist_yangmodels_DATA += yang/frr-route-map.yang dist_yangmodels_DATA += yang/frr-zebra-route-map.yang diff --git a/zebra/interface.c b/zebra/interface.c index 03376afc09..496a85e676 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -137,6 +137,8 @@ static int if_zebra_new_hook(struct interface *ifp) zebra_if->multicast = IF_ZEBRA_DATA_UNSPEC; zebra_if->shutdown = IF_ZEBRA_DATA_OFF; + zebra_if->link_nsid = NS_UNKNOWN; + zebra_if_nhg_dependents_init(zebra_if); zebra_ptm_if_init(zebra_if); @@ -305,6 +307,14 @@ struct interface *if_lookup_by_name_per_ns(struct zebra_ns *ns, return NULL; } +struct interface *if_lookup_by_index_per_nsid(ns_id_t ns_id, uint32_t ifindex) +{ + struct zebra_ns *zns; + + zns = zebra_ns_lookup(ns_id); + return zns ? if_lookup_by_index_per_ns(zns, ifindex) : NULL; +} + const char *ifindex2ifname_per_ns(struct zebra_ns *zns, unsigned int ifindex) { struct interface *ifp; @@ -991,7 +1001,6 @@ void if_up(struct interface *ifp, bool install_connected) { struct zebra_if *zif; struct interface *link_if; - struct zebra_vrf *zvrf = ifp->vrf->info; zif = ifp->info; zif->up_count++; @@ -1024,8 +1033,7 @@ void if_up(struct interface *ifp, bool install_connected) link_if = ifp; zebra_vxlan_svi_up(ifp, link_if); } else if (IS_ZEBRA_IF_VLAN(ifp)) { - link_if = if_lookup_by_index_per_ns(zvrf->zns, - zif->link_ifindex); + link_if = zif->link; if (link_if) zebra_vxlan_svi_up(ifp, link_if); } else if (IS_ZEBRA_IF_MACVLAN(ifp)) { @@ -1049,7 +1057,6 @@ void if_down(struct interface *ifp) { struct zebra_if *zif; struct interface *link_if; - struct zebra_vrf *zvrf = ifp->vrf->info; zif = ifp->info; zif->down_count++; @@ -1068,8 +1075,7 @@ void if_down(struct interface *ifp) link_if = ifp; zebra_vxlan_svi_down(ifp, link_if); } else if (IS_ZEBRA_IF_VLAN(ifp)) { - link_if = if_lookup_by_index_per_ns(zvrf->zns, - zif->link_ifindex); + link_if = zif->link; if (link_if) zebra_vxlan_svi_down(ifp, link_if); } else if (IS_ZEBRA_IF_MACVLAN(ifp)) { @@ -1109,6 +1115,7 @@ void zebra_if_update_link(struct interface *ifp, ifindex_t link_ifindex, if (IS_ZEBRA_IF_VETH(ifp)) return; zif = (struct zebra_if *)ifp->info; + zif->link_nsid = ns_id; zif->link_ifindex = link_ifindex; zif->link = if_lookup_by_index_per_ns(zebra_ns_lookup(ns_id), link_ifindex); @@ -1145,8 +1152,8 @@ void zebra_if_update_all_links(struct zebra_ns *zns) /* update SVI linkages */ if ((zif->link_ifindex != IFINDEX_INTERNAL) && !zif->link) { - zif->link = if_lookup_by_index_per_ns( - zns, zif->link_ifindex); + zif->link = if_lookup_by_index_per_nsid( + zif->link_nsid, zif->link_ifindex); if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug("interface %s/%d's lower fixup to %s/%d", ifp->name, ifp->ifindex, diff --git a/zebra/interface.h b/zebra/interface.h index 4c6ebaa11d..e5545d6ba0 100644 --- a/zebra/interface.h +++ b/zebra/interface.h @@ -195,6 +195,7 @@ struct zebra_if { struct list *mac_list; /* Link fields - for sub-interfaces. */ + ns_id_t link_nsid; ifindex_t link_ifindex; struct interface *link; @@ -259,6 +260,8 @@ extern struct interface *if_lookup_by_index_per_ns(struct zebra_ns *, uint32_t); extern struct interface *if_lookup_by_name_per_ns(struct zebra_ns *, const char *); extern struct interface *if_link_per_ns(struct zebra_ns *, struct interface *); +extern struct interface *if_lookup_by_index_per_nsid(ns_id_t nsid, + uint32_t ifindex); extern const char *ifindex2ifname_per_ns(struct zebra_ns *, unsigned int); extern void if_unlink_per_ns(struct interface *); diff --git a/zebra/rib.h b/zebra/rib.h index 26425a331f..a56bb05d68 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -180,7 +180,7 @@ struct route_entry { * sub-queue 9: any other origin (if any) typically those that * don't generate routes */ -#define MQ_SIZE 10 +#define MQ_SIZE 11 struct meta_queue { struct list *subq[MQ_SIZE]; uint32_t size; /* sum of lengths of all subqueues */ @@ -602,6 +602,12 @@ static inline struct nexthop_group *rib_get_fib_backup_nhg( return &(re->fib_backup_ng); } +extern void zebra_gr_process_client(afi_t afi, vrf_id_t vrf_id, uint8_t proto, + uint8_t instance); + +extern int rib_add_gr_run(afi_t afi, vrf_id_t vrf_id, uint8_t proto, + uint8_t instance); + extern void zebra_vty_init(void); extern pid_t pid; diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index fb1ebc6827..e821572c5d 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -3745,6 +3745,11 @@ dplane_route_update_internal(struct route_node *rn, NEXTHOP_FLAG_FIB); } + if ((op == DPLANE_OP_ROUTE_UPDATE) && old_re && re && + (old_re != re) && + !CHECK_FLAG(re->status, ROUTE_ENTRY_INSTALLED)) + SET_FLAG(re->status, ROUTE_ENTRY_INSTALLED); + dplane_ctx_free(&ctx); return ZEBRA_DPLANE_REQUEST_SUCCESS; } @@ -6853,10 +6858,6 @@ void zebra_dplane_shutdown(void) zdplane_info.dg_run = false; - if (zdplane_info.dg_t_update) - event_cancel_async(zdplane_info.dg_t_update->master, - &zdplane_info.dg_t_update, NULL); - frr_pthread_stop(zdplane_info.dg_pthread, NULL); /* Destroy pthread */ diff --git a/zebra/zebra_evpn_mac.c b/zebra/zebra_evpn_mac.c index ccc1b61e06..5ba075e297 100644 --- a/zebra/zebra_evpn_mac.c +++ b/zebra/zebra_evpn_mac.c @@ -1685,6 +1685,7 @@ struct zebra_mac *zebra_evpn_proc_sync_mac_update(struct zebra_evpn *zevpn, struct zebra_mac *mac; bool inform_bgp = false; bool inform_dataplane = false; + bool mac_inactive = false; bool seq_change = false; bool es_change = false; uint32_t tmp_seq; @@ -1701,6 +1702,7 @@ struct zebra_mac *zebra_evpn_proc_sync_mac_update(struct zebra_evpn *zevpn, */ inform_bgp = true; inform_dataplane = true; + mac_inactive = true; /* create the MAC and associate it with the dest ES */ mac = zebra_evpn_mac_add(zevpn, macaddr); @@ -1812,6 +1814,7 @@ struct zebra_mac *zebra_evpn_proc_sync_mac_update(struct zebra_evpn *zevpn, if (es_change) { inform_bgp = true; inform_dataplane = true; + mac_inactive = true; } /* if peer-flag is being set notify dataplane that the @@ -1867,9 +1870,9 @@ struct zebra_mac *zebra_evpn_proc_sync_mac_update(struct zebra_evpn *zevpn, * the activity as we are yet to establish activity * locally */ - zebra_evpn_sync_mac_dp_install(mac, false /* set_inactive */, - false /* force_clear_static */, - __func__); + zebra_evpn_sync_mac_dp_install( + mac, mac_inactive /* set_inactive */, + false /* force_clear_static */, __func__); } return mac; diff --git a/zebra/zebra_gr.c b/zebra/zebra_gr.c index 96d598f7c4..cf2056b7ac 100644 --- a/zebra/zebra_gr.c +++ b/zebra/zebra_gr.c @@ -42,8 +42,8 @@ static struct zserv *zebra_gr_find_stale_client(struct zserv *client); static void zebra_gr_route_stale_delete_timer_expiry(struct event *thread); static int32_t zebra_gr_delete_stale_routes(struct client_gr_info *info); static void zebra_gr_process_client_stale_routes(struct zserv *client, - vrf_id_t vrf_id); - + struct client_gr_info *info); +static void zebra_gr_delete_stale_route_table_afi(struct event *event); /* * Debug macros. */ @@ -53,7 +53,6 @@ static void zebra_gr_process_client_stale_routes(struct zserv *client, zlog_debug(msg, ##__VA_ARGS__); \ } while (0) - /* * Client connection functions */ @@ -82,11 +81,12 @@ void zebra_gr_stale_client_cleanup(struct list *client_list) if (info->t_stale_removal != NULL) { EVENT_OFF(info->t_stale_removal); info->t_stale_removal = NULL; + info->do_delete = true; /* Process the stale routes */ event_execute( zrouter.master, zebra_gr_route_stale_delete_timer_expiry, - info, 1); + info, 0); } } } @@ -101,6 +101,8 @@ static struct client_gr_info *zebra_gr_client_info_create(struct zserv *client) info = XCALLOC(MTYPE_ZEBRA_GR, sizeof(struct client_gr_info)); + info->stale_client_ptr = client; + TAILQ_INSERT_TAIL(&(client->gr_info_queue), info, gr_info); return info; } @@ -108,8 +110,8 @@ static struct client_gr_info *zebra_gr_client_info_create(struct zserv *client) /* * A helper function to delete and destroy client info. */ -static void zebra_gr_client_info_delte(struct zserv *client, - struct client_gr_info *info) +static void zebra_gr_client_info_delete(struct zserv *client, + struct client_gr_info *info) { struct vrf *vrf = vrf_lookup_by_id(info->vrf_id); @@ -117,8 +119,6 @@ static void zebra_gr_client_info_delte(struct zserv *client, EVENT_OFF(info->t_stale_removal); - XFREE(MTYPE_ZEBRA_GR, info->current_prefix); - LOG_GR("%s: Instance info is being deleted for client %s vrf %s(%u)", __func__, zebra_route_string(client->proto), VRF_LOGNAME(vrf), info->vrf_id); @@ -164,7 +164,6 @@ int32_t zebra_gr_client_disconnect(struct zserv *client) zebra_gr_route_stale_delete_timer_expiry, info, info->stale_removal_time, &info->t_stale_removal); - info->current_afi = AFI_IP; info->stale_client_ptr = client; info->stale_client = true; LOG_GR("%s: Client %s vrf %s(%u) Stale timer update to %d", @@ -287,31 +286,65 @@ void zebra_gr_client_reconnect(struct zserv *client) zserv_client_delete(old_client); } +struct zebra_gr_afi_clean { + struct client_gr_info *info; + afi_t afi; + uint8_t proto; + uint8_t instance; + + struct event *t_gac; +}; + /* * Functions to deal with capabilities */ /* - * Update the graceful restart information - * for the client instance. - * This function handles all the capabilities that are received. + * Function to decode and call appropriate functions + * to handle client capabilities. */ -static void zebra_client_update_info(struct zserv *client, struct zapi_cap *api) +void zread_client_capabilities(ZAPI_HANDLER_ARGS) { + struct zapi_cap api; struct client_gr_info *info = NULL; + struct stream *s; + struct vrf *vrf; + + s = msg; + + if (zapi_capabilities_decode(s, &api)) { + LOG_GR("%s: Error in reading capabilities for client %s", + __func__, zebra_route_string(client->proto)); + return; + } + + vrf = vrf_lookup_by_id(api.vrf_id); + + /* + * If this ever matters uncomment and add safi to the + * arrays as needed to track + */ + if (api.safi != SAFI_UNICAST) + return; + + /* GR only for dynamic clients */ + if (client->proto <= ZEBRA_ROUTE_CONNECT) { + LOG_GR("%s: GR capabilities for client %s not supported", + __func__, zebra_route_string(client->proto)); + return; + } /* Find the bgp information for the specified vrf id */ TAILQ_FOREACH (info, &client->gr_info_queue, gr_info) { - if (info->vrf_id == api->vrf_id) + if (info->vrf_id == api.vrf_id) break; } - /* * If the command is delete, then cancel the stale timer and * delete the bgp info */ - switch (api->cap) { + switch (api.cap) { case ZEBRA_CLIENT_GR_DISABLE: if (!info) return; @@ -323,7 +356,7 @@ static void zebra_client_update_info(struct zserv *client, struct zapi_cap *api) if ((info->gr_enable) && (client->gr_instance_count > 0)) client->gr_instance_count--; - zebra_gr_client_info_delte(client, info); + zebra_gr_client_info_delete(client, info); break; case ZEBRA_CLIENT_GR_CAPABILITIES: /* Allocate bgp info */ @@ -332,18 +365,16 @@ static void zebra_client_update_info(struct zserv *client, struct zapi_cap *api) /* Update other parameters */ if (!info->gr_enable) { - struct vrf *vrf = vrf_lookup_by_id(api->vrf_id); - client->gr_instance_count++; LOG_GR("%s: Cient %s vrf %s(%u) GR enabled count %d", __func__, zebra_route_string(client->proto), - VRF_LOGNAME(vrf), api->vrf_id, + VRF_LOGNAME(vrf), api.vrf_id, client->gr_instance_count); - info->capabilities = api->cap; - info->stale_removal_time = api->stale_removal_time; - info->vrf_id = api->vrf_id; + info->capabilities = api.cap; + info->stale_removal_time = api.stale_removal_time; + info->vrf_id = api.vrf_id; info->gr_enable = true; } break; @@ -353,107 +384,54 @@ static void zebra_client_update_info(struct zserv *client, struct zapi_cap *api) /* Update the stale removal timer */ if (info && info->t_stale_removal == NULL) { - struct vrf *vrf = vrf_lookup_by_id(info->vrf_id); LOG_GR("%s: vrf %s(%u) Stale time: %d is now update to: %d", __func__, VRF_LOGNAME(vrf), info->vrf_id, info->stale_removal_time, - api->stale_removal_time); + api.stale_removal_time); - info->stale_removal_time = api->stale_removal_time; + info->stale_removal_time = api.stale_removal_time; } break; case ZEBRA_CLIENT_ROUTE_UPDATE_COMPLETE: if (!info) { - LOG_GR("%s: Client %s route update complete for AFI %d, SAFI %d", - __func__, zebra_route_string(client->proto), - api->afi, api->safi); - } else { - struct vrf *vrf = vrf_lookup_by_id(info->vrf_id); - - LOG_GR("%s: Client %s vrf %s(%u) route update complete for AFI %d, SAFI %d", + LOG_GR("%s: Client %s route update complete for AFI %d, SAFI %d, no Graceful Restart communication, returning", __func__, zebra_route_string(client->proto), - VRF_LOGNAME(vrf), info->vrf_id, api->afi, - api->safi); - info->route_sync[api->afi][api->safi] = true; + api.afi, api.safi); + return; } + + LOG_GR("%s: Client %s vrf %s(%u) route update complete for AFI %d, SAFI %d", + __func__, zebra_route_string(client->proto), + VRF_LOGNAME(vrf), info->vrf_id, api.afi, api.safi); + info->route_sync[api.afi] = true; + + /* + * Schedule for after anything already in the meta Q + */ + rib_add_gr_run(api.afi, api.vrf_id, client->proto, + client->instance); + zebra_gr_process_client_stale_routes(client, info); break; case ZEBRA_CLIENT_ROUTE_UPDATE_PENDING: if (!info) { LOG_GR("%s: Client %s route update pending for AFI %d, SAFI %d", __func__, zebra_route_string(client->proto), - api->afi, api->safi); + api.afi, api.safi); } else { - struct vrf *vrf = vrf_lookup_by_id(info->vrf_id); - LOG_GR("%s: Client %s vrf %s(%u) route update pending for AFI %d, SAFI %d", __func__, zebra_route_string(client->proto), - VRF_LOGNAME(vrf), info->vrf_id, api->afi, - api->safi); + VRF_LOGNAME(vrf), info->vrf_id, api.afi, + api.safi); - info->af_enabled[api->afi][api->safi] = true; + info->af_enabled[api.afi] = true; } break; } } /* - * Handler for capabilities that are received from client. - */ -static void zebra_client_capabilities_handler(struct zserv *client, - struct zapi_cap *api) -{ - switch (api->cap) { - case ZEBRA_CLIENT_GR_CAPABILITIES: - case ZEBRA_CLIENT_ROUTE_UPDATE_PENDING: - case ZEBRA_CLIENT_GR_DISABLE: - case ZEBRA_CLIENT_RIB_STALE_TIME: - /* - * For all the cases we need to update the client info. - */ - zebra_client_update_info(client, api); - break; - case ZEBRA_CLIENT_ROUTE_UPDATE_COMPLETE: - /* - * After client info has been updated delete all - * stale routes - */ - zebra_client_update_info(client, api); - zebra_gr_process_client_stale_routes(client, api->vrf_id); - break; - } -} - -/* - * Function to decode and call appropriate functions - * to handle client capabilities. - */ -void zread_client_capabilities(ZAPI_HANDLER_ARGS) -{ - struct zapi_cap api; - struct stream *s; - - s = msg; - - if (zapi_capabilities_decode(s, &api)) { - LOG_GR("%s: Error in reading capabilities for client %s", - __func__, zebra_route_string(client->proto)); - return; - } - - /* GR only for dynamic clients */ - if (client->proto <= ZEBRA_ROUTE_CONNECT) { - LOG_GR("%s: GR capabilities for client %s not supported", - __func__, zebra_route_string(client->proto)); - return; - } - /* Call the capabilities handler */ - zebra_client_capabilities_handler(client, &api); -} - - -/* * Stale route handling */ @@ -470,10 +448,6 @@ static void zebra_gr_route_stale_delete_timer_expiry(struct event *thread) client = (struct zserv *)info->stale_client_ptr; - /* Set the flag to indicate all stale route deletion */ - if (thread->u.val == 1) - info->do_delete = true; - cnt = zebra_gr_delete_stale_routes(info); /* Restart the timer */ @@ -492,8 +466,6 @@ static void zebra_gr_route_stale_delete_timer_expiry(struct event *thread) __func__, zebra_route_string(client->proto), VRF_LOGNAME(vrf), info->vrf_id); - XFREE(MTYPE_ZEBRA_GR, info->current_prefix); - info->current_afi = 0; zebra_gr_delete_stale_client(info); } } @@ -502,14 +474,13 @@ static void zebra_gr_route_stale_delete_timer_expiry(struct event *thread) /* * Function to process to check if route entry is stale * or has been updated. + * + * Returns true when a node is deleted else false */ -static void zebra_gr_process_route_entry(struct zserv *client, +static bool zebra_gr_process_route_entry(struct zserv *client, struct route_node *rn, struct route_entry *re) { - if ((client == NULL) || (rn == NULL) || (re == NULL)) - return; - /* If the route is not refreshed after restart, delete the entry */ if (re->uptime < client->restart_time) { if (IS_ZEBRA_DEBUG_RIB) @@ -517,7 +488,62 @@ static void zebra_gr_process_route_entry(struct zserv *client, __func__, zebra_route_string(client->proto), &rn->p); rib_delnode(rn, re); + + return true; } + + return false; +} + +static void zebra_gr_delete_stale_route_table_afi(struct event *event) +{ + struct zebra_gr_afi_clean *gac = EVENT_ARG(event); + struct route_table *table; + struct route_node *rn; + struct route_entry *re, *next; + struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(gac->info->vrf_id); + int32_t n = 0; + + if (!zvrf) + goto done; + + table = zvrf->table[gac->afi][SAFI_UNICAST]; + if (!table) + goto done; + + for (rn = route_top(table); rn; rn = srcdest_route_next(rn)) { + RNODE_FOREACH_RE_SAFE (rn, re, next) { + if (CHECK_FLAG(re->status, ROUTE_ENTRY_REMOVED)) + continue; + + /* If the route refresh is received + * after restart then do not delete + * the route + */ + + if (re->type == gac->proto && + re->instance == gac->instance && + zebra_gr_process_route_entry( + gac->info->stale_client_ptr, rn, re)) + n++; + + /* If the max route count is reached + * then timer thread will be restarted + * Store the current prefix and afi + */ + if ((n >= ZEBRA_MAX_STALE_ROUTE_COUNT) && + (gac->info->do_delete == false)) { + event_add_timer( + zrouter.master, + zebra_gr_delete_stale_route_table_afi, + gac, ZEBRA_DEFAULT_STALE_UPDATE_DELAY, + &gac->t_gac); + } + } + } + +done: + XFREE(MTYPE_ZEBRA_GR, gac); } /* @@ -528,19 +554,11 @@ static void zebra_gr_process_route_entry(struct zserv *client, static int32_t zebra_gr_delete_stale_route(struct client_gr_info *info, struct zebra_vrf *zvrf) { - struct route_node *rn, *curr; - struct route_entry *re; - struct route_entry *next; - struct route_table *table; - int32_t n = 0; - afi_t afi, curr_afi; + afi_t afi; uint8_t proto; uint16_t instance; struct zserv *s_client; - if ((info == NULL) || (zvrf == NULL)) - return -1; - s_client = info->stale_client_ptr; if (s_client == NULL) { LOG_GR("%s: Stale client %s(%u) not present", __func__, @@ -550,69 +568,18 @@ static int32_t zebra_gr_delete_stale_route(struct client_gr_info *info, proto = s_client->proto; instance = s_client->instance; - curr_afi = info->current_afi; LOG_GR("%s: Client %s %s(%u) stale routes are being deleted", __func__, zebra_route_string(proto), zvrf->vrf->name, zvrf->vrf->vrf_id); /* Process routes for all AFI */ - for (afi = curr_afi; afi < AFI_MAX; afi++) { - table = zvrf->table[afi][SAFI_UNICAST]; + for (afi = AFI_IP; afi < AFI_MAX; afi++) { - if (table) { - /* - * If the current prefix is NULL then get the first - * route entry in the table - */ - if (info->current_prefix == NULL) { - rn = route_top(table); - if (rn == NULL) - continue; - curr = rn; - } else - /* Get the next route entry */ - curr = route_table_get_next( - table, info->current_prefix); - - for (rn = curr; rn; rn = srcdest_route_next(rn)) { - RNODE_FOREACH_RE_SAFE (rn, re, next) { - if (CHECK_FLAG(re->status, - ROUTE_ENTRY_REMOVED)) - continue; - /* If the route refresh is received - * after restart then do not delete - * the route - */ - if (re->type == proto - && re->instance == instance) { - zebra_gr_process_route_entry( - s_client, rn, re); - n++; - } - - /* If the max route count is reached - * then timer thread will be restarted - * Store the current prefix and afi - */ - if ((n >= ZEBRA_MAX_STALE_ROUTE_COUNT) - && (info->do_delete == false)) { - info->current_afi = afi; - info->current_prefix = XCALLOC( - MTYPE_ZEBRA_GR, - sizeof(struct prefix)); - prefix_copy( - info->current_prefix, - &rn->p); - return n; - } - } - } - } /* - * Reset the current prefix to indicate processing completion - * of the current AFI + * Schedule for immediately after anything in the + * meta-Q */ - XFREE(MTYPE_ZEBRA_GR, info->current_prefix); + rib_add_gr_run(afi, info->vrf_id, proto, instance); } return 0; } @@ -623,21 +590,13 @@ static int32_t zebra_gr_delete_stale_route(struct client_gr_info *info, */ static int32_t zebra_gr_delete_stale_routes(struct client_gr_info *info) { - struct vrf *vrf; struct zebra_vrf *zvrf; uint64_t cnt = 0; if (info == NULL) return -1; - /* Get the current VRF */ - vrf = vrf_lookup_by_id(info->vrf_id); - if (vrf == NULL) { - LOG_GR("%s: Invalid VRF specified %u", __func__, info->vrf_id); - return -1; - } - - zvrf = vrf->info; + zvrf = zebra_vrf_lookup_by_id(info->vrf_id); if (zvrf == NULL) { LOG_GR("%s: Invalid VRF entry %u", __func__, info->vrf_id); return -1; @@ -652,49 +611,63 @@ static int32_t zebra_gr_delete_stale_routes(struct client_gr_info *info) * and cancels the stale timer */ static void zebra_gr_process_client_stale_routes(struct zserv *client, - vrf_id_t vrf_id) + struct client_gr_info *info) { - struct client_gr_info *info = NULL; afi_t afi; - safi_t safi; - - TAILQ_FOREACH (info, &client->gr_info_queue, gr_info) { - if (info->vrf_id == vrf_id) - break; - } if (info == NULL) return; /* Check if route update completed for all AFI, SAFI */ - FOREACH_AFI_SAFI_NSF (afi, safi) { - if (info->af_enabled[afi][safi]) { - if (!info->route_sync[afi][safi]) { - struct vrf *vrf = vrf_lookup_by_id(vrf_id); - - LOG_GR("%s: Client %s vrf: %s(%u) route update not completed for AFI %d, SAFI %d", - __func__, - zebra_route_string(client->proto), - VRF_LOGNAME(vrf), info->vrf_id, afi, - safi); - return; - } + for (afi = AFI_IP; afi < AFI_MAX; afi++) { + if (info->af_enabled[afi] && !info->route_sync[afi]) { + struct vrf *vrf = vrf_lookup_by_id(info->vrf_id); + + LOG_GR("%s: Client %s vrf: %s(%u) route update not completed for AFI %d", + __func__, zebra_route_string(client->proto), + VRF_LOGNAME(vrf), info->vrf_id, afi); + return; } } /* * Route update completed for all AFI, SAFI - * Cancel the stale timer and process the routes + * Cancel the stale timer, routes are already being processed */ if (info->t_stale_removal) { - struct vrf *vrf = vrf_lookup_by_id(vrf_id); + struct vrf *vrf = vrf_lookup_by_id(info->vrf_id); LOG_GR("%s: Client %s canceled stale delete timer vrf %s(%d)", __func__, zebra_route_string(client->proto), VRF_LOGNAME(vrf), info->vrf_id); EVENT_OFF(info->t_stale_removal); - event_execute(zrouter.master, - zebra_gr_route_stale_delete_timer_expiry, info, - 0); } } + +void zebra_gr_process_client(afi_t afi, vrf_id_t vrf_id, uint8_t proto, + uint8_t instance) +{ + struct zserv *client = zserv_find_client(proto, instance); + struct client_gr_info *info = NULL; + struct zebra_gr_afi_clean *gac; + + if (client == NULL) + return; + + TAILQ_FOREACH (info, &client->gr_info_queue, gr_info) { + if (info->vrf_id == vrf_id) + break; + } + + if (info == NULL) + return; + + gac = XCALLOC(MTYPE_ZEBRA_GR, sizeof(*gac)); + gac->info = info; + gac->afi = afi; + gac->proto = proto; + gac->instance = instance; + + event_add_event(zrouter.master, zebra_gr_delete_stale_route_table_afi, + gac, 0, &gac->t_gac); +} diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index ceb0640553..adcaf64044 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -64,7 +64,12 @@ DEFINE_HOOK(rib_update, (struct route_node * rn, const char *reason), DEFINE_HOOK(rib_shutdown, (struct route_node * rn), (rn)); -/* Meta Q's specific names */ +/* + * Meta Q's specific names + * + * If you add something here ensure that you + * change MQ_SIZE as well over in rib.h + */ enum meta_queue_indexes { META_QUEUE_NHG, META_QUEUE_EVPN, @@ -76,6 +81,7 @@ enum meta_queue_indexes { META_QUEUE_NOTBGP, META_QUEUE_BGP, META_QUEUE_OTHER, + META_QUEUE_GR_RUN, }; /* Each route type's string and default distance value. */ @@ -250,6 +256,8 @@ static const char *subqueue2str(enum meta_queue_indexes index) return "BGP Routes"; case META_QUEUE_OTHER: return "Other Routes"; + case META_QUEUE_GR_RUN: + return "Graceful Restart"; } return "Unknown"; @@ -3089,6 +3097,23 @@ static void process_subq_early_route(struct listnode *lnode) process_subq_early_route_add(ere); } +struct meta_q_gr_run { + afi_t afi; + vrf_id_t vrf_id; + uint8_t proto; + uint8_t instance; +}; + +static void process_subq_gr_run(struct listnode *lnode) +{ + struct meta_q_gr_run *gr_run = listgetdata(lnode); + + zebra_gr_process_client(gr_run->afi, gr_run->vrf_id, gr_run->proto, + gr_run->instance); + + XFREE(MTYPE_WQ_WRAPPER, gr_run); +} + /* * Examine the specified subqueue; process one entry and return 1 if * there is a node, return 0 otherwise. @@ -3122,6 +3147,9 @@ static unsigned int process_subq(struct list *subq, case META_QUEUE_OTHER: process_subq_route(lnode, qindex); break; + case META_QUEUE_GR_RUN: + process_subq_gr_run(lnode); + break; } list_delete_node(subq, lnode); @@ -3727,6 +3755,23 @@ static void early_route_meta_queue_free(struct meta_queue *mq, struct list *l, } } +static void rib_meta_queue_gr_run_free(struct meta_queue *mq, struct list *l, + struct zebra_vrf *zvrf) +{ + struct meta_q_gr_run *gr_run; + struct listnode *node, *nnode; + + for (ALL_LIST_ELEMENTS(l, node, nnode, gr_run)) { + if (zvrf && zvrf->vrf->vrf_id != gr_run->vrf_id) + continue; + + XFREE(MTYPE_WQ_WRAPPER, gr_run); + node->data = NULL; + list_delete_node(l, node); + mq->size--; + } +} + void meta_queue_free(struct meta_queue *mq, struct zebra_vrf *zvrf) { enum meta_queue_indexes i; @@ -3754,6 +3799,9 @@ void meta_queue_free(struct meta_queue *mq, struct zebra_vrf *zvrf) case META_QUEUE_OTHER: rib_meta_queue_free(mq, mq->subq[i], zvrf); break; + case META_QUEUE_GR_RUN: + rib_meta_queue_gr_run_free(mq, mq->subq[i], zvrf); + break; } if (!zvrf) list_delete(&mq->subq[i]); @@ -4094,6 +4142,17 @@ void _route_entry_dump(const char *func, union prefixconstptr pp, zlog_debug("%s: dump complete", straddr); } +static int rib_meta_queue_gr_run_add(struct meta_queue *mq, void *data) +{ + listnode_add(mq->subq[META_QUEUE_GR_RUN], data); + mq->size++; + + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug("Graceful Run adding"); + + return 0; +} + static int rib_meta_queue_early_route_add(struct meta_queue *mq, void *data) { struct zebra_early_route *ere = data; @@ -4110,6 +4169,20 @@ static int rib_meta_queue_early_route_add(struct meta_queue *mq, void *data) return 0; } +int rib_add_gr_run(afi_t afi, vrf_id_t vrf_id, uint8_t proto, uint8_t instance) +{ + struct meta_q_gr_run *gr_run; + + gr_run = XCALLOC(MTYPE_WQ_WRAPPER, sizeof(*gr_run)); + + gr_run->afi = afi; + gr_run->proto = proto; + gr_run->vrf_id = vrf_id; + gr_run->instance = instance; + + return mq_add_handler(gr_run, rib_meta_queue_gr_run_add); +} + struct route_entry *zebra_rib_route_entry_new(vrf_id_t vrf_id, int type, uint8_t instance, uint32_t flags, uint32_t nhe_id, diff --git a/zebra/zebra_routemap.c b/zebra/zebra_routemap.c index d9a7ee465a..142501b149 100644 --- a/zebra/zebra_routemap.c +++ b/zebra/zebra_routemap.c @@ -1041,7 +1041,7 @@ route_match_ip_next_hop(void *rule, const struct prefix *prefix, void *object) } alist = access_list_lookup(AFI_IP, (char *)rule); if (alist == NULL) { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))) zlog_debug( "%s: Access-List Specified: %s does not exist defaulting to NO_MATCH", __func__, (char *)rule); @@ -1104,7 +1104,7 @@ route_match_ip_next_hop_prefix_list(void *rule, const struct prefix *prefix, } plist = prefix_list_lookup(AFI_IP, (char *)rule); if (plist == NULL) { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))) zlog_debug( "%s: Prefix List %s specified does not exist defaulting to NO_MATCH", __func__, (char *)rule); @@ -1145,7 +1145,7 @@ route_match_address(afi_t afi, void *rule, const struct prefix *prefix, alist = access_list_lookup(afi, (char *)rule); if (alist == NULL) { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))) zlog_debug( "%s: Access-List Specified: %s does not exist defaulting to NO_MATCH", __func__, (char *)rule); @@ -1207,7 +1207,7 @@ route_match_address_prefix_list(void *rule, const struct prefix *prefix, plist = prefix_list_lookup(afi, (char *)rule); if (plist == NULL) { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))) zlog_debug( "%s: Prefix List %s specified does not exist defaulting to NO_MATCH", __func__, (char *)rule); diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index c370ad9169..36290f99e0 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -2582,19 +2582,18 @@ void zebra_vxlan_print_specific_rmac_l3vni(struct vty *vty, vni_t l3vni, struct zebra_mac *zrmac = NULL; json_object *json = NULL; + if (use_json) + json = json_object_new_object(); + if (!is_evpn_enabled()) { - if (use_json) - vty_out(vty, "{}\n"); + vty_json(vty, json); return; } - if (use_json) - json = json_object_new_object(); - zl3vni = zl3vni_lookup(l3vni); if (!zl3vni) { if (use_json) - vty_out(vty, "{}\n"); + vty_json(vty, json); else vty_out(vty, "%% L3-VNI %u doesn't exist\n", l3vni); return; @@ -2603,7 +2602,7 @@ void zebra_vxlan_print_specific_rmac_l3vni(struct vty *vty, vni_t l3vni, zrmac = zl3vni_rmac_lookup(zl3vni, rmac); if (!zrmac) { if (use_json) - vty_out(vty, "{}\n"); + vty_json(vty, json); else vty_out(vty, "%% Requested RMAC doesn't exist in L3-VNI %u\n", @@ -2624,13 +2623,18 @@ void zebra_vxlan_print_rmacs_l3vni(struct vty *vty, vni_t l3vni, bool use_json) struct rmac_walk_ctx wctx; json_object *json = NULL; - if (!is_evpn_enabled()) + if (use_json) + json = json_object_new_object(); + + if (!is_evpn_enabled()) { + vty_json(vty, json); return; + } zl3vni = zl3vni_lookup(l3vni); if (!zl3vni) { if (use_json) - vty_out(vty, "{}\n"); + vty_json(vty, json); else vty_out(vty, "%% L3-VNI %u does not exist\n", l3vni); return; @@ -2639,9 +2643,6 @@ void zebra_vxlan_print_rmacs_l3vni(struct vty *vty, vni_t l3vni, bool use_json) if (!num_rmacs) return; - if (use_json) - json = json_object_new_object(); - memset(&wctx, 0, sizeof(wctx)); wctx.vty = vty; wctx.json = json; @@ -2663,15 +2664,14 @@ void zebra_vxlan_print_rmacs_all_l3vni(struct vty *vty, bool use_json) json_object *json = NULL; void *args[2]; + if (use_json) + json = json_object_new_object(); + if (!is_evpn_enabled()) { - if (use_json) - vty_out(vty, "{}\n"); + vty_json(vty, json); return; } - if (use_json) - json = json_object_new_object(); - args[0] = vty; args[1] = json; hash_iterate(zrouter.l3vni_table, @@ -2690,15 +2690,14 @@ void zebra_vxlan_print_specific_nh_l3vni(struct vty *vty, vni_t l3vni, struct zebra_neigh *n = NULL; json_object *json = NULL; + if (use_json) + json = json_object_new_object(); + if (!is_evpn_enabled()) { - if (use_json) - vty_out(vty, "{}\n"); + vty_json(vty, json); return; } - if (use_json) - json = json_object_new_object(); - /* If vni=0 passed, assume svd lookup */ if (!l3vni) n = svd_nh_lookup(ip); @@ -2799,15 +2798,14 @@ void zebra_vxlan_print_nh_all_l3vni(struct vty *vty, bool use_json) json_object *json = NULL; void *args[2]; + if (use_json) + json = json_object_new_object(); + if (!is_evpn_enabled()) { - if (use_json) - vty_out(vty, "{}\n"); + vty_json(vty, json); return; } - if (use_json) - json = json_object_new_object(); - args[0] = vty; args[1] = json; hash_iterate(zrouter.l3vni_table, @@ -2828,24 +2826,23 @@ void zebra_vxlan_print_l3vni(struct vty *vty, vni_t vni, bool use_json) json_object *json = NULL; struct zebra_l3vni *zl3vni = NULL; + if (use_json) + json = json_object_new_object(); + if (!is_evpn_enabled()) { - if (use_json) - vty_out(vty, "{}\n"); + vty_json(vty, json); return; } zl3vni = zl3vni_lookup(vni); if (!zl3vni) { if (use_json) - vty_out(vty, "{}\n"); + vty_json(vty, json); else vty_out(vty, "%% VNI %u does not exist\n", vni); return; } - if (use_json) - json = json_object_new_object(); - args[0] = vty; args[1] = json; zl3vni_print(zl3vni, (void *)args); @@ -2900,12 +2897,18 @@ void zebra_vxlan_print_neigh_vni(struct vty *vty, struct zebra_vrf *zvrf, struct neigh_walk_ctx wctx; json_object *json = NULL; - if (!is_evpn_enabled()) + if (use_json) + json = json_object_new_object(); + + if (!is_evpn_enabled()) { + vty_json(vty, json); return; + } + zevpn = zebra_evpn_lookup(vni); if (!zevpn) { if (use_json) - vty_out(vty, "{}\n"); + vty_json(vty, json); else vty_out(vty, "%% VNI %u does not exist\n", vni); return; @@ -2914,9 +2917,6 @@ void zebra_vxlan_print_neigh_vni(struct vty *vty, struct zebra_vrf *zvrf, if (!num_neigh) return; - if (use_json) - json = json_object_new_object(); - /* Since we have IPv6 addresses to deal with which can vary widely in * size, we try to be a bit more elegant in display by first computing * the maximum width. @@ -2951,12 +2951,14 @@ void zebra_vxlan_print_neigh_all_vni(struct vty *vty, struct zebra_vrf *zvrf, json_object *json = NULL; void *args[3]; - if (!is_evpn_enabled()) - return; - if (use_json) json = json_object_new_object(); + if (!is_evpn_enabled()) { + vty_json(vty, json); + return; + } + args[0] = vty; args[1] = json; args[2] = (void *)(ptrdiff_t)print_dup; @@ -2979,12 +2981,14 @@ void zebra_vxlan_print_neigh_all_vni_detail(struct vty *vty, json_object *json = NULL; void *args[3]; - if (!is_evpn_enabled()) - return; - if (use_json) json = json_object_new_object(); + if (!is_evpn_enabled()) { + vty_json(vty, json); + return; + } + args[0] = vty; args[1] = json; args[2] = (void *)(ptrdiff_t)print_dup; @@ -3008,12 +3012,18 @@ void zebra_vxlan_print_specific_neigh_vni(struct vty *vty, struct zebra_neigh *n; json_object *json = NULL; - if (!is_evpn_enabled()) + if (use_json) + json = json_object_new_object(); + + if (!is_evpn_enabled()) { + vty_json(vty, json); return; + } + zevpn = zebra_evpn_lookup(vni); if (!zevpn) { if (use_json) - vty_out(vty, "{}\n"); + vty_json(vty, json); else vty_out(vty, "%% VNI %u does not exist\n", vni); return; @@ -3026,8 +3036,6 @@ void zebra_vxlan_print_specific_neigh_vni(struct vty *vty, vni); return; } - if (use_json) - json = json_object_new_object(); zebra_evpn_print_neigh(n, vty, json); @@ -3048,12 +3056,18 @@ void zebra_vxlan_print_neigh_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf, struct neigh_walk_ctx wctx; json_object *json = NULL; - if (!is_evpn_enabled()) + if (use_json) + json = json_object_new_object(); + + if (!is_evpn_enabled()) { + vty_json(vty, json); return; + } + zevpn = zebra_evpn_lookup(vni); if (!zevpn) { if (use_json) - vty_out(vty, "{}\n"); + vty_json(vty, json); else vty_out(vty, "%% VNI %u does not exist\n", vni); return; @@ -3062,9 +3076,6 @@ void zebra_vxlan_print_neigh_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf, if (!num_neigh) return; - if (use_json) - json = json_object_new_object(); - memset(&wctx, 0, sizeof(wctx)); wctx.zevpn = zevpn; wctx.vty = vty; @@ -3094,12 +3105,20 @@ void zebra_vxlan_print_neigh_vni_dad(struct vty *vty, struct neigh_walk_ctx wctx; json_object *json = NULL; - if (!is_evpn_enabled()) + if (use_json) + json = json_object_new_object(); + + if (!is_evpn_enabled()) { + vty_json(vty, json); return; + } zevpn = zebra_evpn_lookup(vni); if (!zevpn) { - vty_out(vty, "%% VNI %u does not exist\n", vni); + if (use_json) + vty_json(vty, json); + else + vty_out(vty, "%% VNI %u does not exist\n", vni); return; } @@ -3111,9 +3130,6 @@ void zebra_vxlan_print_neigh_vni_dad(struct vty *vty, if (!num_neigh) return; - if (use_json) - json = json_object_new_object(); - /* Since we have IPv6 addresses to deal with which can vary widely in * size, we try to be a bit more elegant in display by first computing * the maximum width. @@ -3155,8 +3171,12 @@ void zebra_vxlan_print_macs_vni(struct vty *vty, struct zebra_vrf *zvrf, json_object *json = NULL; json_object *json_mac = NULL; - if (!is_evpn_enabled()) + if (!is_evpn_enabled()) { + if (use_json) + vty_out(vty, "{}\n"); return; + } + zevpn = zebra_evpn_lookup(vni); if (!zevpn) { if (use_json) @@ -3218,13 +3238,13 @@ void zebra_vxlan_print_macs_all_vni(struct vty *vty, struct zebra_vrf *zvrf, struct mac_walk_ctx wctx; json_object *json = NULL; + if (use_json) + json = json_object_new_object(); + if (!is_evpn_enabled()) { - if (use_json) - vty_out(vty, "{}\n"); + vty_json(vty, json); return; } - if (use_json) - json = json_object_new_object(); memset(&wctx, 0, sizeof(wctx)); wctx.vty = vty; @@ -3246,13 +3266,13 @@ void zebra_vxlan_print_macs_all_vni_detail(struct vty *vty, struct mac_walk_ctx wctx; json_object *json = NULL; + if (use_json) + json = json_object_new_object(); + if (!is_evpn_enabled()) { - if (use_json) - vty_out(vty, "{}\n"); + vty_json(vty, json); return; } - if (use_json) - json = json_object_new_object(); memset(&wctx, 0, sizeof(wctx)); wctx.vty = vty; @@ -3275,12 +3295,14 @@ void zebra_vxlan_print_macs_all_vni_vtep(struct vty *vty, struct mac_walk_ctx wctx; json_object *json = NULL; - if (!is_evpn_enabled()) - return; - if (use_json) json = json_object_new_object(); + if (!is_evpn_enabled()) { + vty_json(vty, json); + return; + } + memset(&wctx, 0, sizeof(wctx)); wctx.vty = vty; wctx.flags = SHOW_REMOTE_MAC_FROM_VTEP; @@ -3303,13 +3325,18 @@ void zebra_vxlan_print_specific_mac_vni(struct vty *vty, struct zebra_vrf *zvrf, struct zebra_mac *mac; json_object *json = NULL; - if (!is_evpn_enabled()) + if (use_json) + json = json_object_new_object(); + + if (!is_evpn_enabled()) { + vty_json(vty, json); return; + } zevpn = zebra_evpn_lookup(vni); if (!zevpn) { if (use_json) - vty_out(vty, "{}\n"); + vty_json(vty, json); else vty_out(vty, "%% VNI %u does not exist\n", vni); return; @@ -3317,7 +3344,7 @@ void zebra_vxlan_print_specific_mac_vni(struct vty *vty, struct zebra_vrf *zvrf, mac = zebra_evpn_mac_lookup(zevpn, macaddr); if (!mac) { if (use_json) - vty_out(vty, "{}\n"); + vty_json(vty, json); else vty_out(vty, "%% Requested MAC does not exist in VNI %u\n", @@ -3325,10 +3352,8 @@ void zebra_vxlan_print_specific_mac_vni(struct vty *vty, struct zebra_vrf *zvrf, return; } - if (use_json) - json = json_object_new_object(); - zebra_evpn_print_mac(mac, vty, json); + if (use_json) vty_json(vty, json); } @@ -3693,8 +3718,11 @@ void zebra_vxlan_print_macs_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf, json_object *json = NULL; json_object *json_mac = NULL; - if (!is_evpn_enabled()) + if (!is_evpn_enabled()) { + vty_json(vty, json); return; + } + zevpn = zebra_evpn_lookup(vni); if (!zevpn) { if (use_json) @@ -3745,12 +3773,14 @@ void zebra_vxlan_print_vni(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, struct zebra_l3vni *zl3vni = NULL; struct zebra_evpn *zevpn = NULL; - if (!is_evpn_enabled()) - return; - if (use_json) json = json_object_new_object(); + if (!is_evpn_enabled()) { + vty_json(vty, json); + return; + } + args[0] = vty; args[1] = json; @@ -3787,8 +3817,13 @@ void zebra_vxlan_print_evpn(struct vty *vty, bool uj) json_object *json = NULL; struct zebra_vrf *zvrf = NULL; - if (!is_evpn_enabled()) + if (uj) + json = json_object_new_object(); + + if (!is_evpn_enabled()) { + vty_json(vty, json); return; + } zvrf = zebra_vrf_get_evpn(); @@ -3797,7 +3832,6 @@ void zebra_vxlan_print_evpn(struct vty *vty, bool uj) num_vnis = num_l2vnis + num_l3vnis; if (uj) { - json = json_object_new_object(); json_object_string_add(json, "advertiseGatewayMacip", zvrf->advertise_gw_macip ? "Yes" : "No"); json_object_string_add(json, "advertiseSviMacip", @@ -3860,12 +3894,15 @@ void zebra_vxlan_print_vnis(struct vty *vty, struct zebra_vrf *zvrf, json_object *json = NULL; void *args[2]; - if (!is_evpn_enabled()) - return; - if (use_json) json = json_object_new_object(); - else + + if (!is_evpn_enabled()) { + vty_json(vty, json); + return; + } + + if (!use_json) vty_out(vty, "%-10s %-4s %-21s %-8s %-8s %-15s %-37s\n", "VNI", "Type", "VxLAN IF", "# MACs", "# ARPs", "# Remote VTEPs", "Tenant VRF"); @@ -3943,8 +3980,11 @@ void zebra_vxlan_print_vnis_detail(struct vty *vty, struct zebra_vrf *zvrf, struct zebra_ns *zns = NULL; struct zebra_evpn_show zes; - if (!is_evpn_enabled()) + if (!is_evpn_enabled()) { + if (use_json) + vty_out(vty, "{}\n"); return; + } zns = zebra_ns_lookup(NS_DEFAULT); if (!zns) diff --git a/zebra/zserv.c b/zebra/zserv.c index 70707866ee..6abd49310c 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -1152,10 +1152,6 @@ static void zebra_show_stale_client_detail(struct vty *vty, info->t_stale_removal)); } } - vty_out(vty, "Current AFI : %d\n", info->current_afi); - if (info->current_prefix) - vty_out(vty, "Current prefix : %pFX\n", - info->current_prefix); } } vty_out(vty, "\n"); diff --git a/zebra/zserv.h b/zebra/zserv.h index aa58a3a299..90aa4d53f4 100644 --- a/zebra/zserv.h +++ b/zebra/zserv.h @@ -51,9 +51,6 @@ struct client_gr_info { /* VRF for which GR enabled */ vrf_id_t vrf_id; - /* AFI */ - afi_t current_afi; - /* Stale time and GR cap */ uint32_t stale_removal_time; enum zserv_client_capabilities capabilities; @@ -64,11 +61,10 @@ struct client_gr_info { bool stale_client; /* Route sync and enable flags for AFI/SAFI */ - bool af_enabled[AFI_MAX][SAFI_MAX]; - bool route_sync[AFI_MAX][SAFI_MAX]; + bool af_enabled[AFI_MAX]; + bool route_sync[AFI_MAX]; /* Book keeping */ - struct prefix *current_prefix; void *stale_client_ptr; struct event *t_stale_removal; |
