+++ /dev/null
-;;; Directory Local Variables
-;;; For more information see (info "(emacs) Directory Variables")
-;;; Match project coding conventions
-
-((c-mode . ((indent-tabs-mode . t)
- (show-trailing-whitespace . t)
- (c-basic-offset . 8)))
- (json-mode . ((js-indent-level 4))))
--- /dev/null
+[flake8]
+max-line-length = 88
+extend-ignore = E203
\ No newline at end of file
-name: Add a conflict label is PR needs to rebase
+name: Add a conflict label if PR needs to rebase
on:
push:
{arch}
build
.cache
+.dir-locals.el
.msg
.rebase-*
*~
--- /dev/null
+[settings]
+profile = black
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
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"
--enable-vty-group=frrvty \
--enable-user=$_user \
--enable-group=$_user \
- --enable-pcre2posix
+ --enable-pcre2posix \
+ --enable-scripting
make -j $(nproc)
}
#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
}
#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)
[ MESSAGE_MH_REQUEST ] = 14,
};
+/* Checks whether an AE exists or must be silently ignored */
+static bool
+known_ae(int ae)
+{
+ return ae <= 4;
+}
+
/* Parse a network prefix, encoded in the somewhat baroque compressed
representation used by Babel. Return the number of bytes parsed. */
static int
return ret;
}
+static int
+parse_request_subtlv(int ae, const unsigned char *a, int alen,
+ unsigned char *src_prefix, unsigned char *src_plen)
+{
+ int type, len, i = 0;
+ int have_src_prefix = 0;
+
+ while(i < alen) {
+ type = a[0];
+ if(type == SUBTLV_PAD1) {
+ i++;
+ continue;
+ }
+
+ if(i + 2 > alen)
+ goto fail;
+
+ len = a[i + 1];
+ if(i + 2 + len > alen)
+ goto fail;
+
+ if(type == SUBTLV_PADN) {
+ /* Nothing to do. */
+ } else if(type == SUBTLV_SOURCE_PREFIX) {
+ int rc;
+ if(len < 1)
+ goto fail;
+ if(a[i + 2] == 0)
+ goto fail;
+ if(have_src_prefix != 0)
+ goto fail;
+ rc = network_prefix(ae, a[i + 2], 0, a + i + 3, NULL,
+ len - 1, src_prefix);
+ if(rc < 0)
+ goto fail;
+ if(ae==1)
+ *src_plen = a[i + 2] + 96;
+ else
+ *src_plen = a[i + 2];
+ have_src_prefix = 1;
+ } else {
+ debugf(BABEL_DEBUG_COMMON,"Received unknown%s Route Request sub-TLV %d.",
+ ((type & 0x80) != 0) ? " mandatory" : "", type);
+ if((type & 0x80) != 0)
+ return -1;
+ }
+
+ i += len + 2;
+ }
+ return 1;
+
+ fail:
+ flog_err(EC_BABEL_PACKET, "Received truncated sub-TLV on Route Request.");
+ return -1;
+}
+
static int
network_address(int ae, const unsigned char *a, unsigned int len,
unsigned char *a_r)
#define BABEL_UNICAST_HELLO 0x8000
DO_NTOHS(flags, message + 2);
- /*
- * RFC 8966 4.6.5
- * All other bits MUST be sent as a 0 and silently
- * ignored on reception
- */
- if (CHECK_FLAG(flags, ~BABEL_UNICAST_HELLO)) {
- debugf(BABEL_DEBUG_COMMON,
- "Received Hello from %s on %s that does not have all 0's in the unused section of flags, ignoring",
- format_address(from), ifp->name);
- goto done;
- }
-
/*
* RFC 8966 Appendix F
* TL;DR -> Please ignore Unicast hellos until FRR's
interval, neigh, nh, channels,
channels_len(channels));
} else if(type == MESSAGE_REQUEST) {
- unsigned char prefix[16], plen;
- int rc;
+ unsigned char prefix[16], src_prefix[16], plen, src_plen;
+ int rc, is_ss;
+ if(len < 2) goto fail;
+ if(!known_ae(message[2])) {
+ debugf(BABEL_DEBUG_COMMON,"Received request with unknown AE %d. Ignoring.",
+ message[2]);
+ goto done;
+ }
rc = network_prefix(message[2], message[3], 0,
message + 4, NULL, len - 2, prefix);
if(rc < 0) goto fail;
debugf(BABEL_DEBUG_COMMON,"Received request for %s from %s on %s.",
message[2] == 0 ? "any" : format_prefix(prefix, plen),
format_address(from), ifp->name);
+ if(message[2] == 1) {
+ v4tov6(src_prefix, zeroes);
+ src_plen = 96;
+ } else {
+ memcpy(src_prefix, zeroes, 16);
+ src_plen = 0;
+ }
+ rc = parse_request_subtlv(message[2], message + 4 + rc,
+ len - 2 - rc, src_prefix, &src_plen);
+ if(rc < 0)
+ goto done;
+ is_ss = !is_default(src_prefix, src_plen);
if(message[2] == 0) {
struct babel_interface *neigh_ifp =babel_get_if_nfo(neigh->ifp);
+ if(is_ss) {
+ /* Wildcard requests don't carry a source prefix. */
+ flog_err(EC_BABEL_PACKET,
+ "Received source-specific wildcard request.");
+ goto done;
+ }
/* If a neighbour is requesting a full route dump from us,
we might as well send it an IHU. */
send_ihu(neigh, NULL);
#define SUBTLV_PADN 1
#define SUBTLV_DIVERSITY 2 /* Also known as babelz. */
#define SUBTLV_TIMESTAMP 3 /* Used to compute RTT. */
+#define SUBTLV_SOURCE_PREFIX 128 /* Source-specific routing. */
#define SUBTLV_MANDATORY 0x80
extern unsigned short myseqno;
int daemonise(void);
extern const unsigned char v4prefix[16];
+static inline bool
+is_default(const unsigned char *prefix, int plen)
+{
+ return plen == 0 || (plen == 96 && v4mapped(prefix));
+}
+
/* If debugging is disabled, we want to avoid calling format_address
for every omitted debugging message. So debug is a macro. But
vararg macros are not portable. */
#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 */
int srgb_count;
uint8_t sid_type, sid_flags;
+ /*
+ * Check that we actually have at least as much data as
+ * specified by the length field
+ */
+ if (STREAM_READABLE(peer->curr) < length) {
+ flog_err(
+ EC_BGP_ATTR_LEN,
+ "Prefix SID specifies length %hu, but only %zu bytes remain",
+ length, STREAM_READABLE(peer->curr));
+ return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ args->total);
+ }
+
if (type == BGP_PREFIX_SID_LABEL_INDEX) {
- if (STREAM_READABLE(peer->curr) < length
- || length != BGP_PREFIX_SID_LABEL_INDEX_LENGTH) {
+ if (length != BGP_PREFIX_SID_LABEL_INDEX_LENGTH) {
flog_err(EC_BGP_ATTR_LEN,
"Prefix SID label index length is %hu instead of %u",
length, BGP_PREFIX_SID_LABEL_INDEX_LENGTH);
/* Store label index; subsequently, we'll check on
* address-family */
attr->label_index = label_index;
- }
-
- /* Placeholder code for the IPv6 SID type */
- else if (type == BGP_PREFIX_SID_IPV6) {
- if (STREAM_READABLE(peer->curr) < length
- || length != BGP_PREFIX_SID_IPV6_LENGTH) {
+ } else if (type == BGP_PREFIX_SID_IPV6) {
+ if (length != BGP_PREFIX_SID_IPV6_LENGTH) {
flog_err(EC_BGP_ATTR_LEN,
"Prefix SID IPv6 length is %hu instead of %u",
length, BGP_PREFIX_SID_IPV6_LENGTH);
stream_getw(peer->curr);
stream_get(&ipv6_sid, peer->curr, 16);
- }
-
- /* Placeholder code for the Originator SRGB type */
- else if (type == BGP_PREFIX_SID_ORIGINATOR_SRGB) {
+ } else if (type == BGP_PREFIX_SID_ORIGINATOR_SRGB) {
/*
* ietf-idr-bgp-prefix-sid-05:
* Length is the total length of the value portion of the
args->total);
}
- /*
- * Check that we actually have at least as much data as
- * specified by the length field
- */
- if (STREAM_READABLE(peer->curr) < length) {
- flog_err(EC_BGP_ATTR_LEN,
- "Prefix SID Originator SRGB specifies length %hu, but only %zu bytes remain",
- length, STREAM_READABLE(peer->curr));
- return bgp_attr_malformed(
- args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
- args->total);
- }
-
/*
* Check that the portion of the TLV containing the sequence of
* SRGBs corresponds to a multiple of the SRGB size; to get
stream_get(&srgb_base, peer->curr, 3);
stream_get(&srgb_range, peer->curr, 3);
}
- }
-
- /* Placeholder code for the VPN-SID Service type */
- else if (type == BGP_PREFIX_SID_VPN_SID) {
- if (STREAM_READABLE(peer->curr) < length
- || length != BGP_PREFIX_SID_VPN_SID_LENGTH) {
+ } else if (type == BGP_PREFIX_SID_VPN_SID) {
+ if (length != BGP_PREFIX_SID_VPN_SID_LENGTH) {
flog_err(EC_BGP_ATTR_LEN,
"Prefix SID VPN SID length is %hu instead of %u",
length, BGP_PREFIX_SID_VPN_SID_LENGTH);
attr->srv6_vpn->sid_flags = sid_flags;
sid_copy(&attr->srv6_vpn->sid, &ipv6_sid);
attr->srv6_vpn = srv6_vpn_intern(attr->srv6_vpn);
- }
-
- /* Placeholder code for the SRv6 L3 Service type */
- else if (type == BGP_PREFIX_SID_SRV6_L3_SERVICE) {
- if (STREAM_READABLE(peer->curr) < length) {
+ } else if (type == BGP_PREFIX_SID_SRV6_L3_SERVICE) {
+ if (STREAM_READABLE(peer->curr) < 1) {
flog_err(
EC_BGP_ATTR_LEN,
- "Prefix SID SRv6 L3-Service length is %hu, but only %zu bytes remain",
- length, STREAM_READABLE(peer->curr));
- return bgp_attr_malformed(args,
- BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
- args->total);
+ "Prefix SID SRV6 L3 Service not enough data left, it must be at least 1 byte");
+ return bgp_attr_malformed(
+ args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ args->total);
}
-
/* ignore reserved */
stream_getc(peer->curr);
return bgp_attr_srv6_service(args);
}
-
/* Placeholder code for Unsupported TLV */
else {
-
- if (STREAM_READABLE(peer->curr) < length) {
- flog_err(
- EC_BGP_ATTR_LEN,
- "Prefix SID SRv6 length is %hu - too long, only %zu remaining in this UPDATE",
- length, STREAM_READABLE(peer->curr));
- return bgp_attr_malformed(
- args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
- args->total);
- }
-
if (bgp_debug_update(peer, NULL, NULL, 1))
zlog_debug(
"%s attr Prefix-SID sub-type=%u is not supported, skipped",
}
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);
/* 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,
memset(&bgp_dump_routes, 0, sizeof(bgp_dump_routes));
bgp_dump_obuf =
- stream_new((BGP_STANDARD_MESSAGE_MAX_PACKET_SIZE * 2)
- + BGP_DUMP_MSG_HEADER + BGP_DUMP_HEADER_SIZE);
+ stream_new(BGP_MAX_PACKET_SIZE + BGP_MAX_PACKET_SIZE_OVERFLOW);
install_node(&bgp_dump_node);
enum ecommunity_token {
ecommunity_token_unknown = 0,
ecommunity_token_rt,
+ ecommunity_token_nt,
ecommunity_token_soo,
ecommunity_token_val,
ecommunity_token_rt6,
(void)ptr; /* consume value */
}
+bool ecommunity_node_target_match(struct ecommunity *ecom,
+ struct in_addr *local_id)
+{
+ uint32_t i;
+ bool match = false;
+
+ if (!ecom || !ecom->size)
+ return NULL;
+
+ for (i = 0; i < ecom->size; i++) {
+ const uint8_t *pnt;
+ uint8_t type, sub_type;
+
+ pnt = (ecom->val + (i * ECOMMUNITY_SIZE));
+ type = *pnt++;
+ sub_type = *pnt++;
+
+ if (type == ECOMMUNITY_ENCODE_IP &&
+ sub_type == ECOMMUNITY_NODE_TARGET) {
+ /* Node Target ID is encoded as A.B.C.D:0 */
+ if (IPV4_ADDR_SAME((struct in_addr *)pnt, local_id))
+ match = true;
+ (void)pnt;
+ }
+ }
+
+ return match;
+}
+
+static void ecommunity_node_target_str(char *buf, size_t bufsz, uint8_t *ptr)
+{
+ /*
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x01 or 0x41 | Sub-Type(0x09) | Target BGP Identifier |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Target BGP Identifier (cont.) | Reserved |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ struct in_addr node_id = {};
+
+ IPV4_ADDR_COPY(&node_id, (struct in_addr *)ptr);
+
+ snprintfrr(buf, bufsz, "NT:%pI4", &node_id);
+
+ (void)ptr; /* consume value */
+}
+
static int ecommunity_encode_internal(uint8_t type, uint8_t sub_type,
int trans, as_t as,
struct in_addr *ip,
eval->val[0] |= ECOMMUNITY_FLAG_NON_TRANSITIVE;
eval->val[1] = sub_type;
if (type == ECOMMUNITY_ENCODE_AS) {
- eval->val[2] = (as >> 8) & 0xff;
- eval->val[3] = as & 0xff;
- eval->val[4] = (val >> 24) & 0xff;
- eval->val[5] = (val >> 16) & 0xff;
- eval->val[6] = (val >> 8) & 0xff;
- eval->val[7] = val & 0xff;
+ encode_route_target_as(as, val, eval, trans);
} else if (type == ECOMMUNITY_ENCODE_IP) {
- memcpy(&eval->val[2], ip, sizeof(struct in_addr));
- eval->val[6] = (val >> 8) & 0xff;
- eval->val[7] = val & 0xff;
+ if (sub_type == ECOMMUNITY_NODE_TARGET)
+ encode_node_target(ip, eval, trans);
+ else
+ encode_route_target_ip(ip, val, eval, trans);
} else if (type == ECOMMUNITY_ENCODE_TRANS_EXP &&
sub_type == ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6) {
memcpy(&eval6->val[2], ip6, sizeof(struct in6_addr));
eval6->val[18] = (val >> 8) & 0xff;
eval6->val[19] = val & 0xff;
} else {
- eval->val[2] = (as >> 24) & 0xff;
- eval->val[3] = (as >> 16) & 0xff;
- eval->val[4] = (as >> 8) & 0xff;
- eval->val[5] = as & 0xff;
- eval->val[6] = (val >> 8) & 0xff;
- eval->val[7] = val & 0xff;
+ encode_route_target_as4(as, val, eval, trans);
}
return 0;
}
/* Get next Extended Communities token from the string. */
-static const char *ecommunity_gettoken(const char *str,
- void *eval_ptr,
- enum ecommunity_token *token)
+static const char *ecommunity_gettoken(const char *str, void *eval_ptr,
+ enum ecommunity_token *token, int type)
{
int ret;
int dot = 0;
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++;
if (*p == '\0')
return NULL;
- /* "rt" and "soo" keyword parse. */
+ /* "rt", "nt", and "soo" keyword parse. */
if (!isdigit((unsigned char)*p)) {
/* "rt" match check. */
if (tolower((unsigned char)*p) == 'r') {
}
goto error;
}
+ /* "nt" match check. */
+ if (tolower((unsigned char)*p) == 'n') {
+ p++;
+ if (tolower((unsigned char)*p) == 't') {
+ p++;
+ *token = ecommunity_token_nt;
+ return p;
+ }
+ if (isspace((unsigned char)*p) || *p == '\0') {
+ *token = ecommunity_token_nt;
+ return p;
+ }
+ goto error;
+ }
/* "soo" match check. */
else if (tolower((unsigned char)*p) == 's') {
p++;
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';
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)
ecomm_type = ECOMMUNITY_ENCODE_AS4;
else
ecomm_type = ECOMMUNITY_ENCODE_AS;
- if (ecommunity_encode(ecomm_type, 0, 1, as, ip, val, eval))
+ if (ecommunity_encode(ecomm_type, type, 1, as, ip, val, eval))
goto error;
*token = ecommunity_token_val;
return p;
if (is_ipv6_extcomm)
token = ecommunity_token_rt6;
- while ((str = ecommunity_gettoken(str, (void *)&eval, &token))) {
+ while ((str = ecommunity_gettoken(str, (void *)&eval, &token, type))) {
switch (token) {
case ecommunity_token_rt:
+ case ecommunity_token_nt:
case ecommunity_token_rt6:
case ecommunity_token_soo:
if (!keyword_included || keyword) {
if (token == ecommunity_token_soo) {
type = ECOMMUNITY_SITE_ORIGIN;
}
+ if (token == ecommunity_token_nt) {
+ type = ECOMMUNITY_NODE_TARGET;
+ }
break;
case ecommunity_token_val:
if (keyword_included) {
ecommunity_lb_str(
encbuf, sizeof(encbuf), pnt,
ecom->disable_ieee_floating);
+ } else if (sub_type == ECOMMUNITY_NODE_TARGET &&
+ type == ECOMMUNITY_ENCODE_IP) {
+ ecommunity_node_target_str(
+ encbuf, sizeof(encbuf), pnt);
} else
unk_ecom = 1;
} else {
ecom->disable_ieee_floating);
else
unk_ecom = 1;
+ } else if (type == ECOMMUNITY_ENCODE_IP_NON_TRANS) {
+ sub_type = *pnt++;
+ if (sub_type == ECOMMUNITY_NODE_TARGET)
+ ecommunity_node_target_str(encbuf,
+ sizeof(encbuf), pnt);
+ else
+ unk_ecom = 1;
} else if (type == ECOMMUNITY_ENCODE_OPAQUE_NON_TRANS) {
sub_type = *pnt++;
if (sub_type == ECOMMUNITY_ORIGIN_VALIDATION_STATE)
/* Extended Community readable string length */
#define ECOMMUNITY_STRLEN 64
+/* Node Target Extended Communities */
+#define ECOMMUNITY_NODE_TARGET 0x09
+#define ECOMMUNITY_NODE_TARGET_RESERVED 0
+
/* Extended Communities attribute. */
struct ecommunity {
/* Reference counter. */
* Encode BGP Route Target AS:nn.
*/
static inline void encode_route_target_as(as_t as, uint32_t val,
- struct ecommunity_val *eval)
+ struct ecommunity_val *eval,
+ bool trans)
{
eval->val[0] = ECOMMUNITY_ENCODE_AS;
+ if (!trans)
+ eval->val[0] |= ECOMMUNITY_FLAG_NON_TRANSITIVE;
eval->val[1] = ECOMMUNITY_ROUTE_TARGET;
eval->val[2] = (as >> 8) & 0xff;
eval->val[3] = as & 0xff;
/*
* Encode BGP Route Target IP:nn.
*/
-static inline void encode_route_target_ip(struct in_addr ip, uint16_t val,
- struct ecommunity_val *eval)
+static inline void encode_route_target_ip(struct in_addr *ip, uint16_t val,
+ struct ecommunity_val *eval,
+ bool trans)
{
eval->val[0] = ECOMMUNITY_ENCODE_IP;
+ if (!trans)
+ eval->val[0] |= ECOMMUNITY_FLAG_NON_TRANSITIVE;
eval->val[1] = ECOMMUNITY_ROUTE_TARGET;
- memcpy(&eval->val[2], &ip, sizeof(struct in_addr));
+ memcpy(&eval->val[2], ip, sizeof(struct in_addr));
eval->val[6] = (val >> 8) & 0xff;
eval->val[7] = val & 0xff;
}
* Encode BGP Route Target AS4:nn.
*/
static inline void encode_route_target_as4(as_t as, uint16_t val,
- struct ecommunity_val *eval)
+ struct ecommunity_val *eval,
+ bool trans)
{
eval->val[0] = ECOMMUNITY_ENCODE_AS4;
+ if (!trans)
+ eval->val[0] |= ECOMMUNITY_FLAG_NON_TRANSITIVE;
eval->val[1] = ECOMMUNITY_ROUTE_TARGET;
eval->val[2] = (as >> 24) & 0xff;
eval->val[3] = (as >> 16) & 0xff;
eval->val[7] = ovs_state;
}
+static inline void encode_node_target(struct in_addr *node_id,
+ struct ecommunity_val *eval, bool trans)
+{
+ /*
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x01 or 0x41 | Sub-Type(0x09) | Target BGP Identifier |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Target BGP Identifier (cont.) | Reserved |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ memset(eval, 0, sizeof(*eval));
+ eval->val[0] = ECOMMUNITY_ENCODE_IP;
+ if (!trans)
+ eval->val[0] |= ECOMMUNITY_ENCODE_IP_NON_TRANS;
+ eval->val[1] = ECOMMUNITY_NODE_TARGET;
+ memcpy(&eval->val[2], node_id, sizeof(*node_id));
+ eval->val[6] = ECOMMUNITY_NODE_TARGET_RESERVED;
+ eval->val[7] = ECOMMUNITY_NODE_TARGET_RESERVED;
+}
+
extern void ecommunity_init(void);
extern void ecommunity_finish(void);
extern void ecommunity_free(struct ecommunity **);
extern struct ecommunity *
ecommunity_add_origin_validation_state(enum rpki_states rpki_state,
struct ecommunity *ecom);
+extern struct ecommunity *ecommunity_add_node_target(struct in_addr *node_id,
+ struct ecommunity *old,
+ bool non_trans);
+extern bool ecommunity_node_target_match(struct ecommunity *ecomm,
+ struct in_addr *local_id);
#endif /* _QUAGGA_BGP_ECOMMUNITY_H */
if (bgp->advertise_autort_rfc8365)
vni |= EVPN_AUTORT_VXLAN;
- encode_route_target_as((bgp->as & 0xFFFF), vni, &eval);
+ encode_route_target_as((bgp->as & 0xFFFF), vni, &eval, true);
ecomadd = ecommunity_new();
ecommunity_add_val(ecomadd, &eval, false, false);
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.
if (bgp->advertise_autort_rfc8365)
vni |= EVPN_AUTORT_VXLAN;
- encode_route_target_as((bgp->as & 0xFFFF), vni, &eval);
+ encode_route_target_as((bgp->as & 0xFFFF), vni, &eval, true);
ecom_auto = ecommunity_new();
ecommunity_add_val(ecom_auto, &eval, false, false);
}
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;
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 */
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
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);
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)) {
{
json_object *json_vtep_entry;
json_object *json_flags;
+ char alg_buf[EVPN_DF_ALG_STR_LEN];
json_vtep_entry = json_object_new_object();
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)));
}
}
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,
struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp); /* bgp vrf instance */
struct bgp *bgp_evpn = NULL;
- if (EVPN_ENABLED(bgp_vrf)) {
+ if (!bgp_vrf || EVPN_ENABLED(bgp_vrf)) {
vty_out(vty,
"This command is supported under L3VNI BGP EVPN VRF\n");
return CMD_WARNING_CONFIG_FAILED;
}
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;
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)",
#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);
offset++;
}
/* Prefix length check. */
- if (prefix_local.prefixlen > prefix_blen(&prefix_local) * 8)
+ if (prefix_local.prefixlen > prefix_blen(&prefix_local) * 8) {
*error = -1;
+ return offset;
+ }
/* When packet overflow occur return immediately. */
- if (psize + offset > max_len)
+ if (psize + offset > max_len) {
*error = -1;
+ return offset;
+ }
/* Defensive coding, double-check
* the psize fits in a struct prefix
*/
- if (psize > (ssize_t)sizeof(prefix_local.u))
+ if (psize > (ssize_t)sizeof(prefix_local.u)) {
*error = -1;
+ return offset;
+ }
+
memcpy(&prefix_local.u.prefix, &nlri_ptr[offset], psize);
offset += psize;
switch (type) {
*error = 0;
do {
- if (loop > BGP_PBR_MATCH_VAL_MAX)
+ if (loop > BGP_PBR_MATCH_VAL_MAX) {
*error = -2;
+ return offset;
+ }
hex2bin(&nlri_ptr[offset], op);
/* if first element, AND bit can not be set */
if (op[1] == 1 && loop == 0)
}
/* 1 space + lcom->size lcom strings + null terminator */
- size_t str_buf_sz = BUFSIZ;
+ size_t str_buf_sz = (LCOMMUNITY_STRLEN * lcom->size) + 2;
str_buf = XCALLOC(MTYPE_LCOMMUNITY_STR, str_buf_sz);
+ len = 0;
for (i = 0; i < lcom->size; i++) {
if (i > 0)
- strlcat(str_buf, " ", str_buf_sz);
+ len = strlcat(str_buf, " ", str_buf_sz);
pnt = lcom->val + (i * LCOMMUNITY_SIZE);
pnt = ptr_get_be32(pnt, &global);
snprintf(lcsb, sizeof(lcsb), "%u:%u:%u", global, local1,
local2);
+ /*
+ * Aliases can cause havoc, if the alias length is greater
+ * than the LCOMMUNITY_STRLEN for a particular item
+ * then we need to realloc the memory associated
+ * with the string so that it can fit
+ */
const char *com2alias =
translate_alias ? bgp_community2alias(lcsb) : lcsb;
+ size_t individual_len = strlen(com2alias);
+ if (individual_len + len > str_buf_sz) {
+ str_buf_sz = individual_len + len + 1;
+ str_buf = XREALLOC(MTYPE_LCOMMUNITY_STR, str_buf,
+ str_buf_sz);
+ }
len = strlcat(str_buf, com2alias, str_buf_sz);
- assert((unsigned int)len < str_buf_sz);
if (make_json) {
json_string = json_object_new_string(com2alias);
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;
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)
}
} else
return SNMP_INTEGER(MPLSL3VPNVRFRTECIDRTYPEOTHER);
+ break;
case MPLSL3VPNVRFRTEINETCIDRPROTO:
switch (pi->type) {
case ZEBRA_ROUTE_CONNECT:
}
switch (nexthop->type) {
case NEXTHOP_TYPE_IPV6:
- vty_out(vty, " gate %pI6\n", &nexthop->gate.ipv6);
- break;
case NEXTHOP_TYPE_IPV6_IFINDEX:
- vty_out(vty, " gate %pI6, if %s\n",
- &nexthop->gate.ipv6,
- ifindex2ifname(bnc->ifindex ? bnc->ifindex
- : nexthop->ifindex,
- bgp->vrf_id));
+ vty_out(vty, " gate %pI6", &nexthop->gate.ipv6);
+ if (nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX &&
+ bnc->ifindex)
+ vty_out(vty, ", if %s\n",
+ ifindex2ifname(bnc->ifindex,
+ bgp->vrf_id));
+ else if (nexthop->ifindex)
+ vty_out(vty, ", if %s\n",
+ ifindex2ifname(nexthop->ifindex,
+ bgp->vrf_id));
+ else
+ vty_out(vty, "\n");
break;
case NEXTHOP_TYPE_IPV4:
- vty_out(vty, " gate %pI4\n", &nexthop->gate.ipv4);
+ case NEXTHOP_TYPE_IPV4_IFINDEX:
+ vty_out(vty, " gate %pI4", &nexthop->gate.ipv4);
+ if (nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX &&
+ bnc->ifindex)
+ vty_out(vty, ", if %s\n",
+ ifindex2ifname(bnc->ifindex,
+ bgp->vrf_id));
+ else if (nexthop->ifindex)
+ vty_out(vty, ", if %s\n",
+ ifindex2ifname(nexthop->ifindex,
+ bgp->vrf_id));
+ else
+ vty_out(vty, "\n");
break;
case NEXTHOP_TYPE_IFINDEX:
vty_out(vty, " if %s\n",
: nexthop->ifindex,
bgp->vrf_id));
break;
- case NEXTHOP_TYPE_IPV4_IFINDEX:
- vty_out(vty, " gate %pI4, if %s\n",
- &nexthop->gate.ipv4,
- ifindex2ifname(bnc->ifindex ? bnc->ifindex
- : nexthop->ifindex,
- bgp->vrf_id));
- break;
case NEXTHOP_TYPE_BLACKHOLE:
vty_out(vty, " blackhole\n");
break;
* 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:
} 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);
* 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.
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;
+ /* Disable PHP : explicit-null */
+ if (!!CHECK_FLAG(bgp->flags, BGP_FLAG_LU_IPV4_EXPLICIT_NULL) &&
+ afi == AFI_IP)
+ *label = MPLS_LABEL_IPV4_EXPLICIT_NULL;
+ else if (!!CHECK_FLAG(bgp->flags, BGP_FLAG_LU_IPV6_EXPLICIT_NULL) &&
+ afi == AFI_IP6)
+ *label = MPLS_LABEL_IPV6_EXPLICIT_NULL;
else
- return false;
+ /* Enforced PHP popping: implicit-null */
+ *label = MPLS_LABEL_IMPLICIT_NULL;
+
+ return true;
}
/*
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)
* 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]) {
|| 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)
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,
(type == ZEBRA_ROUTE_BGP && stype == BGP_ROUTE_STATIC) ? true
: false;
+ /* If `bgp allow-martian-nexthop` is turned on, return next-hop
+ * as good.
+ */
+ if (bgp->allow_martian)
+ return false;
+
/*
* Only validated for unicast and multicast currently.
* Also valid for EVPN where the nexthop is an IP address.
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))
goto filtered;
}
+ /* If the route has Node Target Extended Communities, check
+ * if it's allowed to be installed locally.
+ */
+ if ((attr->flag & ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES))) {
+ struct ecommunity *ecomm = bgp_attr_get_ecommunity(attr);
+
+ if (ecommunity_lookup(ecomm, ECOMMUNITY_ENCODE_IP,
+ ECOMMUNITY_NODE_TARGET) &&
+ !ecommunity_node_target_match(ecomm, &peer->local_id)) {
+ reason =
+ "Node-Target Extended Communities do not contain own BGP Identifier;";
+ goto filtered;
+ }
+ }
+
/* RFC 8212 to prevent route leaks.
* This specification intends to improve this situation by requiring the
* explicit configuration of both BGP Import and Export Policies for any
}
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
bgp_static->label = label;
bgp_static->prd = prd;
- if (rd_str)
- bgp_static->prd_pretty = XSTRDUP(MTYPE_BGP, rd_str);
+ bgp_static->prd_pretty = XSTRDUP(MTYPE_BGP, rd_str);
+
if (rmap_str) {
XFREE(MTYPE_ROUTE_MAP_NAME, bgp_static->rmap.name);
route_map_counter_decrement(bgp_static->rmap.map);
/* Unlock aggregate address configuration. */
bgp_dest_set_bgp_aggregate_info(dest, NULL);
- if (aggregate->community)
- community_free(&aggregate->community);
-
- hash_clean_and_free(&aggregate->community_hash,
- bgp_aggr_community_remove);
-
- if (aggregate->ecommunity)
- ecommunity_free(&aggregate->ecommunity);
-
- hash_clean_and_free(&aggregate->ecommunity_hash,
- bgp_aggr_ecommunity_remove);
-
- if (aggregate->lcommunity)
- lcommunity_free(&aggregate->lcommunity);
-
- hash_clean_and_free(&aggregate->lcommunity_hash,
- bgp_aggr_lcommunity_remove);
-
- if (aggregate->aspath)
- aspath_free(aggregate->aspath);
-
- hash_clean_and_free(&aggregate->aspath_hash, bgp_aggr_aspath_remove);
-
- bgp_aggregate_free(aggregate);
+ bgp_free_aggregate_info(aggregate);
bgp_dest_unlock_node(dest);
bgp_dest_unlock_node(dest);
match_med != NULL, suppress_map);
}
+void bgp_free_aggregate_info(struct bgp_aggregate *aggregate)
+{
+ if (aggregate->community)
+ community_free(&aggregate->community);
+
+ hash_clean_and_free(&aggregate->community_hash,
+ bgp_aggr_community_remove);
+
+ if (aggregate->ecommunity)
+ ecommunity_free(&aggregate->ecommunity);
+
+ hash_clean_and_free(&aggregate->ecommunity_hash,
+ bgp_aggr_ecommunity_remove);
+
+ if (aggregate->lcommunity)
+ lcommunity_free(&aggregate->lcommunity);
+
+ hash_clean_and_free(&aggregate->lcommunity_hash,
+ bgp_aggr_lcommunity_remove);
+
+ if (aggregate->aspath)
+ aspath_free(aggregate->aspath);
+
+ hash_clean_and_free(&aggregate->aspath_hash, bgp_aggr_aspath_remove);
+
+ bgp_aggregate_free(aggregate);
+}
+
DEFPY(aggregate_addressv6, aggregate_addressv6_cmd,
"[no] aggregate-address X:X::X:X/M$prefix [{"
"as-set$as_set_s"
vty_out(vty, ",\"%pFX\": ", dest_p);
}
+ /* This is used for 'json detail' vty keywords.
+ *
+ * In plain 'json' the per-prefix header is encoded
+ * as a standalone dictionary in the first json_paths
+ * array element:
+ * "<prefix>": [{header}, {path-1}, {path-N}]
+ * (which is confusing and borderline broken)
+ *
+ * For 'json detail' this changes the value
+ * of each prefix-key to be a dictionary where each
+ * header item has its own key, and json_paths is
+ * tucked under the "paths" key:
+ * "<prefix>": {
+ * "<header-key-1>": <header-val-1>,
+ * "<header-key-N>": <header-val-N>,
+ * "paths": [{path-1}, {path-N}]
+ * }
+ */
if (json_detail_header && json_paths != NULL) {
const struct prefix_rd *prd;
+ /* Start per-prefix dictionary */
vty_out(vty, "{\n");
prd = bgp_rd_from_dest(dest, safi);
*/
vty_json_no_pretty(vty, json_paths);
+ /* End per-prefix dictionary */
if (json_detail_header_used)
vty_out(vty, "} ");
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 {
for (ain = dest->adj_in; ain; ain = ain->next) {
if (ain->peer != peer)
continue;
-
show_adj_route_header(vty, peer, table, header1,
header2, json, json_scode,
json_ocode, wide, detail);
if (use_json)
json_net =
json_object_new_object();
+
+ struct bgp_path_info bpi;
+ struct bgp_dest buildit = *dest;
+ struct bgp_dest *pass_in;
+
+ if (route_filtered ||
+ ret == RMAP_DENY) {
+ bpi.attr = &attr;
+ bpi.peer = peer;
+ buildit.info = &bpi;
+
+ pass_in = &buildit;
+ } else
+ pass_in = dest;
bgp_show_path_info(
- NULL /* prefix_rd */, dest, vty,
- bgp, afi, safi, json_net,
+ NULL, pass_in, vty, bgp, afi,
+ safi, json_net,
BGP_PATH_SHOW_ALL, &display,
RPKI_NOT_BEING_USED);
if (use_json)
extern void bgp_route_init(void);
extern void bgp_route_finish(void);
extern void bgp_cleanup_routes(struct bgp *);
+extern void bgp_free_aggregate_info(struct bgp_aggregate *aggregate);
extern void bgp_announce_route(struct peer *peer, afi_t afi, safi_t safi,
bool force);
extern void bgp_stop_announce_route_timer(struct peer_af *paf);
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);
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);
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);
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);
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);
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);
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);
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);
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);
route_set_ecommunity_free,
};
+static void *route_set_ecommunity_nt_compile(const char *arg)
+{
+ struct rmap_ecom_set *rcs;
+ struct ecommunity *ecom;
+
+ ecom = ecommunity_str2com(arg, ECOMMUNITY_NODE_TARGET, 0);
+ if (!ecom)
+ return NULL;
+
+ rcs = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(struct rmap_ecom_set));
+ rcs->ecom = ecommunity_intern(ecom);
+ rcs->none = false;
+
+ return rcs;
+}
+
+static const struct route_map_rule_cmd route_set_ecommunity_nt_cmd = {
+ "extcommunity nt",
+ route_set_ecommunity,
+ route_set_ecommunity_nt_compile,
+ route_set_ecommunity_free,
+};
+
/* `set extcommunity bandwidth' */
struct rmap_ecomm_lb_set {
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);
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);
"BGP extended community attribute\n"
"Link bandwidth extended community\n")
+DEFPY_YANG (set_ecommunity_nt,
+ set_ecommunity_nt_cmd,
+ "set extcommunity nt RTLIST...",
+ SET_STR
+ "BGP extended community attribute\n"
+ "Node Target extended community\n"
+ "Node Target ID\n")
+{
+ int idx_nt = 3;
+ char *str;
+ int ret;
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:set-extcommunity-nt']";
+ char xpath_value[XPATH_MAXLEN];
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-set-action/frr-bgp-route-map:extcommunity-nt", xpath);
+ str = argv_concat(argv, argc, idx_nt);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, str);
+ ret = nb_cli_apply_changes(vty, NULL);
+ XFREE(MTYPE_TMP, str);
+ return ret;
+}
+
+DEFPY_YANG (no_set_ecommunity_nt,
+ no_set_ecommunity_nt_cmd,
+ "no set extcommunity nt RTLIST...",
+ NO_STR
+ SET_STR
+ "BGP extended community attribute\n"
+ "Node Target extended community\n"
+ "Node Target ID\n")
+{
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:set-extcommunity-nt']";
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+ALIAS_YANG (no_set_ecommunity_nt,
+ no_set_ecommunity_nt_short_cmd,
+ "no set extcommunity nt",
+ NO_STR
+ SET_STR
+ "BGP extended community attribute\n"
+ "Node Target extended community\n")
+
DEFUN_YANG (set_origin,
set_origin_cmd,
"set origin <egp|igp|incomplete>",
route_map_install_set(&route_set_vpnv6_nexthop_cmd);
route_map_install_set(&route_set_originator_id_cmd);
route_map_install_set(&route_set_ecommunity_rt_cmd);
+ route_map_install_set(&route_set_ecommunity_nt_cmd);
route_map_install_set(&route_set_ecommunity_soo_cmd);
route_map_install_set(&route_set_ecommunity_lb_cmd);
route_map_install_set(&route_set_ecommunity_none_cmd);
install_element(RMAP_NODE, &no_set_ecommunity_lb_short_cmd);
install_element(RMAP_NODE, &set_ecommunity_none_cmd);
install_element(RMAP_NODE, &no_set_ecommunity_none_cmd);
+ install_element(RMAP_NODE, &set_ecommunity_nt_cmd);
+ install_element(RMAP_NODE, &no_set_ecommunity_nt_cmd);
+ install_element(RMAP_NODE, &no_set_ecommunity_nt_short_cmd);
#ifdef KEEP_OLD_VPN_COMMANDS
install_element(RMAP_NODE, &set_vpn_nexthop_cmd);
install_element(RMAP_NODE, &no_set_vpn_nexthop_cmd);
.destroy = lib_route_map_entry_set_action_rmap_set_action_extcommunity_rt_destroy,
}
},
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-nt",
+ .cbs = {
+ .modify = lib_route_map_entry_set_action_rmap_set_action_extcommunity_nt_modify,
+ .destroy = lib_route_map_entry_set_action_rmap_set_action_extcommunity_nt_destroy,
+ }
+ },
{
.xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-soo",
.cbs = {
int lib_route_map_entry_set_action_rmap_set_action_distance_destroy(struct nb_cb_destroy_args *args);
int lib_route_map_entry_set_action_rmap_set_action_extcommunity_rt_modify(struct nb_cb_modify_args *args);
int lib_route_map_entry_set_action_rmap_set_action_extcommunity_rt_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_extcommunity_nt_modify(
+ struct nb_cb_modify_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_extcommunity_nt_destroy(
+ struct nb_cb_destroy_args *args);
int lib_route_map_entry_set_action_rmap_set_action_extcommunity_soo_modify(struct nb_cb_modify_args *args);
int lib_route_map_entry_set_action_rmap_set_action_extcommunity_soo_destroy(struct nb_cb_destroy_args *args);
int lib_route_map_entry_set_action_rmap_set_action_ipv4_address_modify(struct nb_cb_modify_args *args);
return NB_OK;
}
+/*
+ * XPath:
+ * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-nt
+ */
+int lib_route_map_entry_set_action_rmap_set_action_extcommunity_nt_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ const char *str;
+ int rv;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ str = yang_dnode_get_string(args->dnode, NULL);
+
+ /* Set destroy information. */
+ rhc->rhc_shook = generic_set_delete;
+ rhc->rhc_rule = "extcommunity nt";
+ rhc->rhc_event = RMAP_EVENT_SET_DELETED;
+
+ rv = generic_set_add(rhc->rhc_rmi, "extcommunity nt", str,
+ args->errmsg, args->errmsg_len);
+ if (rv != CMD_SUCCESS) {
+ rhc->rhc_shook = NULL;
+ return NB_ERR_INCONSISTENCY;
+ }
+ }
+
+ return NB_OK;
+}
+
+int lib_route_map_entry_set_action_rmap_set_action_extcommunity_nt_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_match_destroy(args);
+ }
+
+ return NB_OK;
+}
+
/*
* XPath:
* /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-soo
}
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;
}
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))
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;
return CMD_SUCCESS;
}
+DEFPY(bgp_lu_uses_explicit_null, bgp_lu_uses_explicit_null_cmd,
+ "[no] bgp labeled-unicast <explicit-null|ipv4-explicit-null|ipv6-explicit-null>$value",
+ NO_STR BGP_STR
+ "BGP Labeled-unicast options\n"
+ "Use explicit-null label values for all local prefixes\n"
+ "Use the IPv4 explicit-null label value for IPv4 local prefixes\n"
+ "Use the IPv6 explicit-null label value for IPv6 local prefixes\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ uint64_t label_mode;
+
+ if (strmatch(value, "ipv4-explicit-null"))
+ label_mode = BGP_FLAG_LU_IPV4_EXPLICIT_NULL;
+ else if (strmatch(value, "ipv6-explicit-null"))
+ label_mode = BGP_FLAG_LU_IPV6_EXPLICIT_NULL;
+ else
+ label_mode = BGP_FLAG_LU_IPV4_EXPLICIT_NULL |
+ BGP_FLAG_LU_IPV6_EXPLICIT_NULL;
+ if (no)
+ UNSET_FLAG(bgp->flags, label_mode);
+ else
+ SET_FLAG(bgp->flags, label_mode);
+ return CMD_SUCCESS;
+}
+
DEFUN(bgp_suppress_duplicates, bgp_suppress_duplicates_cmd,
"bgp suppress-duplicates",
BGP_STR
return CMD_WARNING_CONFIG_FAILED;
if (!yes) {
- /* implement me */
- vty_out(vty, "It's not implemented\n");
- return CMD_WARNING_CONFIG_FAILED;
+ /* when SID is not set, do nothing */
+ if ((bgp->vpn_policy[afi].tovpn_sid_index == 0) &&
+ !CHECK_FLAG(bgp->vpn_policy[afi].flags,
+ BGP_VPN_POLICY_TOVPN_SID_AUTO))
+ return CMD_SUCCESS;
+
+ /* pre-change */
+ vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, afi,
+ bgp_get_default(), bgp);
+ bgp->vpn_policy[afi].tovpn_sid_index = 0;
+ UNSET_FLAG(bgp->vpn_policy[afi].flags,
+ BGP_VPN_POLICY_TOVPN_SID_AUTO);
+
+ /* post-change */
+ vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, afi,
+ bgp_get_default(), bgp);
+
+ return CMD_SUCCESS;
}
if (bgp->tovpn_sid_index != 0 ||
zlog_debug("%s: auto sid alloc.", __func__);
SET_FLAG(bgp->vpn_policy[afi].flags,
BGP_VPN_POLICY_TOVPN_SID_AUTO);
- } else {
+ } else if (sid_idx != 0) {
/* SID allocation index-mode */
if (debug)
zlog_debug("%s: idx %ld sid alloc.", __func__, sid_idx);
{
static char stripped[BUFSIZ];
uint32_t i = 0;
- uint32_t last_space = 0;
+ uint32_t last_space = size;
while (i < size) {
- if (*(desc + i) == 0) {
+ if (*(desc + i) == '\0') {
stripped[i] = '\0';
return stripped;
}
i++;
}
- if (last_space > size)
- stripped[size + 1] = '\0';
- else
- stripped[last_space] = '\0';
+ stripped[last_space] = '\0';
return stripped;
}
? ""
: "no ");
+ if (!!CHECK_FLAG(bgp->flags, BGP_FLAG_LU_IPV4_EXPLICIT_NULL) &&
+ !!CHECK_FLAG(bgp->flags, BGP_FLAG_LU_IPV6_EXPLICIT_NULL))
+ vty_out(vty, " bgp labeled-unicast explicit-null\n");
+ else if (!!CHECK_FLAG(bgp->flags,
+ BGP_FLAG_LU_IPV4_EXPLICIT_NULL))
+ vty_out(vty,
+ " bgp labeled-unicast ipv4-explicit-null\n");
+ else if (!!CHECK_FLAG(bgp->flags,
+ BGP_FLAG_LU_IPV6_EXPLICIT_NULL))
+ vty_out(vty,
+ " bgp labeled-unicast ipv6-explicit-null\n");
+
/* draft-ietf-idr-deprecate-as-set-confed-set */
if (bgp->reject_as_sets)
vty_out(vty, " bgp reject-as-sets\n");
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);
peer->bgp->vrf_id);
}
+ /* Handle peerings via loopbacks. For instance, peer between
+ * 127.0.0.1 and 127.0.0.2. In short, allow peering with self
+ * via 127.0.0.0/8.
+ */
+ if (!ifp && cmd_allow_reserved_ranges_get())
+ ifp = if_get_vrf_loopback(peer->bgp->vrf_id);
+
if (!ifp) {
/*
* BGP views do not currently get proper data
#ifdef ENABLE_BGP_VNC
rfapi_delete(bgp);
#endif
+
+ /* Free memory allocated with aggregate address configuration. */
+ FOREACH_AFI_SAFI (afi, safi) {
+ struct bgp_aggregate *aggregate = NULL;
+
+ for (struct bgp_dest *dest =
+ bgp_table_top(bgp->aggregate[afi][safi]);
+ dest; dest = bgp_route_next(dest)) {
+ aggregate = bgp_dest_get_bgp_aggregate_info(dest);
+ if (aggregate == NULL)
+ continue;
+
+ bgp_dest_set_bgp_aggregate_info(dest, NULL);
+ bgp_free_aggregate_info(aggregate);
+ }
+ }
+
bgp_cleanup_routes(bgp);
for (afi = 0; afi < AFI_MAX; ++afi) {
return;
if (CHECK_FLAG(peer->cap, PEER_CAP_REFRESH_OLD_RCV) ||
- CHECK_FLAG(peer->cap, PEER_CAP_REFRESH_NEW_RCV)) {
- if (CHECK_FLAG(peer->af_cap[afi][safi],
- PEER_CAP_ORF_PREFIX_SM_ADV) &&
- (CHECK_FLAG(peer->af_cap[afi][safi],
- PEER_CAP_ORF_PREFIX_RM_RCV) ||
- CHECK_FLAG(peer->af_cap[afi][safi],
- PEER_CAP_ORF_PREFIX_RM_OLD_RCV)))
- peer_clear_soft(peer, afi, safi,
- BGP_CLEAR_SOFT_IN_ORF_PREFIX);
- else
- bgp_route_refresh_send(
- peer, afi, safi, 0, 0, 0,
- BGP_ROUTE_REFRESH_NORMAL);
- }
+ CHECK_FLAG(peer->cap, PEER_CAP_REFRESH_NEW_RCV))
+ bgp_route_refresh_send(peer, afi, safi, 0, 0, 0,
+ BGP_ROUTE_REFRESH_NORMAL);
}
}
/* If we touch prefix-list, we need to process
* new updates. This is important for ORF to
- * work correctly as well.
+ * work correctly.
*/
- if (peer->afc_nego[afi][safi])
- peer_on_policy_change(peer, afi, safi,
- 0);
+ if (CHECK_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_SM_ADV) &&
+ (CHECK_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_RM_RCV) ||
+ CHECK_FLAG(
+ peer->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_RM_OLD_RCV)))
+ peer_clear_soft(
+ peer, afi, safi,
+ BGP_CLEAR_SOFT_IN_ORF_PREFIX);
}
}
for (ALL_LIST_ELEMENTS(bgp->group, node, nnode, group)) {
#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 IPv4 local prefixes to use ipv4-explicit-null label */
+#define BGP_FLAG_LU_IPV4_EXPLICIT_NULL (1ULL << 33)
+/* For BGP-LU, force IPv6 local prefixes to use ipv6-explicit-null label */
+#define BGP_FLAG_LU_IPV6_EXPLICIT_NULL (1ULL << 34)
/* BGP default address-families.
* New peers inherit enabled afi/safis from bgp instance.
#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"
{
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);
struct agg_node *rn;
int holddown_count = 0;
- int local_count = 0;
int imported_count = 0;
int remote_count = 0;
++holddown_count;
} else {
- if (RFAPI_LOCAL_BI(bpi)) {
- ++local_count;
- } else {
+ if (!RFAPI_LOCAL_BI(bpi)) {
if (RFAPI_DIRECT_IMPORT_BI(
bpi)) {
++imported_count;
#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
struct bgp *bgp = bgp_get_default();
uint32_t t_pfx_active = 0;
- uint32_t t_pfx_deleted = 0;
uint32_t t_ri_active = 0;
uint32_t t_ri_deleted = 0;
afi_t afi;
uint32_t pfx_active = 0;
- uint32_t pfx_deleted = 0;
for (afi = AFI_IP; afi < AFI_MAX; ++afi) {
if (dsl) {
ri_deleted = skiplist_count(dsl);
t_ri_deleted += ri_deleted;
- ++pfx_deleted;
- ++t_pfx_deleted;
}
}
for (rn = agg_route_top(rfd->rib_pending[afi]); rn;
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;
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 *)
{
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
int printedheader = 0;
int routes_total = 0;
int nhs_total = 0;
- int prefixes_total = 0;
- int prefixes_displayed = 0;
- int nves_total = 0;
- int nves_with_routes = 0;
int nves_displayed = 0;
int routes_displayed = 0;
int nhs_displayed = 0;
int printednve = 0;
afi_t afi;
- ++nves_total;
- if (rfd->rib_prefix_count)
- ++nves_with_routes;
-
for (afi = AFI_IP; afi < AFI_MAX; ++afi) {
struct agg_node *rn;
routes_total++;
nhs_total += skiplist_count(sl);
- ++prefixes_total;
if (pfx_match && !prefix_match(pfx_match, p)
&& !prefix_match(p, pfx_match))
continue;
- ++prefixes_displayed;
-
if (!printedheader) {
++printedheader;
const char *vty_newline;
int printedheader = 0;
-
- int nves_total = 0;
- int nves_with_queries = 0;
- int nves_displayed = 0;
-
int queries_total = 0;
int queries_displayed = 0;
struct agg_node *rn;
int printedquerier = 0;
-
- ++nves_total;
-
- if (rfd->mon
- || (rfd->mon_eth && skiplist_count(rfd->mon_eth))) {
- ++nves_with_queries;
- } else {
+ if (!rfd->mon &&
+ !(rfd->mon_eth && skiplist_count(rfd->mon_eth)))
continue;
- }
/*
* IP Queries
fp(out, "%-15s %-15s", buf_vn, buf_un);
printedquerier = 1;
-
- ++nves_displayed;
} else
fp(out, "%-15s %-15s", "", "");
buf_remain[0] = 0;
fp(out, "%-15s %-15s", buf_vn, buf_un);
printedquerier = 1;
-
- ++nves_displayed;
} else
fp(out, "%-15s %-15s", "", "");
buf_remain[0] = 0;
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;
}
if (RFAPI_HAS_MONITOR_EXTERIOR(rn_interior)) {
- int count = 0; /* debugging */
-
vnc_zlog_debug_verbose(
"%s: has exterior monitor; ext src: %p", __func__,
RFAPI_MONITOR_EXTERIOR(rn_interior)->source);
struct attr new_attr;
uint32_t label = 0;
-
- ++count; /* debugging */
-
assert(bpi_exterior);
assert(pfx_exterior);
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
AC_ARG_ENABLE([ospfclient],
AS_HELP_STRING([--disable-ospfclient], [do not build OSPFAPI client for OSPFAPI,
(this is the default if --disable-ospfapi is set)]))
+AC_ARG_WITH([log_timestamp_precision],
+ AS_HELP_STRING([--with-log-timestamp-precision=ARG], [set startup log timestamp precision, ARG must be 0-12]))
AC_ARG_ENABLE([multipath],
AS_HELP_STRING([--enable-multipath=ARG], [enable multipath function, ARG must be digit]))
AC_ARG_WITH([service_timeout],
AC_DEFINE_UNQUOTED([MULTIPATH_NUM], [$MPATH_NUM], [Maximum number of paths for a route])
-AC_DEFINE_UNQUOTED([VTYSH_PAGER], ["$VTYSH_PAGER"], [What pager to use])
+case "${with_log_timestamp_precision}" in
+[[0-9]|1[012]])
+;;
+"")
+;;
+*)
+AC_MSG_FAILURE([Please specify a number from 0-12 for log precision ARG])
+;;
+esac
+with_log_timestamp_precision=${with_log_timestamp_precision:-0}
+AC_DEFINE_UNQUOTED([LOG_TIMESTAMP_PRECISION], [${with_log_timestamp_precision}], [Startup zlog timestamp precision])
+AC_DEFINE_UNQUOTED([VTYSH_PAGER], ["$VTYSH_PAGER"], [What pager to use])
TIMEOUT_MIN=2
case "${with_service_timeout}" in
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
^^^^^^^^^^^^^^^
.. code:: shell
cd test_to_be_run
- ./test_to_be_run.py
+ sudo -E pytest ./test_to_be_run.py
For example, and assuming you are inside the frr directory:
.. code:: shell
cd tests/topotests/bgp_l3vpn_to_bgp_vrf
- ./test_bgp_l3vpn_to_bgp_vrf.py
+ sudo -E pytest ./test_bgp_l3vpn_to_bgp_vrf.py
For further options, refer to pytest documentation.
.. _screen: https://www.gnu.org/software/screen/
.. _tmux: https://github.com/tmux/tmux/wiki
+Capturing Packets
+"""""""""""""""""
+
+One can view and capture packets on any of the networks or interfaces defined by
+the topotest by specifying the ``--pcap=NET|INTF|all[,NET|INTF,...]`` CLI option
+as shown in the examples below.
+
+.. code:: shell
+
+ # Capture on all networks in isis_topo1 test
+ sudo -E pytest isis_topo1 --pcap=all
+
+ # Capture on `sw1` network
+ sudo -E pytest isis_topo1 --pcap=sw1
+
+ # Capture on `sw1` network and on interface `eth0` on router `r2`
+ sudo -E pytest isis_topo1 --pcap=sw1,r2:r2-eth0
+
+For each capture a window is opened displaying a live summary of the captured
+packets. Additionally, the entire packet stream is captured in a pcap file in
+the tests log directory e.g.,::
+
+.. code:: console
+
+ $ sudo -E pytest isis_topo1 --pcap=sw1,r2:r2-eth0
+ ...
+ $ ls -l /tmp/topotests/isis_topo1.test_isis_topo1/
+ -rw------- 1 root root 45172 Apr 19 05:30 capture-r2-r2-eth0.pcap
+ -rw------- 1 root root 48412 Apr 19 05:30 capture-sw1.pcap
+ ...
+-
+Viewing Live Daemon Logs
+""""""""""""""""""""""""
+
+One can live view daemon or the frr logs in separate windows using the
+``--logd`` CLI option as shown below.
+
+.. code:: shell
+
+ # View `ripd` logs on all routers in test
+ sudo -E pytest rip_allow_ecmp --logd=ripd
+
+ # View `ripd` logs on all routers and `mgmtd` log on `r1`
+ sudo -E pytest rip_allow_ecmp --logd=ripd --logd=mgmtd,r1
+
+For each capture a window is opened displaying a live summary of the captured
+packets. Additionally, the entire packet stream is captured in a pcap file in
+the tests log directory e.g.,::
+
+When using a unified log file `frr.log` one substitutes `frr` for the daemon
+name in the ``--logd`` CLI option, e.g.,
+
+.. code:: shell
+
+ # View `frr` log on all routers in test
+ sudo -E pytest some_test_suite --logd=frr
+
Spawning Debugging CLI, ``vtysh`` or Shells on Routers on Test Failure
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
test_bgp_multiview_topo1/test_bgp_routingTable> help
- 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
+ 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:
+ logd HOST [HOST ...] DAEMON :: tail -f on the logfile of the given DAEMON for the given HOST[S]
+ pcap NETWORK :: 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.
+ stderr HOST [HOST ...] DAEMON :: tail -f on the stderr of the given DAEMON for the given HOST[S]
+ stdlog HOST [HOST ...] :: tail -f on the `frr.log` for the given HOST[S]
+ stdout HOST [HOST ...] DAEMON :: tail -f on the stdout of the given DAEMON for the given HOST[S]
+ term HOST [HOST ...] :: open terminal[s] (TMUX or XTerm) on HOST[S], * for all
+ vtysh ROUTER [ROUTER ...] ::
+ xterm HOST [HOST ...] :: open XTerm[s] on HOST[S], * for all
+ Inline Commands:
+ [ROUTER ...] COMMAND :: execute vtysh COMMAND on the router[s]
+ [HOST ...] sh <SHELL-COMMAND> :: execute <SHELL-COMMAND> on hosts
+ [HOST ...] shi <INTERACTIVE-COMMAND> :: execute <INTERACTIVE-COMMAND> on HOST[s]
test_bgp_multiview_topo1/test_bgp_routingTable> r1 show int br
------ Host: r1 ------
sudo -E pytest --valgrind-memleaks all-protocol-startup
+Collecting Performance Data using perf(1)
+"""""""""""""""""""""""""""""""""""""""""
+
+Topotest can automatically launch any daemon under ``perf(1)`` to collect
+performance data. The daemon is run in non-daemon mode with ``perf record -g``.
+The ``perf.data`` file will be saved in the router specific directory under the
+tests run directoy.
+
+Here's an example of collecting performance data from ``mgmtd`` on router ``r1``
+during the config_timing test.
+
+.. code:: console
+
+ $ sudo -E pytest --perf=mgmtd,r1 config_timing
+ ...
+ $ find /tmp/topotests/ -name '*perf.data*'
+ /tmp/topotests/config_timing.test_config_timing/r1/perf.data
+
+To specify different arguments for ``perf record``, one can use the
+``--perf-options`` this will replace the ``-g`` used by default.
+
.. _topotests_docker:
Running Tests with Docker
+------------------------------------+-------+
| ZEBRA_IMPORT_ROUTE_UNREGISTER | 27 |
+------------------------------------+-------+
-| ZEBRA_IMPORT_CHECK_UPDATE | 28 |
-+------------------------------------+-------+
| ZEBRA_BFD_DEST_REGISTER | 29 |
+------------------------------------+-------+
| ZEBRA_BFD_DEST_DEREGISTER | 30 |
Display a usage message on standard output and exit.
+.. option:: -t, --timestamp
+
+ Print a timestamp before going to shell or reading the configuration file.
+
+.. option:: --no-fork
+
+ When used in conjunction with ``-b``, prevents vtysh from forking children to handle configuring each target daemon.
+
+
ENVIRONMENT VARIABLES
=====================
VTYSH_PAGER
Allow using IPv4 reserved (Class E) IP ranges for daemons. E.g.: setting
IPv4 addresses for interfaces or allowing reserved ranges in BGP next-hops.
+ If you need multiple FRR instances (or FRR + any other daemon) running in a
+ single router and peering via 127.0.0.0/8, it's also possible to use this
+ knob if turned on.
+
Default: off.
.. _sample-config-file:
* :rfc:`5880`
* :rfc:`5881`
+* :rfc:`5882`
* :rfc:`5883`
Currently, there are two implementations of the BFD commands in FRR:
that interface.
+.. _bfd-rip-peer-config:
+
+RIP BFD configuration
+---------------------
+
+The following commands are available inside the interface configuration node:
+
+.. clicmd:: ip rip bfd
+
+ Automatically create BFD session for each RIP peer discovered in this
+ interface. When the BFD session monitor signalize that the link is down
+ the RIP peer is removed and all the learned routes associated with that
+ peer are removed.
+
+
+.. clicmd:: ip rip bfd profile BFD_PROFILE_NAME
+
+ Selects a BFD profile for the BFD sessions created in this interface.
+
+
+The following command is available in the RIP router configuration node:
+
+.. clicmd:: bfd default-profile BFD_PROFILE_NAME
+
+ Selects a default BFD profile for all sessions without a profile specified.
+
+
.. _bfd-static-peer-config:
BFD Static Route Monitoring Configuration
.. clicmd:: set extcommunity rt EXTCOMMUNITY
- This command set Route Target value.
+ This command sets Route Target value.
+
+.. clicmd:: set extcommunity nt EXTCOMMUNITY
+
+ This command sets Node Target value.
+
+ If the receiving BGP router supports Node Target Extended Communities,
+ it will install the route with the community that contains it's own
+ local BGP Identifier. Otherwise, it's not installed.
.. clicmd:: set extcommunity soo EXTCOMMUNITY
- This command set Site of Origin value.
+ This command sets Site of Origin value.
.. clicmd:: set extcommunity bandwidth <(1-25600) | cumulative | num-multipaths> [non-transitive]
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|ipv4-explicit-null|ipv6-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
grpc
filter
routemap
+ affinitymap
ipv6
kernel
snmp
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:
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.
:ref:`ospf-traffic-engineering`
-
-.. _debugging-isis:
+.. _isis-segment-routing:
Segment Routing
===============
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'
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
==============
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.
.. 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
.. clicmd:: show mgmt commit-history
This command dumps details of upto last 10 commits handled by MGMTd.
+
+
+MGMT Daemon debug commands
+==========================
+
+The following debug commands enable debugging within the management daemon:
+
+.. clicmd:: [no] debug mgmt backend
+
+ Enable[/Disable] debugging messages related to backend operations within the
+ management daemon.
+
+.. clicmd:: [no] debug mgmt datastore
+
+ Enable[/Disable] debugging messages related to YANG datastore operations
+ within the management daemon.
+
+.. clicmd:: [no] debug mgmt frontend
+
+ Enable[/Disable] debugging messages related to frontend operations within the
+ management daemon.
+
+.. clicmd:: [no] debug mgmt transaction
+
+ Enable[/Disable] debugging messages related to transactions within the
+ management daemon.
+
+
+MGMT Client debug commands
+==========================
+
+The following debug commands enable debugging within the management front and
+backend clients:
+
+.. clicmd:: [no] debug mgmt client backend
+
+ Enable[/Disable] debugging messages related to backend operations inside the
+ backend mgmtd clients.
+
+.. clicmd:: [no] debug mgmt client frontend
+
+ Enable[/Disable] debugging messages related to frontend operations inside the
+ frontend mgmtd clients.
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
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
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)
: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
----
:t:`Bidirectional Forwarding Detection (BFD), D. Katz, D. Ward. June 2010`
- :rfc:`5881`
:t:`Bidirectional Forwarding Detection (BFD) for IPv4 and IPv6 (Single Hop), D. Katz, D. Ward. June 2010`
+- :rfc:`5882`
+ :t:`Generic Application of Bidirectional Forwarding Detection (BFD), D. Katz, D. Ward. June 2010`
- :rfc:`5883`
:t:`Bidirectional Forwarding Detection (BFD) for Multihop Paths, D. Katz, D. Ward. June 2010`
.. 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.
.. 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)>
+++ /dev/null
-# 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"]
+++ /dev/null
-#!/bin/sh
-
-set -e
-
-##
-# Package version needs to be decimal
-##
-DISTRO=ubi-8
-
-GITREV="$2"
-if [ -z "$GITREV" ];then
- GITREV="$(git rev-parse --short=10 HEAD)"
-fi
-
-FRR_IMAGE_TAG="$1"
-if [ -z $FRR_IMAGE_TAG ];then
- FRR_IMAGE_TAG="frr:ubi-8-$GITREV"
-fi
-PKGVER="$(printf '%u\n' 0x$GITREV)"
-
-FRR_RELEASE="$3"
-if [ -z $FRR_RELEASE ];then
- FRR_RELEASE=$(git describe --tags --abbrev=0)
-fi
-
-FRR_NAME=$4
-if [ -z $FRR_NAME ];then
- FRR_NAME=frr
-fi
-
-FRR_VENDOR=$5
-if [ -z $FRR_VENDOR ];then
- FRR_VENDOR=frr
-fi
-
-docker build \
- --cache-from="frr:$DISTRO-builder-$GITREV" \
- --file=docker/$DISTRO/Dockerfile \
- --build-arg="PKGVER=$PKGVER" \
- --build-arg="FRR_IMAGE_TAG=$FRR_IMAGE_TAG" \
- --build-arg="FRR_RELEASE=$FRR_RELEASE" \
- --build-arg="FRR_NAME=$FRR_NAME" \
- --build-arg="FRR_VENDOR=$FRR_VENDOR" \
- --tag="$FRR_IMAGE_TAG" \
- .
-
+++ /dev/null
-#!/bin/bash
-
-source /usr/lib/frr/frrcommon.sh
-/usr/lib/frr/watchfrr $(daemon_list)
--- /dev/null
+# 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"]
--- /dev/null
+[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
--- /dev/null
+#!/bin/sh
+
+set -e
+
+##
+# Package version needs to be decimal
+##
+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="$3"
+if [ -z $FRR_IMAGE_TAG ];then
+ FRR_IMAGE_TAG="frr:ubi8-minimal-$GITREV"
+fi
+PKGVER="$(printf '%u\n' 0x$GITREV)"
+
+FRR_RELEASE="$4"
+if [ -z $FRR_RELEASE ];then
+ FRR_RELEASE=$(git describe --tags --abbrev=0)
+fi
+
+FRR_NAME=$5
+if [ -z $FRR_NAME ];then
+ FRR_NAME=frr
+fi
+
+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="$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" \
+ --build-arg="FRR_NAME=$FRR_NAME" \
+ --build-arg="FRR_VENDOR=$FRR_VENDOR" \
+ --tag="$FRR_IMAGE_TAG" \
+ .
+
--- /dev/null
+#!/bin/bash
+
+source /usr/lib/frr/frrcommon.sh
+/usr/lib/frr/watchfrr $(daemon_list)
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,
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);
}
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);
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;
}
const char *isis_adj_name(const struct isis_adjacency *adj)
{
+ static char buf[ISO_SYSID_STRLEN];
+
if (!adj)
return "NONE";
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,
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++)
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) {
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);
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",
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) {
+ 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");
}
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);
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",
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) {
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;
--- /dev/null
+// 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;
+
+ if (!isis)
+ return false;
+
+ 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;
+
+ if (!isis)
+ return;
+
+ 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 */
--- /dev/null
+// 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__ */
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))
}
#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();
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();
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");
#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"
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
*/
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
}
}
+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
*/
vty_out(vty, " log-adjacency-changes\n");
}
+/*
+ * 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
*/
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);
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);
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);
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);
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 */
#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
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;
}
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;
}
--- /dev/null
+// 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(struct isis_flex_algo_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]);
+ XFREE(MTYPE_FLEX_ALGO, data);
+}
+
+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;
+ 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)
+ zlog_warn(
+ "ISIS-SPF: LSP from %pPN neighbor %pPN. Admin-group 0x%08x differs from ext admin-group 0x%08x.",
+ lsp->hdr.lsp_id, reach->id, *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 */
--- /dev/null
+// 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 */
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,
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. */
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);
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;
/* 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;
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);
}
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;
}
/* 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;
#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");
|| (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);
&& 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,
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,
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;
}
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;
}
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,
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)
{
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);
}
}
}
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,
/* 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 */
+
+ rcap = isis_tlvs_init_router_capability(lsp->tlvs);
- cap.router_id.s_addr = area->isis->router_id;
+ 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) {
/* 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)) {
+ fa->state = false;
+ continue;
+ }
+ fa->state = true;
+ 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.
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) {
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) {
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);
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);
}
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.",
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);
}
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 */
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))
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();
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;
}
&& !(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;
}
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);
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);
}
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;
}
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);
}
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,
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)
#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"
#endif /* FABRICD */
#ifndef FABRICD
isis_cli_init();
-#endif /* ifdef FABRICD */
+#endif /* ifndef FABRICD */
isis_spf_init();
isis_redist_init();
isis_route_map_init();
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);
#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
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;
return dyn->hostname;
}
- return sysid_print(sysid);
+ snprintfrr(sys_hostname, ISO_SYSID_STRLEN, "%pSY", sysid);
+ return sys_hostname;
}
/*
/* 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) {
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 */
/*
* 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; */
/* 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;
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);
}
.modify = isis_instance_purge_originator_modify,
},
},
+ {
+ .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 = {
.modify = isis_instance_log_adjacency_changes_modify,
},
},
+ {
+ .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 = {
.modify = 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",
+ .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 = {
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(
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);
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);
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);
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);
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,
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);
#include "vrf.h"
#include "ldp_sync.h"
#include "link_state.h"
+#include "affinitymap.h"
#include "isisd/isisd.h"
#include "isisd/isis_nb.h"
#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
*/
}
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);
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];
}
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;
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;
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
*/
return NB_OK;
}
+/*
+ * 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
*/
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;
return NB_OK;
}
+/*
+ * 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
*/
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");
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;
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);
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);
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);
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;
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);
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);
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);
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);
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);
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);
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);
}
/*
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);
}
/*
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));
#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 */
}
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;
}
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 */
}
}
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
}
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
#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);
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;
}
/* 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);
}
}
if (idrp == ISO9542_ESIS) {
flog_err(EC_LIB_DEVELOPMENT,
"No support for ES-IS packet IDRP=%hhx", idrp);
+ 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_drop(circuit->area, pdu_type);
return ISIS_ERROR;
}
isis_notif_version_skew(circuit, version1, raw_pdu,
sizeof(raw_pdu));
#endif /* ifndef FABRICD */
+ pdu_counter_count_drop(circuit->area, pdu_type);
return ISIS_WARNING;
}
isis_notif_id_len_mismatch(circuit, id_len, raw_pdu,
sizeof(raw_pdu));
#endif /* ifndef FABRICD */
+ 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_drop(circuit->area, pdu_type);
return ISIS_WARNING;
}
flog_err(EC_ISIS_PACKET,
"Expected fixed header length = %hhu but got %hhu",
expected_length, length);
+ pdu_counter_count_drop(circuit->area, pdu_type);
return ISIS_ERROR;
}
flog_err(
EC_ISIS_PACKET,
"PDU is too short to contain fixed header of given PDU type.");
+ pdu_counter_count_drop(circuit->area, pdu_type);
return ISIS_ERROR;
}
isis_notif_version_skew(circuit, version2, raw_pdu,
sizeof(raw_pdu));
#endif /* ifndef FABRICD */
+ 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_drop(circuit->area, pdu_type);
return ISIS_WARNING;
}
isis_notif_max_area_addr_mismatch(circuit, max_area_addrs,
raw_pdu, sizeof(raw_pdu));
#endif /* ifndef FABRICD */
+ pdu_counter_count_drop(circuit->area, pdu_type);
return ISIS_ERROR;
}
case L1_LAN_HELLO:
case L2_LAN_HELLO:
case P2P_HELLO:
- if (fabricd && pdu_type != P2P_HELLO)
+ if (fabricd && pdu_type != P2P_HELLO) {
+ pdu_counter_count_drop(circuit->area, pdu_type);
return ISIS_ERROR;
+ }
+
retval = process_hello(pdu_type, circuit, ssnpa);
break;
case L1_LINK_STATE:
case L2_LINK_STATE:
case FS_LINK_STATE:
- if (fabricd
- && pdu_type != L2_LINK_STATE
- && pdu_type != FS_LINK_STATE)
+ if (fabricd && pdu_type != L2_LINK_STATE &&
+ pdu_type != FS_LINK_STATE) {
+ pdu_counter_count_drop(circuit->area, pdu_type);
return ISIS_ERROR;
+ }
+
retval = process_lsp(pdu_type, circuit, ssnpa, max_area_addrs);
break;
case L1_COMPLETE_SEQ_NUM:
retval = process_snp(pdu_type, circuit, ssnpa);
break;
default:
+ pdu_counter_count_drop(circuit->area, pdu_type);
return ISIS_ERROR;
}
+ if (retval != ISIS_OK)
+ pdu_counter_count_drop(circuit->area, pdu_type);
+
return retval;
}
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 */
}
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));
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));
+}
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 */
#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)
{
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];
+}
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
#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,
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;
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)
{
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;
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)
{
|| new->sid.value != old->sid.value)
return false;
+ if (new->sid.algorithm != old->sid.algorithm)
+ return false;
+
return true;
}
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) {
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,
* 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,
/* 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);
#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)) {
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);
}
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) {
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;
}
}
+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
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;
(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);
}
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
ISIS_ROUTE_FLAG_ZEBRA_SYNCED)) {
continue;
}
+ } else {
+ mrnode->info = rnode->info;
}
- mrnode->info = rnode->info;
}
}
{
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;
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.
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),
/* 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,
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 */
/* Declare static local variables for convenience. */
SNMP_LOCAL_VARIABLES
-/* If ARRAY_SIZE is not available use a primitive substitution */
-#ifndef ARRAY_SIZE
-#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
-#endif
-
/*
* Define time function, it serves two purposes
* 1. Uses unint32_t for unix time and encapsulates
{isis_snmp_find_isadj_ipaddr, {ISIS_ISADJIPADDR_ENTRY}, 4},
{isis_snmp_find_isadj_prot_supp, {ISIS_ISADJPROTSUPP_ENTRY}, 4},
};
-static size_t isis_func_to_prefix_count = ARRAY_SIZE(isis_func_to_prefix_arr);
+static size_t isis_func_to_prefix_count = array_size(isis_func_to_prefix_arr);
static struct variable isis_var_arr[] = {
{ISIS_SYS_VERSION, INTEGER, RONLY, isis_snmp_find_sys_object},
isis_snmp_find_isadj_prot_supp},
};
-static const size_t isis_var_count = ARRAY_SIZE(isis_var_arr);
+static const size_t isis_var_count = array_size(isis_var_arr);
/* Minimal set of hard-coded data */
#define ISIS_VERSION (1)
*/
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);
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);
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;
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) {
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:
/* Put in trap value */
snmp_varlist_add_variable(¬ification_vars, isis_snmp_trap_var,
- ARRAY_SIZE(isis_snmp_trap_var), ASN_OBJECT_ID,
+ array_size(isis_snmp_trap_var), ASN_OBJECT_ID,
(uint8_t *)&isis_snmp_trap_val_db_overload,
sizeof(isis_snmp_trap_val_db_overload));
snmp_varlist_add_variable(
¬ification_vars, isis_snmp_trap_data_var_sys_level_index,
- ARRAY_SIZE(isis_snmp_trap_data_var_sys_level_index), INTEGER,
+ array_size(isis_snmp_trap_data_var_sys_level_index), INTEGER,
(uint8_t *)&val, sizeof(val));
/* Patch sys_level_state with proper index */
- off = ARRAY_SIZE(isis_snmp_trap_data_var_sys_level_state) - 1;
+ off = array_size(isis_snmp_trap_data_var_sys_level_state) - 1;
isis_snmp_trap_data_var_sys_level_state[off] = val;
/* Prepare data */
snmp_varlist_add_variable(
¬ification_vars, isis_snmp_trap_data_var_sys_level_state,
- ARRAY_SIZE(isis_snmp_trap_data_var_sys_level_state), INTEGER,
+ array_size(isis_snmp_trap_data_var_sys_level_state), INTEGER,
(uint8_t *)&val, sizeof(val));
send_v2trap(notification_vars);
/* Put in trap value */
snmp_varlist_add_variable(¬ification_vars, isis_snmp_trap_var,
- ARRAY_SIZE(isis_snmp_trap_var), ASN_OBJECT_ID,
+ array_size(isis_snmp_trap_var), ASN_OBJECT_ID,
(uint8_t *)&isis_snmp_trap_val_lsp_exceed_max,
sizeof(isis_snmp_trap_val_lsp_exceed_max));
snmp_varlist_add_variable(
¬ification_vars, isis_snmp_trap_data_var_sys_level_index,
- ARRAY_SIZE(isis_snmp_trap_data_var_sys_level_index), INTEGER,
+ array_size(isis_snmp_trap_data_var_sys_level_index), INTEGER,
(uint8_t *)&val, sizeof(val));
snmp_varlist_add_variable(
¬ification_vars, isis_snmp_trap_data_var_pdu_lsp_id,
- ARRAY_SIZE(isis_snmp_trap_data_var_pdu_lsp_id), STRING, lsp_id,
+ array_size(isis_snmp_trap_data_var_pdu_lsp_id), STRING, lsp_id,
ISIS_SYS_ID_LEN + 2);
send_v2trap(notification_vars);
/* Put in trap value */
memcpy(var_name, isis_snmp_notifications,
sizeof(isis_snmp_notifications));
- var_count = ARRAY_SIZE(isis_snmp_notifications);
+ var_count = array_size(isis_snmp_notifications);
var_name[var_count++] = trap_id;
/* Put in trap value */
snmp_varlist_add_variable(¬ification_vars, isis_snmp_trap_var,
- ARRAY_SIZE(isis_snmp_trap_var), ASN_OBJECT_ID,
+ array_size(isis_snmp_trap_var), ASN_OBJECT_ID,
(uint8_t *)var_name, var_count * sizeof(oid));
val = circuit->is_type;
snmp_varlist_add_variable(
¬ification_vars, isis_snmp_trap_data_var_sys_level_index,
- ARRAY_SIZE(isis_snmp_trap_data_var_sys_level_index), INTEGER,
+ array_size(isis_snmp_trap_data_var_sys_level_index), INTEGER,
(uint8_t *)&val, sizeof(val));
if (oid_a_len != 0) {
snmp_varlist_add_variable(
¬ification_vars, isis_snmp_trap_data_var_circ_if_index,
- ARRAY_SIZE(isis_snmp_trap_data_var_circ_if_index), UNSIGNED32,
+ array_size(isis_snmp_trap_data_var_circ_if_index), UNSIGNED32,
(uint8_t *)&val, sizeof(val));
/* Put in trap value */
memcpy(var_name, isis_snmp_notifications,
sizeof(isis_snmp_notifications));
- var_count = ARRAY_SIZE(isis_snmp_notifications);
+ var_count = array_size(isis_snmp_notifications);
var_name[var_count++] = trap_id;
/* Put in trap value */
snmp_varlist_add_variable(¬ification_vars, isis_snmp_trap_var,
- ARRAY_SIZE(isis_snmp_trap_var), ASN_OBJECT_ID,
+ array_size(isis_snmp_trap_var), ASN_OBJECT_ID,
(uint8_t *)var_name, var_count * sizeof(oid));
val = circuit->is_type;
snmp_varlist_add_variable(
¬ification_vars, isis_snmp_trap_data_var_sys_level_index,
- ARRAY_SIZE(isis_snmp_trap_data_var_sys_level_index), INTEGER,
+ array_size(isis_snmp_trap_data_var_sys_level_index), INTEGER,
(uint8_t *)&val, sizeof(val));
if (circuit->interface == NULL)
snmp_varlist_add_variable(
¬ification_vars, isis_snmp_trap_data_var_circ_if_index,
- ARRAY_SIZE(isis_snmp_trap_data_var_circ_if_index), UNSIGNED32,
+ array_size(isis_snmp_trap_data_var_circ_if_index), UNSIGNED32,
(uint8_t *)&val, sizeof(val));
isis_snmp_update_worker_a(
circuit, ISIS_TRAP_ID_LEN_MISMATCH,
isis_snmp_trap_data_var_pdu_field_len,
- ARRAY_SIZE(isis_snmp_trap_data_var_pdu_field_len), UNSIGNED32,
+ array_size(isis_snmp_trap_data_var_pdu_field_len), UNSIGNED32,
&val, sizeof(val), isis_snmp_trap_data_var_pdu_fragment,
- ARRAY_SIZE(isis_snmp_trap_data_var_pdu_fragment), STRING,
+ array_size(isis_snmp_trap_data_var_pdu_fragment), STRING,
raw_pdu, raw_pdu_len);
return 0;
}
isis_snmp_update_worker_a(
circuit, ISIS_TRAP_MAX_AREA_ADDR_MISMATCH,
isis_snmp_trap_data_var_pdu_max_area_addr,
- ARRAY_SIZE(isis_snmp_trap_data_var_pdu_max_area_addr),
+ array_size(isis_snmp_trap_data_var_pdu_max_area_addr),
UNSIGNED32, &val, sizeof(val),
isis_snmp_trap_data_var_pdu_fragment,
- ARRAY_SIZE(isis_snmp_trap_data_var_pdu_fragment), STRING,
+ array_size(isis_snmp_trap_data_var_pdu_fragment), STRING,
raw_pdu, raw_pdu_len);
return 0;
}
isis_snmp_update_worker_a(
circuit, ISIS_TRAP_OWN_LSP_PURGE, NULL, 0, STRING, NULL, 0,
isis_snmp_trap_data_var_pdu_lsp_id,
- ARRAY_SIZE(isis_snmp_trap_data_var_pdu_lsp_id), STRING, lsp_id,
+ array_size(isis_snmp_trap_data_var_pdu_lsp_id), STRING, lsp_id,
ISIS_SYS_ID_LEN + 2);
return 0;
}
isis_snmp_update_worker_a(
circuit, ISIS_TRAP_SEQNO_SKIPPED, NULL, 0, STRING, NULL, 0,
isis_snmp_trap_data_var_pdu_lsp_id,
- ARRAY_SIZE(isis_snmp_trap_data_var_pdu_lsp_id), STRING, lsp_id,
+ array_size(isis_snmp_trap_data_var_pdu_lsp_id), STRING, lsp_id,
ISIS_SYS_ID_LEN + 2);
return 0;
}
isis_snmp_update_worker_a(
circuit, ISIS_TRAP_AUTHEN_TYPE_FAILURE, NULL, 0, STRING, NULL,
0, isis_snmp_trap_data_var_pdu_fragment,
- ARRAY_SIZE(isis_snmp_trap_data_var_pdu_fragment), STRING,
+ array_size(isis_snmp_trap_data_var_pdu_fragment), STRING,
raw_pdu, raw_pdu_len);
return 0;
}
isis_snmp_update_worker_a(
circuit, ISIS_TRAP_AUTHEN_FAILURE, NULL, 0, STRING, NULL, 0,
isis_snmp_trap_data_var_pdu_fragment,
- ARRAY_SIZE(isis_snmp_trap_data_var_pdu_fragment), STRING,
+ array_size(isis_snmp_trap_data_var_pdu_fragment), STRING,
raw_pdu, raw_pdu_len);
return 0;
}
isis_snmp_update_worker_b(
circuit, ISIS_TRAP_VERSION_SKEW,
isis_snmp_trap_data_var_pdu_proto_ver,
- ARRAY_SIZE(isis_snmp_trap_data_var_pdu_proto_ver), UNSIGNED32,
+ array_size(isis_snmp_trap_data_var_pdu_proto_ver), UNSIGNED32,
&val, sizeof(val), isis_snmp_trap_data_var_pdu_fragment,
- ARRAY_SIZE(isis_snmp_trap_data_var_pdu_fragment), STRING,
+ array_size(isis_snmp_trap_data_var_pdu_fragment), STRING,
raw_pdu, raw_pdu_len);
return 0;
}
/* Put in trap value */
snmp_varlist_add_variable(¬ification_vars, isis_snmp_trap_var,
- ARRAY_SIZE(isis_snmp_trap_var), ASN_OBJECT_ID,
+ array_size(isis_snmp_trap_var), ASN_OBJECT_ID,
(uint8_t *)&isis_snmp_trap_val_area_mismatch,
sizeof(isis_snmp_trap_val_area_mismatch));
snmp_varlist_add_variable(
¬ification_vars, isis_snmp_trap_data_var_circ_if_index,
- ARRAY_SIZE(isis_snmp_trap_data_var_circ_if_index), UNSIGNED32,
+ array_size(isis_snmp_trap_data_var_circ_if_index), UNSIGNED32,
(uint8_t *)&val, sizeof(val));
snmp_varlist_add_variable(
¬ification_vars, isis_snmp_trap_data_var_pdu_fragment,
- ARRAY_SIZE(isis_snmp_trap_data_var_pdu_fragment), STRING,
+ array_size(isis_snmp_trap_data_var_pdu_fragment), STRING,
raw_pdu, raw_pdu_len);
send_v2trap(notification_vars);
isis_snmp_update_worker_a(
circuit, ISIS_TRAP_REJ_ADJACENCY, NULL, 0, STRING, NULL, 0,
isis_snmp_trap_data_var_pdu_fragment,
- ARRAY_SIZE(isis_snmp_trap_data_var_pdu_fragment), STRING,
+ array_size(isis_snmp_trap_data_var_pdu_fragment), STRING,
raw_pdu, raw_pdu_len);
return 0;
}
isis_snmp_update_worker_b(
circuit, ISIS_TRAP_LSP_TOO_LARGE,
isis_snmp_trap_data_var_pdu_lsp_size,
- ARRAY_SIZE(isis_snmp_trap_data_var_pdu_lsp_size), UNSIGNED32,
+ array_size(isis_snmp_trap_data_var_pdu_lsp_size), UNSIGNED32,
&pdu_size, sizeof(pdu_size), isis_snmp_trap_data_var_pdu_lsp_id,
- ARRAY_SIZE(isis_snmp_trap_data_var_pdu_lsp_id), STRING, lsp_id,
+ array_size(isis_snmp_trap_data_var_pdu_lsp_id), STRING, lsp_id,
ISIS_SYS_ID_LEN + 2);
return 0;
}
isis_snmp_update_worker_b(
adj->circuit, ISIS_TRAP_ADJ_STATE_CHANGE,
isis_snmp_trap_data_var_pdu_lsp_id,
- ARRAY_SIZE(isis_snmp_trap_data_var_pdu_lsp_id), STRING, lsp_id,
+ array_size(isis_snmp_trap_data_var_pdu_lsp_id), STRING, lsp_id,
ISIS_SYS_ID_LEN + 2, isis_snmp_trap_data_var_adj_state,
- ARRAY_SIZE(isis_snmp_trap_data_var_adj_state), INTEGER, &val,
+ array_size(isis_snmp_trap_data_var_adj_state), INTEGER, &val,
sizeof(val));
return 0;
}
/* Put in trap value */
snmp_varlist_add_variable(¬ification_vars, isis_snmp_trap_var,
- ARRAY_SIZE(isis_snmp_trap_var), ASN_OBJECT_ID,
+ array_size(isis_snmp_trap_var), ASN_OBJECT_ID,
(uint8_t *)&isis_snmp_trap_val_lsp_error,
sizeof(isis_snmp_trap_val_lsp_error));
snmp_varlist_add_variable(
¬ification_vars, isis_snmp_trap_data_var_sys_level_index,
- ARRAY_SIZE(isis_snmp_trap_data_var_sys_level_index), INTEGER,
+ array_size(isis_snmp_trap_data_var_sys_level_index), INTEGER,
(uint8_t *)&val, sizeof(val));
snmp_varlist_add_variable(
¬ification_vars, isis_snmp_trap_data_var_pdu_lsp_id,
- ARRAY_SIZE(isis_snmp_trap_data_var_pdu_lsp_id), STRING, lsp_id,
+ array_size(isis_snmp_trap_data_var_pdu_lsp_id), STRING, lsp_id,
ISIS_SYS_ID_LEN + 2);
/* Prepare data */
snmp_varlist_add_variable(
¬ification_vars, isis_snmp_trap_data_var_circ_if_index,
- ARRAY_SIZE(isis_snmp_trap_data_var_circ_if_index), UNSIGNED32,
+ array_size(isis_snmp_trap_data_var_circ_if_index), UNSIGNED32,
(uint8_t *)&val, sizeof(val));
/* Prepare data */
snmp_varlist_add_variable(
¬ification_vars, isis_snmp_trap_data_var_pdu_fragment,
- ARRAY_SIZE(isis_snmp_trap_data_var_pdu_fragment), STRING,
+ array_size(isis_snmp_trap_data_var_pdu_fragment), STRING,
raw_pdu, raw_pdu_len);
/* Prepare data */
snmp_varlist_add_variable(
¬ification_vars, isis_snmp_trap_data_var_error_offset,
- ARRAY_SIZE(isis_snmp_trap_data_var_error_offset), UNSIGNED32,
+ array_size(isis_snmp_trap_data_var_error_offset), UNSIGNED32,
(uint8_t *)&val, sizeof(val));
/* Prepare data */
snmp_varlist_add_variable(
¬ification_vars, isis_snmp_trap_data_var_error_tlv_type,
- ARRAY_SIZE(isis_snmp_trap_data_var_error_tlv_type), UNSIGNED32,
+ array_size(isis_snmp_trap_data_var_error_tlv_type), UNSIGNED32,
(uint8_t *)&val, sizeof(val));
send_v2trap(notification_vars);
#include "spf_backoff.h"
#include "srcdest_table.h"
#include "vrf.h"
+#include "lib/json.h"
#include "isis_errors.h"
#include "isis_constants.h"
#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"
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;
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);
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);
route_table_finish(spftree->route_table);
route_table_finish(spftree->route_table_backup);
+}
+
+void isis_spftree_del(struct isis_spftree *spftree)
+{
+ _isis_spftree_del(spftree);
+
spftree->route_table = NULL;
XFREE(MTYPE_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)
{
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);
}
}
}
#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 */
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);
}
#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));
#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);
#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,
#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 */
#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 */
&& !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)
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,
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,
&& !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");
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;
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;
}
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;
}
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,
#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 */
}
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",
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);
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;
}
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)) {
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) &&
+ !flex_algo_get_state(spftree->area->flex_algos,
+ spftree->algorithm)) {
+ 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
*/
}
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);
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;
}
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);
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)) {
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);
}
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;
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);
}
}
" [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"
#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;
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));
}
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;
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;
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];
+
+ if (!json)
+ 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];
+
+ if (!json)
+ 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];
+
+ if (!json)
+ 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);
}
}
}
" [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;
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;
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;
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);
#define _ZEBRA_ISIS_SPF_H
#include "isisd/isis_lfa.h"
+#include "lib/json.h"
struct isis_spftree;
#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,
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,
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,
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);
* @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;
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(
* @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);
}
struct isis_prefix_sid *psid)
{
/* Set SID algorithm. */
- psid->algorithm = SR_ALGORITHM_SPF;
+ psid->algorithm = pcfg->algorithm;
/* Set SID flags. */
psid->flags = 0;
*/
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);
* 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;
}
* @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));
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. */
}
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)) {
}
for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS;
level++)
- show_node(vty, area, level);
+ show_node(vty, area, level, algorithm);
}
}
/* 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 */
/* Backpointer to IS-IS area. */
struct isis_area *area;
+
+ /* SR Algorithm number */
+ uint8_t algorithm;
};
/* Per-area IS-IS Segment Routing Data Base (SRDB). */
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);
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,
} 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(
ext->status = 0;
}
+ isis_link_params_update_asla(circuit, ifp);
+
return;
}
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;
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];
}
{
struct ls_edge *edge;
struct ls_standard *std;
- uint64_t key = 0;
+ struct ls_edge_key key;
/* Check parameters */
if (!ted || !attr)
std = &attr->standard;
/* Compute keys in function of local address (IPv4/v6) or identifier */
- if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR))
- key = ((uint64_t)ntohl(std->local.s_addr)) & 0xffffffff;
- else if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR6))
- key = ((uint64_t)ntohl(std->local6.s6_addr32[2]) << 32
- | (uint64_t)ntohl(std->local6.s6_addr32[3]));
- else if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ID))
- key = ((uint64_t)std->remote_id << 32)
- | (((uint64_t)std->local_id) & 0xffffffff);
- else
- key = 0;
+ if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR)) {
+ key.family = AF_INET;
+ IPV4_ADDR_COPY(&key.k.addr, &std->local);
+ } else if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR6)) {
+ key.family = AF_INET6;
+ IPV6_ADDR_COPY(&key.k.addr6, &std->local6);
+ } else if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ID)) {
+ key.family = AF_LOCAL;
+ key.k.link_id = (((uint64_t)std->local_id) & 0xffffffff) |
+ ((uint64_t)std->remote_id << 32);
+ } else {
+ key.family = AF_UNSPEC;
+ }
/* Stop here if we don't got a valid key */
- if (key == 0)
+ if (key.family == AF_UNSPEC)
return NULL;
/* Get corresponding Edge by key from Link State Data Base */
}
if (CHECK_FLAG(edge->attributes->flags, LS_ATTR_LOCAL_ADDR))
- te_debug(" |- %s Edge (%" PRIu64
- ") from Extended Reach. %pI4",
- edge->status == NEW ? "Create" : "Found", edge->key,
- &attr->standard.local);
+ te_debug(" |- %s Edge (%pI4) from Extended Reach. %pI4",
+ edge->status == NEW ? "Create" : "Found",
+ &edge->key.k.addr, &attr->standard.local);
else if (CHECK_FLAG(edge->attributes->flags, LS_ATTR_LOCAL_ADDR6))
- te_debug(" |- %s Edge (%" PRIu64
- ") from Extended Reach. %pI6",
- edge->status == NEW ? "Create" : "Found", edge->key,
- &attr->standard.local6);
+ te_debug(" |- %s Edge (%pI6) from Extended Reach. %pI6",
+ edge->status == NEW ? "Create" : "Found",
+ &edge->key.k.addr6, &attr->standard.local6);
else
te_debug(" |- %s Edge (%" PRIu64 ")",
- edge->status == NEW ? "Create" : "Found", edge->key);
+ edge->status == NEW ? "Create" : "Found",
+ edge->key.k.link_id);
return edge;
}
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)
}
/* Try to update remote Link from remote address or reachability ID */
- te_debug(" |- Link Edge (%" PRIu64 ") to destination vertex (%s)",
- edge->key, print_sys_hostname(id));
+ if (edge->key.family == AF_INET)
+ te_debug(" |- Link Edge (%pI4) to destination vertex (%s)",
+ &edge->key.k.addr, print_sys_hostname(id));
+ else if (edge->key.family == AF_INET6)
+ te_debug(" |- Link Edge (%pI6) to destination vertex (%s)",
+ &edge->key.k.addr6, print_sys_hostname(id));
+ else if (edge->key.family == AF_LOCAL)
+ te_debug(" |- Link Edge (%" PRIu64
+ ") to destination vertex (%s)",
+ edge->key.k.link_id, print_sys_hostname(id));
+ else
+ te_debug(
+ " |- Link Edge (Unknown) to destination vertex (%s)",
+ print_sys_hostname(id));
+
dst = ls_find_edge_by_destination(args->ted, edge->attributes);
if (dst) {
/* Attach remote link if not set */
}
if (!std)
prefix_copy(&p, prefix);
- else
+ else {
+ /* Remove old subnet if any before prefix adjustment */
+ subnet = ls_find_subnet(args->ted, prefix);
+ if (subnet) {
+ if (args->export) {
+ subnet->status = DELETE;
+ isis_te_export(LS_MSG_TYPE_PREFIX, subnet);
+ }
+ te_debug(" |- Remove subnet with prefix %pFX",
+ &subnet->key);
+ ls_subnet_del_all(args->ted, subnet);
+ }
te_debug(" |- Adjust prefix %pFX with local address to: %pFX",
prefix, &p);
+ }
/* 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)
return LSP_ITER_CONTINUE;
}
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;
}
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)
struct ls_vertex *vertex;
struct ls_edge *edge;
struct ls_subnet *subnet;
- uint64_t key;
+ struct ls_edge_key key;
bool detail = false;
bool uj = use_json(argc, argv);
json_object *json = NULL;
return CMD_WARNING_CONFIG_FAILED;
}
/* Get the Edge from the Link State Database */
- key = ((uint64_t)ntohl(ip_addr.s_addr)) & 0xffffffff;
+ key.family = AF_INET;
+ IPV4_ADDR_COPY(&key.k.addr, &ip_addr);
edge = ls_find_edge_by_key(ted, key);
if (!edge) {
vty_out(vty, "No edge found for ID %pI4\n",
return CMD_WARNING_CONFIG_FAILED;
}
/* Get the Edge from the Link State Database */
- key = (uint64_t)ntohl(ip6_addr.s6_addr32[3])
- | ((uint64_t)ntohl(ip6_addr.s6_addr32[2]) << 32);
+ key.family = AF_INET6;
+ IPV6_ADDR_COPY(&key.k.addr6, &ip6_addr);
edge = ls_find_edge_by_key(ted, key);
if (!edge) {
vty_out(vty, "No edge found for ID %pI6\n",
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);
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);
#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,
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);
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;
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);
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));
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,
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)) {
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(
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'
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)
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;
}
}
+ 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;
}
/* 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);
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);
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);
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);
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);
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);
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);
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);
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++)
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);
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);
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);
/* 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);
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);
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);
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);
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);
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);
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);
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;
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;
&& 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;
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;
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);
&& 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);
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);
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)
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)
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)
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)
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();
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",
} 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");
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();
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 {
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);
}
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;
}
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);
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 */
}
}
+#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);
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);
/* 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);
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 */
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;
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);
}
}
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);
}
}
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
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,
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;
#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);
enum isis_threeway_state {
ISIS_THREEWAY_DOWN = 2,
ISIS_THREEWAY_INITIALIZING = 1,
- ISIS_THREEWAY_UP = 0
+ ISIS_THREEWAY_UP = 0,
};
struct isis_threeway_adj {
#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;
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 {
ISIS_CONTEXT_SUBTLV_NE_REACH,
ISIS_CONTEXT_SUBTLV_IP_REACH,
ISIS_CONTEXT_SUBTLV_IPV6_REACH,
- ISIS_CONTEXT_MAX
+ ISIS_CONTEXT_MAX,
};
struct isis_subtlvs {
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 */
/* 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
/* 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) \
#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);
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,
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,
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,
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);
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);
}
*/
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));
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;
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) {
}
/* 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)
{
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));
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,
#include "zclient.h"
#include "vrf.h"
#include "spf_backoff.h"
+#include "flex_algo.h"
#include "lib/northbound_cli.h"
#include "bfd.h"
#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"
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);
}
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();
isis_area_invalidate_routes(area, area->is_type);
isis_area_verify_routes(area);
+#ifndef FABRICD
+ flex_algos_free(area->flex_algos);
+#endif /* ifndef FABRICD */
+
isis_sr_area_term(area);
isis_mpls_te_term(area);
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];
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);
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
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);
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;
}
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();
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);
}
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");
vty_out(vty, " RX counters per PDU type:\n");
pdu_counter_print(vty, " ", area->pdu_rx_counters);
+ vty_out(vty, " Drop counters per PDU type:\n");
+ pdu_counter_print(vty, " ", area->pdu_drop_counters);
+
vty_out(vty, " Advertise high metrics: %s\n",
area->advertise_high_metrics ? "Enabled" : "Disabled");
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 */
}
}
}
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,
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);
}
}
+#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[tree][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]));
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++;
}
}
.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)
{
.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)
{
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();
}
#include "isis_lfa.h"
#include "qobj.h"
#include "ldp_sync.h"
+#include "iso.h"
DECLARE_MGROUP(ISISD);
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 */
/* 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 */
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 */
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]
pdu_counter_t pdu_tx_counters;
pdu_counter_t pdu_rx_counters;
+ pdu_counter_t pdu_drop_counters;
uint64_t lsp_rxmt_count;
/* Area counters */
endif
noinst_HEADERS += \
+ isisd/isis_affinitymap.h \
isisd/isis_adjacency.h \
isisd/isis_bfd.h \
isisd/isis_circuit.h \
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 \
# end
LIBISIS_SOURCES = \
+ isisd/isis_affinitymap.c \
isisd/isis_adjacency.c \
isisd/isis_bfd.c \
isisd/isis_circuit.c \
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 \
#include "jhash.h"
#include "hook.h"
#include "lib_errors.h"
+#include "mgmt_be_client.h"
+#include "mgmt_fe_client.h"
#include "northbound_cli.h"
#include "network.h"
#include "routemap.h"
while (fgets(vty->buf, VTY_BUFSIZ, fp)) {
++(*line_num);
+ if (vty_log_commands) {
+ int len = strlen(vty->buf);
+
+ /* now log the command */
+ zlog_notice("config-from-file# %.*s", len ? len - 1 : 0,
+ vty->buf);
+ }
+
ret = command_config_read_one_line(vty, NULL, *line_num, 0);
if (ret != CMD_SUCCESS && ret != CMD_WARNING
void cmd_show_lib_debugs(struct vty *vty)
{
route_map_show_debug(vty);
+ mgmt_debug_be_client_show_debug(vty);
+ mgmt_debug_fe_client_show_debug(vty);
}
void install_default(enum node_type node)
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. */
#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
*
* @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;
*/
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
fd = open(filename, O_RDONLY | O_NOCTTY);
if (fd < 0 || fstat(fd, &st)) {
PyErr_SetFromErrnoWithFilename(PyExc_OSError, filename);
- close(fd);
+ if (fd > 0)
+ close(fd);
goto out;
}
w->len = st.st_size;
--- /dev/null
+// 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_DATABASE, "Flex-Algo database");
+DEFINE_MTYPE_STATIC(LIB, FLEX_ALGO, "Flex-Algo algorithm information");
+
+static void _flex_algo_delete(struct flex_algos *flex_algos,
+ struct flex_algo *fa);
+
+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_DATABASE, sizeof(struct flex_algos));
+ flex_algos->flex_algos = list_new();
+ flex_algos->allocator = allocator;
+ flex_algos->releaser = releaser;
+ return flex_algos;
+}
+
+void flex_algos_free(struct flex_algos *flex_algos)
+{
+ struct listnode *node, *nnode;
+ struct flex_algo *fa;
+
+ for (ALL_LIST_ELEMENTS(flex_algos->flex_algos, node, nnode, fa))
+ _flex_algo_delete(flex_algos, fa);
+ list_delete(&flex_algos->flex_algos);
+ XFREE(MTYPE_FLEX_ALGO_DATABASE, 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(struct flex_algo));
+ 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;
+}
+
+static void _flex_algo_delete(struct flex_algos *flex_algos,
+ struct flex_algo *fa)
+{
+ 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);
+}
+
+
+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;
+ _flex_algo_delete(flex_algos, 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 (fa1->exclude_srlg != fa2->exclude_srlg)
+ return false;
+ if (fa1->flags != fa2->flags)
+ return false;
+ if (fa1->unsupported_subtlv != fa2->unsupported_subtlv)
+ 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;
+}
+
+/**
+ * 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;
+}
+
+bool flex_algo_get_state(struct flex_algos *flex_algos, uint8_t algorithm)
+{
+ struct flex_algo *fa = flex_algo_lookup(flex_algos, algorithm);
+
+ if (!fa)
+ return false;
+
+ return fa->state;
+}
+
+void flex_algo_set_state(struct flex_algos *flex_algos, uint8_t algorithm,
+ bool state)
+{
+ struct flex_algo *fa = flex_algo_lookup(flex_algos, algorithm);
+
+ if (!fa)
+ return;
+
+ fa->state = state;
+}
--- /dev/null
+// 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;
+
+ /* True if the Algorithm is locally enabled (ie. a definition has been
+ * found and is supported).
+ */
+ bool state;
+
+ /*
+ * 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);
+void flex_algos_free(struct flex_algos *flex_algos);
+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);
+
+bool flex_algo_get_state(struct flex_algos *flex_algos, uint8_t algorithm);
+
+void flex_algo_set_state(struct flex_algos *flex_algos, uint8_t algorithm,
+ bool state);
+#endif /* _FRR_FLEX_ALGO_H */
+++ /dev/null
-// SPDX-License-Identifier: GPL-2.0-or-later
-/* Getopt for GNU.
- * NOTE: getopt is now part of the C library, so if you don't know what
- * "Keep this file name-space clean" means, talk to drepper@gnu.org
- * before changing it!
- *
- * Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98
- * Free Software Foundation, Inc.
- *
- * NOTE: The canonical source of this file is maintained with the GNU C Library.
- * Bugs can be reported to bug-glibc@gnu.org.
- */
-
-/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>.
- Ditto for AIX 3.2 and <stdlib.h>. */
-#ifndef _NO_PROTO
-# define _NO_PROTO
-#endif
-
-#include <zebra.h>
-
-#if !defined __STDC__ || !__STDC__
-/* This is a separate conditional since some stdc systems
- reject `defined (const)'. */
-#ifndef const
-# define const
-#endif
-#endif
-
-#include <stdio.h>
-
-/* Comment out all this code if we are using the GNU C Library, and are not
- actually compiling the library itself. This code is part of the GNU C
- Library, but also included in many other GNU distributions. Compiling
- and linking in this code is a waste when using the GNU C library
- (especially if it is a shared library). Rather than having every GNU
- program understand `configure --with-gnu-libc' and omit the object files,
- it is simpler to just do this in the source for each such file. */
-
-#define GETOPT_INTERFACE_VERSION 2
-#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2
-#include <gnu-versions.h>
-#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
-# define ELIDE_CODE
-#endif
-#endif
-
-#ifndef ELIDE_CODE
-
-
-/* This needs to come after some library #include
- to get __GNU_LIBRARY__ defined. */
-#ifdef __GNU_LIBRARY__
-/* Don't include stdlib.h for non-GNU C libraries because some of them
- contain conflicting prototypes for getopt. */
-#include <stdlib.h>
-#include <unistd.h>
-#endif /* GNU C library. */
-
-#ifdef VMS
-#include <unixlib.h>
-#if HAVE_STRING_H - 0
-#include <string.h>
-#endif
-#endif
-
-#ifndef _
-/* This is for other GNU distributions with internationalized messages.
- When compiling libc, the _ macro is predefined. */
-#ifdef HAVE_LIBINTL_H
-#include <libintl.h>
-# define _(msgid) gettext (msgid)
-#else
-# define _(msgid) (msgid)
-#endif
-#endif
-
-/* This version of `getopt' appears to the caller like standard Unix `getopt'
- but it behaves differently for the user, since it allows the user
- to intersperse the options with the other arguments.
-
- As `getopt' works, it permutes the elements of ARGV so that,
- when it is done, all the options precede everything else. Thus
- all application programs are extended to handle flexible argument order.
-
- Setting the environment variable POSIXLY_CORRECT disables permutation.
- Then the behavior is completely standard.
-
- GNU application programs can use a third alternative mode in which
- they can distinguish the relative order of options and other arguments. */
-
-#include "getopt.h"
-
-/* For communication from `getopt' to the caller.
- When `getopt' finds an option that takes an argument,
- the argument value is returned here.
- Also, when `ordering' is RETURN_IN_ORDER,
- each non-option ARGV-element is returned here. */
-
-char *optarg = NULL;
-
-/* Index in ARGV of the next element to be scanned.
- This is used for communication to and from the caller
- and for communication between successive calls to `getopt'.
-
- On entry to `getopt', zero means this is the first call; initialize.
-
- When `getopt' returns -1, this is the index of the first of the
- non-option elements that the caller should itself scan.
-
- Otherwise, `optind' communicates from one call to the next
- how much of ARGV has been scanned so far. */
-
-/* 1003.2 says this must be 1 before any call. */
-int optind = 1;
-
-/* Formerly, initialization of getopt depended on optind==0, which
- causes problems with re-calling getopt as programs generally don't
- know that. */
-
-int __getopt_initialized = 0;
-
-/* The next char to be scanned in the option-element
- in which the last option character we returned was found.
- This allows us to pick up the scan where we left off.
-
- If this is zero, or a null string, it means resume the scan
- by advancing to the next ARGV-element. */
-
-static char *nextchar;
-
-/* Callers store zero here to inhibit the error message
- for unrecognized options. */
-
-int opterr = 1;
-
-/* Set to an option character which was unrecognized.
- This must be initialized on some systems to avoid linking in the
- system's own getopt implementation. */
-
-int optopt = '?';
-
-/* Describe how to deal with options that follow non-option ARGV-elements.
-
- If the caller did not specify anything,
- the default is REQUIRE_ORDER if the environment variable
- POSIXLY_CORRECT is defined, PERMUTE otherwise.
-
- REQUIRE_ORDER means don't recognize them as options;
- stop option processing when the first non-option is seen.
- This is what Unix does.
- This mode of operation is selected by either setting the environment
- variable POSIXLY_CORRECT, or using `+' as the first character
- of the list of option characters.
-
- PERMUTE is the default. We permute the contents of ARGV as we scan,
- so that eventually all the non-options are at the end. This allows options
- to be given in any order, even with programs that were not written to
- expect this.
-
- RETURN_IN_ORDER is an option available to programs that were written
- to expect options and other ARGV-elements in any order and that care about
- the ordering of the two. We describe each non-option ARGV-element
- as if it were the argument of an option with character code 1.
- Using `-' as the first character of the list of option characters
- selects this mode of operation.
-
- The special argument `--' forces an end of option-scanning regardless
- of the value of `ordering'. In the case of RETURN_IN_ORDER, only
- `--' can cause `getopt' to return -1 with `optind' != ARGC. */
-
-static enum { REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER } ordering;
-
-/* Value of POSIXLY_CORRECT environment variable. */
-static char *posixly_correct;
-
-#ifdef __GNU_LIBRARY__
-/* We want to avoid inclusion of string.h with non-GNU libraries
- because there are many ways it can cause trouble.
- On some systems, it contains special magic macros that don't work
- in GCC. */
-#include <string.h>
-# define my_index strchr
-#else
-
-#if HAVE_STRING_H
-#include <string.h>
-#else
-#include <strings.h>
-#endif
-
-/* Avoid depending on library functions or files
- whose names are inconsistent. */
-
-#ifndef getenv
-extern char *getenv(const char *);
-#endif
-
-static char *my_index(const char *str, int chr)
-{
- while (*str) {
- if (*str == chr)
- return (char *)str;
- str++;
- }
- return 0;
-}
-
-/* If using GCC, we can safely declare strlen this way.
- If not using GCC, it is ok not to declare it. */
-#ifdef __GNUC__
-/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h.
- That was relevant to code that was here before. */
-#if (!defined __STDC__ || !__STDC__) && !defined strlen
-/* gcc with -traditional declares the built-in strlen to return int,
- and has done so at least since version 2.4.5. -- rms. */
-extern int strlen(const char *);
-#endif /* not __STDC__ */
-#endif /* __GNUC__ */
-
-#endif /* not __GNU_LIBRARY__ */
-
-/* Handle permutation of arguments. */
-
-/* Describe the part of ARGV that contains non-options that have
- been skipped. `first_nonopt' is the index in ARGV of the first of them;
- `last_nonopt' is the index after the last of them. */
-
-static int first_nonopt;
-static int last_nonopt;
-
-#ifdef _LIBC
-/* Bash 2.0 gives us an environment variable containing flags
- indicating ARGV elements that should not be considered arguments. */
-
-/* Defined in getopt_init.c */
-extern char *__getopt_nonoption_flags;
-
-static int nonoption_flags_max_len;
-static int nonoption_flags_len;
-
-static int original_argc;
-static char *const *original_argv;
-
-/* Make sure the environment variable bash 2.0 puts in the environment
- is valid for the getopt call we must make sure that the ARGV passed
- to getopt is that one passed to the process. */
-static void __attribute__((unused))
-store_args_and_env(int argc, char *const *argv)
-{
- /* XXX This is no good solution. We should rather copy the args so
- that we can compare them later. But we must not use malloc(3). */
- original_argc = argc;
- original_argv = argv;
-}
-#ifdef text_set_element
-text_set_element(__libc_subinit, store_args_and_env);
-#endif /* text_set_element */
-
-#define SWAP_FLAGS(ch1, ch2) \
- if (nonoption_flags_len > 0) { \
- char __tmp = __getopt_nonoption_flags[ch1]; \
- __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \
- __getopt_nonoption_flags[ch2] = __tmp; \
- }
-#else /* !_LIBC */
-# define SWAP_FLAGS(ch1, ch2)
-#endif /* _LIBC */
-
-/* Exchange two adjacent subsequences of ARGV.
- One subsequence is elements [first_nonopt,last_nonopt)
- which contains all the non-options that have been skipped so far.
- The other is elements [last_nonopt,optind), which contains all
- the options processed since those non-options were skipped.
-
- `first_nonopt' and `last_nonopt' are relocated so that they describe
- the new indices of the non-options in ARGV after they are moved. */
-
-#if defined __STDC__ && __STDC__
-static void exchange(char **);
-#endif
-
-static void exchange(argv) char **argv;
-{
- int bottom = first_nonopt;
- int middle = last_nonopt;
- int top = optind;
- char *tem;
-
-/* Exchange the shorter segment with the far end of the longer segment.
- That puts the shorter segment into the right place.
- It leaves the longer segment in the right place overall,
- but it consists of two parts that need to be swapped next. */
-
-#ifdef _LIBC
- /* First make sure the handling of the `__getopt_nonoption_flags'
- string can work normally. Our top argument must be in the range
- of the string. */
- if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len) {
- /* We must extend the array. The user plays games with us and
- presents new arguments. */
- char *new_str = malloc(top + 1);
- if (new_str == NULL)
- nonoption_flags_len = nonoption_flags_max_len = 0;
- else {
- memset(__mempcpy(new_str, __getopt_nonoption_flags,
- nonoption_flags_max_len),
- '\0', top + 1 - nonoption_flags_max_len);
- nonoption_flags_max_len = top + 1;
- __getopt_nonoption_flags = new_str;
- }
- }
-#endif
-
- while (top > middle && middle > bottom) {
- if (top - middle > middle - bottom) {
- /* Bottom segment is the short one. */
- int len = middle - bottom;
- register int i;
-
- /* Swap it with the top part of the top segment. */
- for (i = 0; i < len; i++) {
- tem = argv[bottom + i];
- argv[bottom + i] =
- argv[top - (middle - bottom) + i];
- argv[top - (middle - bottom) + i] = tem;
- SWAP_FLAGS(bottom + i,
- top - (middle - bottom) + i);
- }
- /* Exclude the moved bottom segment from further
- * swapping. */
- top -= len;
- } else {
- /* Top segment is the short one. */
- int len = top - middle;
- register int i;
-
- /* Swap it with the bottom part of the bottom segment.
- */
- for (i = 0; i < len; i++) {
- tem = argv[bottom + i];
- argv[bottom + i] = argv[middle + i];
- argv[middle + i] = tem;
- SWAP_FLAGS(bottom + i, middle + i);
- }
- /* Exclude the moved top segment from further swapping.
- */
- bottom += len;
- }
- }
-
- /* Update records for the slots the non-options now occupy. */
-
- first_nonopt += (optind - last_nonopt);
- last_nonopt = optind;
-}
-
-/* Initialize the internal data when the first call is made. */
-
-#if defined __STDC__ && __STDC__
-static const char *_getopt_initialize(int, char *const *, const char *);
-#endif
-static const char *_getopt_initialize(argc, argv, optstring) int argc;
-char *const *argv;
-const char *optstring;
-{
- /* Start processing options with ARGV-element 1 (since ARGV-element 0
- is the program name); the sequence of previously skipped
- non-option ARGV-elements is empty. */
-
- first_nonopt = last_nonopt = optind;
-
- nextchar = NULL;
-
- posixly_correct = getenv("POSIXLY_CORRECT");
-
- /* Determine how to handle the ordering of options and nonoptions. */
-
- if (optstring[0] == '-') {
- ordering = RETURN_IN_ORDER;
- ++optstring;
- } else if (optstring[0] == '+') {
- ordering = REQUIRE_ORDER;
- ++optstring;
- } else if (posixly_correct != NULL)
- ordering = REQUIRE_ORDER;
- else
- ordering = PERMUTE;
-
-#ifdef _LIBC
- if (posixly_correct == NULL && argc == original_argc
- && argv == original_argv) {
- if (nonoption_flags_max_len == 0) {
- if (__getopt_nonoption_flags == NULL
- || __getopt_nonoption_flags[0] == '\0')
- nonoption_flags_max_len = -1;
- else {
- const char *orig_str = __getopt_nonoption_flags;
- int len = nonoption_flags_max_len =
- strlen(orig_str);
- if (nonoption_flags_max_len < argc)
- nonoption_flags_max_len = argc;
- __getopt_nonoption_flags =
- (char *)malloc(nonoption_flags_max_len);
- if (__getopt_nonoption_flags == NULL)
- nonoption_flags_max_len = -1;
- else
- memset(__mempcpy(
- __getopt_nonoption_flags,
- orig_str, len),
- '\0',
- nonoption_flags_max_len - len);
- }
- }
- nonoption_flags_len = nonoption_flags_max_len;
- } else
- nonoption_flags_len = 0;
-#endif
-
- return optstring;
-}
-
-/* Scan elements of ARGV (whose length is ARGC) for option characters
- given in OPTSTRING.
-
- If an element of ARGV starts with '-', and is not exactly "-" or "--",
- then it is an option element. The characters of this element
- (aside from the initial '-') are option characters. If `getopt'
- is called repeatedly, it returns successively each of the option characters
- from each of the option elements.
-
- If `getopt' finds another option character, it returns that character,
- updating `optind' and `nextchar' so that the next call to `getopt' can
- resume the scan with the following option character or ARGV-element.
-
- If there are no more option characters, `getopt' returns -1.
- Then `optind' is the index in ARGV of the first ARGV-element
- that is not an option. (The ARGV-elements have been permuted
- so that those that are not options now come last.)
-
- OPTSTRING is a string containing the legitimate option characters.
- If an option character is seen that is not listed in OPTSTRING,
- return '?' after printing an error message. If you set `opterr' to
- zero, the error message is suppressed but we still return '?'.
-
- If a char in OPTSTRING is followed by a colon, that means it wants an arg,
- so the following text in the same ARGV-element, or the text of the following
- ARGV-element, is returned in `optarg'. Two colons mean an option that
- wants an optional arg; if there is text in the current ARGV-element,
- it is returned in `optarg', otherwise `optarg' is set to zero.
-
- If OPTSTRING starts with `-' or `+', it requests different methods of
- handling the non-option ARGV-elements.
- See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
-
- Long-named options begin with `--' instead of `-'.
- Their names may be abbreviated as long as the abbreviation is unique
- or is an exact match for some defined option. If they have an
- argument, it follows the option name in the same ARGV-element, separated
- from the option name by a `=', or else the in next ARGV-element.
- When `getopt' finds a long-named option, it returns 0 if that option's
- `flag' field is nonzero, the value of the option's `val' field
- if the `flag' field is zero.
-
- The elements of ARGV aren't really const, because we permute them.
- But we pretend they're const in the prototype to be compatible
- with other systems.
-
- LONGOPTS is a vector of `struct option' terminated by an
- element containing a name which is zero.
-
- LONGIND returns the index in LONGOPT of the long-named option found.
- It is only valid when a long-named option has been found by the most
- recent call.
-
- If LONG_ONLY is nonzero, '-' as well as '--' can introduce
- long-named options. */
-
-int _getopt_internal(argc, argv, optstring, longopts, longind,
- long_only) int argc;
-char *const *argv;
-const char *optstring;
-const struct option *longopts;
-int *longind;
-int long_only;
-{
- optarg = NULL;
-
- if (optind == 0 || !__getopt_initialized) {
- if (optind == 0)
- optind = 1; /* Don't scan ARGV[0], the program name. */
- optstring = _getopt_initialize(argc, argv, optstring);
- __getopt_initialized = 1;
- }
-
-/* Test whether ARGV[optind] points to a non-option argument.
- Either it does not have option syntax, or there is an environment flag
- from the shell indicating it is not an option. The later information
- is only used when the used in the GNU libc. */
-#ifdef _LIBC
-#define NONOPTION_P \
- (argv[optind][0] != '-' || argv[optind][1] == '\0' \
- || (optind < nonoption_flags_len \
- && __getopt_nonoption_flags[optind] == '1'))
-#else
-# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0')
-#endif
-
- if (nextchar == NULL || *nextchar == '\0') {
- /* Advance to the next ARGV-element. */
-
- /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has
- been
- moved back by the user (who may also have changed the
- arguments). */
- if (last_nonopt > optind)
- last_nonopt = optind;
- if (first_nonopt > optind)
- first_nonopt = optind;
-
- if (ordering == PERMUTE) {
- /* If we have just processed some options following some
- non-options,
- exchange them so that the options come first. */
-
- if (first_nonopt != last_nonopt
- && last_nonopt != optind)
- exchange((char **)argv);
- else if (last_nonopt != optind)
- first_nonopt = optind;
-
- /* Skip any additional non-options
- and extend the range of non-options previously
- skipped. */
-
- while (optind < argc && NONOPTION_P)
- optind++;
- last_nonopt = optind;
- }
-
- /* The special ARGV-element `--' means premature end of options.
- Skip it like a null option,
- then exchange with previous non-options as if it were an
- option,
- then skip everything else like a non-option. */
-
- if (optind != argc && !strcmp(argv[optind], "--")) {
- optind++;
-
- if (first_nonopt != last_nonopt
- && last_nonopt != optind)
- exchange((char **)argv);
- else if (first_nonopt == last_nonopt)
- first_nonopt = optind;
- last_nonopt = argc;
-
- optind = argc;
- }
-
- /* If we have done all the ARGV-elements, stop the scan
- and back over any non-options that we skipped and permuted.
- */
-
- if (optind == argc) {
- /* Set the next-arg-index to point at the non-options
- that we previously skipped, so the caller will digest
- them. */
- if (first_nonopt != last_nonopt)
- optind = first_nonopt;
- return -1;
- }
-
- /* If we have come to a non-option and did not permute it,
- either stop the scan or describe it to the caller and pass it
- by. */
-
- if (NONOPTION_P) {
- if (ordering == REQUIRE_ORDER)
- return -1;
- optarg = argv[optind++];
- return 1;
- }
-
- /* We have found another option-ARGV-element.
- Skip the initial punctuation. */
-
- nextchar = (argv[optind] + 1
- + (longopts != NULL && argv[optind][1] == '-'));
- }
-
- /* Decode the current option-ARGV-element. */
-
- /* Check whether the ARGV-element is a long option.
-
- If long_only and the ARGV-element has the form "-f", where f is
- a valid short option, don't consider it an abbreviated form of
- a long option that starts with f. Otherwise there would be no
- way to give the -f short option.
-
- On the other hand, if there's a long option "fubar" and
- the ARGV-element is "-fu", do consider that an abbreviation of
- the long option, just like "--fu", and not "-f" with arg "u".
-
- This distinction seems to be the most useful approach. */
-
- if (longopts != NULL
- && (argv[optind][1] == '-'
- || (long_only && (argv[optind][2]
- || !my_index(optstring, argv[optind][1]))))) {
- char *nameend;
- const struct option *p;
- const struct option *pfound = NULL;
- int exact = 0;
- int ambig = 0;
- int indfound = -1;
- int option_index;
-
- for (nameend = nextchar; *nameend && *nameend != '='; nameend++)
- /* Do nothing. */;
-
- /* Test all long options for either exact match
- or abbreviated matches. */
- for (p = longopts, option_index = 0; p->name;
- p++, option_index++)
- if (!strncmp(p->name, nextchar, nameend - nextchar)) {
- if ((unsigned int)(nameend - nextchar)
- == (unsigned int)strlen(p->name)) {
- /* Exact match found. */
- pfound = p;
- indfound = option_index;
- exact = 1;
- break;
- } else if (pfound == NULL) {
- /* First nonexact match found. */
- pfound = p;
- indfound = option_index;
- } else
- /* Second or later nonexact match found.
- */
- ambig = 1;
- }
-
- if (ambig && !exact) {
- if (opterr)
- fprintf(stderr,
- _("%s: option `%s' is ambiguous\n"),
- argv[0], argv[optind]);
- nextchar += strlen(nextchar);
- optind++;
- optopt = 0;
- return '?';
- }
-
- if (pfound != NULL) {
- option_index = indfound;
- optind++;
- if (*nameend) {
- /* Don't test has_arg with >, because some C
- compilers don't
- allow it to be used on enums. */
- if (pfound->has_arg)
- optarg = nameend + 1;
- else {
- if (opterr) {
- if (argv[optind - 1][1] == '-')
- /* --option */
- fprintf(stderr,
- _("%s: option `--%s' doesn't allow an argument\n"),
- argv[0],
- pfound->name);
- else
- /* +option or -option */
- fprintf(stderr,
- _("%s: option `%c%s' doesn't allow an argument\n"),
- argv[0],
- argv[optind - 1]
- [0],
- pfound->name);
- }
-
- nextchar += strlen(nextchar);
-
- optopt = pfound->val;
- return '?';
- }
- } else if (pfound->has_arg == 1) {
- if (optind < argc)
- optarg = argv[optind++];
- else {
- if (opterr)
- fprintf(stderr,
- _("%s: option `%s' requires an argument\n"),
- argv[0],
- argv[optind - 1]);
- nextchar += strlen(nextchar);
- optopt = pfound->val;
- return optstring[0] == ':' ? ':' : '?';
- }
- }
- nextchar += strlen(nextchar);
- if (longind != NULL)
- *longind = option_index;
- if (pfound->flag) {
- *(pfound->flag) = pfound->val;
- return 0;
- }
- return pfound->val;
- }
-
- /* Can't find it as a long option. If this is not
- getopt_long_only,
- or the option starts with '--' or is not a valid short
- option, then it's an error.
- Otherwise interpret it as a short option. */
- if (!long_only || argv[optind][1] == '-'
- || my_index(optstring, *nextchar) == NULL) {
- if (opterr) {
- if (argv[optind][1] == '-')
- /* --option */
- fprintf(stderr,
- _("%s: unrecognized option `--%s'\n"),
- argv[0], nextchar);
- else
- /* +option or -option */
- fprintf(stderr,
- _("%s: unrecognized option `%c%s'\n"),
- argv[0], argv[optind][0],
- nextchar);
- }
- nextchar = (char *)"";
- optind++;
- optopt = 0;
- return '?';
- }
- }
-
- /* Look at and handle the next short option-character. */
-
- {
- char c = *nextchar++;
- char *temp = my_index(optstring, c);
-
- /* Increment `optind' when we start to process its last
- * character. */
- if (*nextchar == '\0')
- ++optind;
-
- if (temp == NULL || c == ':') {
- if (opterr) {
- if (posixly_correct)
- /* 1003.2 specifies the format of this
- * message. */
- fprintf(stderr,
- _("%s: illegal option -- %c\n"),
- argv[0], c);
- else
- fprintf(stderr,
- _("%s: invalid option -- %c\n"),
- argv[0], c);
- }
- optopt = c;
- return '?';
- }
- /* Convenience. Treat POSIX -W foo same as long option --foo */
- if (temp[0] == 'W' && temp[1] == ';') {
- char *nameend;
- const struct option *p;
- const struct option *pfound = NULL;
- int exact = 0;
- int ambig = 0;
- int indfound = 0;
- int option_index;
-
- /* This is an option that requires an argument. */
- if (*nextchar != '\0') {
- optarg = nextchar;
- /* If we end this ARGV-element by taking the
- rest as an arg,
- we must advance to the next element now. */
- optind++;
- } else if (optind == argc) {
- if (opterr) {
- /* 1003.2 specifies the format of this
- * message. */
- fprintf(stderr,
- _("%s: option requires an argument -- %c\n"),
- argv[0], c);
- }
- optopt = c;
- if (optstring[0] == ':')
- c = ':';
- else
- c = '?';
- return c;
- } else
- /* We already incremented `optind' once;
- increment it again when taking next ARGV-elt
- as argument. */
- optarg = argv[optind++];
-
- /* optarg is now the argument, see if it's in the
- table of longopts. */
-
- for (nextchar = nameend = optarg;
- *nameend && *nameend != '='; nameend++)
- /* Do nothing. */;
-
- /* Test all long options for either exact match
- or abbreviated matches. */
- for (p = longopts, option_index = 0; p->name;
- p++, option_index++)
- if (!strncmp(p->name, nextchar,
- nameend - nextchar)) {
- if ((unsigned int)(nameend - nextchar)
- == strlen(p->name)) {
- /* Exact match found. */
- pfound = p;
- indfound = option_index;
- exact = 1;
- break;
- } else if (pfound == NULL) {
- /* First nonexact match found.
- */
- pfound = p;
- indfound = option_index;
- } else
- /* Second or later nonexact
- * match found. */
- ambig = 1;
- }
- if (ambig && !exact) {
- if (opterr)
- fprintf(stderr,
- _("%s: option `-W %s' is ambiguous\n"),
- argv[0], argv[optind]);
- nextchar += strlen(nextchar);
- optind++;
- return '?';
- }
- if (pfound != NULL) {
- option_index = indfound;
- if (*nameend) {
- /* Don't test has_arg with >, because
- some C compilers don't
- allow it to be used on enums. */
- if (pfound->has_arg)
- optarg = nameend + 1;
- else {
- if (opterr)
- fprintf(stderr, _("\
-%s: option `-W %s' doesn't allow an argument\n"),
- argv[0],
- pfound->name);
-
- nextchar += strlen(nextchar);
- return '?';
- }
- } else if (pfound->has_arg == 1) {
- if (optind < argc)
- optarg = argv[optind++];
- else {
- if (opterr)
- fprintf(stderr,
- _("%s: option `%s' requires an argument\n"),
- argv[0],
- argv[optind
- - 1]);
- nextchar += strlen(nextchar);
- return optstring[0] == ':'
- ? ':'
- : '?';
- }
- }
- nextchar += strlen(nextchar);
- if (longind != NULL)
- *longind = option_index;
- if (pfound->flag) {
- *(pfound->flag) = pfound->val;
- return 0;
- }
- return pfound->val;
- }
- nextchar = NULL;
- return 'W'; /* Let the application handle it. */
- }
- if (temp[1] == ':') {
- if (temp[2] == ':') {
- /* This is an option that accepts an argument
- * optionally. */
- if (*nextchar != '\0') {
- optarg = nextchar;
- optind++;
- } else
- optarg = NULL;
- nextchar = NULL;
- } else {
- /* This is an option that requires an argument.
- */
- if (*nextchar != '\0') {
- optarg = nextchar;
- /* If we end this ARGV-element by taking
- the rest as an arg,
- we must advance to the next element
- now. */
- optind++;
- } else if (optind == argc) {
- if (opterr) {
- /* 1003.2 specifies the format
- * of this message. */
- fprintf(stderr,
- _("%s: option requires an argument -- %c\n"),
- argv[0], c);
- }
- optopt = c;
- if (optstring[0] == ':')
- c = ':';
- else
- c = '?';
- } else
- /* We already incremented `optind' once;
- increment it again when taking next
- ARGV-elt as argument. */
- optarg = argv[optind++];
- nextchar = NULL;
- }
- }
- return c;
- }
-}
-
-#ifdef REALLY_NEED_PLAIN_GETOPT
-
-int getopt(argc, argv, optstring) int argc;
-char *const *argv;
-const char *optstring;
-{
- return _getopt_internal(argc, argv, optstring, (const struct option *)0,
- (int *)0, 0);
-}
-
-#endif /* REALLY_NEED_PLAIN_GETOPT */
-
-#endif /* Not ELIDE_CODE. */
-
-#ifdef TEST
-
-/* Compile with -DTEST to make an executable for use in testing
- the above definition of `getopt'. */
-
-int main(argc, argv) int argc;
-char **argv;
-{
- int c;
- int digit_optind = 0;
-
- while (1) {
- int this_option_optind = optind ? optind : 1;
-
- c = getopt(argc, argv, "abc:d:0123456789");
- if (c == -1)
- break;
-
- switch (c) {
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- if (digit_optind != 0
- && digit_optind != this_option_optind)
- printf("digits occur in two different argv-elements.\n");
- digit_optind = this_option_optind;
- printf("option %c\n", c);
- break;
-
- case 'a':
- printf("option a\n");
- break;
-
- case 'b':
- printf("option b\n");
- break;
-
- case 'c':
- printf("option c with value `%s'\n", optarg);
- break;
-
- case '?':
- break;
-
- default:
- printf("?? getopt returned character code 0%o ??\n", c);
- }
- }
-
- if (optind < argc) {
- printf("non-option ARGV-elements: ");
- while (optind < argc)
- printf("%s ", argv[optind++]);
- printf("\n");
- }
-
- exit(0);
-}
-
-#endif /* TEST */
+++ /dev/null
-// SPDX-License-Identifier: GPL-2.0-or-later
-/* Declarations for getopt.
- * Copyright (C) 1989,90,91,92,93,94,96,97 Free Software Foundation, Inc.
- *
- * NOTE: The canonical source of this file is maintained with the GNU C Library.
- * Bugs can be reported to bug-glibc@gnu.org.
- */
-
-#ifndef _GETOPT_H
-#define _GETOPT_H 1
-
-/*
- * The operating system may or may not provide getopt_long(), and if
- * so it may or may not be a version we are willing to use. Our
- * strategy is to declare getopt here, and then provide code unless
- * the supplied version is adequate. The difficult case is when a
- * declaration for getopt is provided, as our declaration must match.
- *
- * XXX Arguably this version should be named differently, and the
- * local names defined to refer to the system version when we choose
- * to use the system version.
- */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* For communication from `getopt' to the caller.
- When `getopt' finds an option that takes an argument,
- the argument value is returned here.
- Also, when `ordering' is RETURN_IN_ORDER,
- each non-option ARGV-element is returned here. */
-
-extern char *optarg;
-
-/* Index in ARGV of the next element to be scanned.
- This is used for communication to and from the caller
- and for communication between successive calls to `getopt'.
-
- On entry to `getopt', zero means this is the first call; initialize.
-
- When `getopt' returns -1, this is the index of the first of the
- non-option elements that the caller should itself scan.
-
- Otherwise, `optind' communicates from one call to the next
- how much of ARGV has been scanned so far. */
-
-extern int optind;
-
-/* Callers store zero here to inhibit the error message `getopt' prints
- for unrecognized options. */
-
-extern int opterr;
-
-/* Set to an option character which was unrecognized. */
-
-extern int optopt;
-
-/* Describe the long-named options requested by the application.
- The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
- of `struct option' terminated by an element containing a name which is
- zero.
-
- The field `has_arg' is:
- no_argument (or 0) if the option does not take an argument,
- required_argument (or 1) if the option requires an argument,
- optional_argument (or 2) if the option takes an optional argument.
-
- If the field `flag' is not NULL, it points to a variable that is set
- to the value given in the field `val' when the option is found, but
- left unchanged if the option is not found.
-
- To have a long-named option do something other than set an `int' to
- a compiled-in constant, such as set a value from `optarg', set the
- option's `flag' field to zero and its `val' field to a nonzero
- value (the equivalent single-letter option character, if there is
- one). For long options that have a zero `flag' field, `getopt'
- returns the contents of the `val' field. */
-
-struct option {
-#if defined(__STDC__) && __STDC__
- const char *name;
-#else
- char *name;
-#endif
- /* has_arg can't be an enum because some compilers complain about
- type mismatches in all the code that assumes it is an int. */
- int has_arg;
- int *flag;
- int val;
-};
-
-/* Names for the values of the `has_arg' field of `struct option'. */
-
-#define no_argument 0
-#define required_argument 1
-#define optional_argument 2
-
-#if defined(__STDC__) && __STDC__
-
-#ifdef REALLY_NEED_PLAIN_GETOPT
-
-/*
- * getopt is defined in POSIX.2. Assume that if the system defines
- * getopt that it complies with POSIX.2. If not, an autoconf test
- * should be written to define NONPOSIX_GETOPT_DEFINITION.
- */
-#ifndef NONPOSIX_GETOPT_DEFINITION
-extern int getopt(int argc, char *const *argv, const char *shortopts);
-#else /* NONPOSIX_GETOPT_DEFINITION */
-extern int getopt(void);
-#endif /* NONPOSIX_GETOPT_DEFINITION */
-
-#endif
-
-
-extern int getopt_long(int argc, char *const *argv, const char *shortopts,
- const struct option *longopts, int *longind);
-extern int getopt_long_only(int argc, char *const *argv, const char *shortopts,
- const struct option *longopts, int *longind);
-
-/* Internal only. Users should not call this directly. */
-extern int _getopt_internal(int argc, char *const *argv, const char *shortopts,
- const struct option *longopts, int *longind,
- int long_only);
-#else /* not __STDC__ */
-
-#ifdef REALLY_NEED_PLAIN_GETOPT
-extern int getopt();
-#endif /* REALLY_NEED_PLAIN_GETOPT */
-
-extern int getopt_long();
-extern int getopt_long_only();
-
-extern int _getopt_internal();
-
-#endif /* __STDC__ */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* getopt.h */
+++ /dev/null
-// SPDX-License-Identifier: GPL-2.0-or-later
-/* getopt_long and getopt_long_only entry points for GNU getopt.
- * Copyright (C) 1987,88,89,90,91,92,93,94,96,97,98
- * Free Software Foundation, Inc.
- *
- * NOTE: The canonical source of this file is maintained with the GNU C Library.
- * Bugs can be reported to bug-glibc@gnu.org.
- */
-
-#include <zebra.h>
-#include "getopt.h"
-
-#if !defined __STDC__ || !__STDC__
-/* This is a separate conditional since some stdc systems
- reject `defined (const)'. */
-#ifndef const
-#define const
-#endif
-#endif
-
-#include <stdio.h>
-
-/* Comment out all this code if we are using the GNU C Library, and are not
- actually compiling the library itself. This code is part of the GNU C
- Library, but also included in many other GNU distributions. Compiling
- and linking in this code is a waste when using the GNU C library
- (especially if it is a shared library). Rather than having every GNU
- program understand `configure --with-gnu-libc' and omit the object files,
- it is simpler to just do this in the source for each such file. */
-
-#define GETOPT_INTERFACE_VERSION 2
-#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2
-#include <gnu-versions.h>
-#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
-#define ELIDE_CODE
-#endif
-#endif
-
-#ifndef ELIDE_CODE
-
-
-/* This needs to come after some library #include
- to get __GNU_LIBRARY__ defined. */
-#ifdef __GNU_LIBRARY__
-#include <stdlib.h>
-#endif
-
-#ifndef NULL
-#define NULL 0
-#endif
-
-int getopt_long(argc, argv, options, long_options, opt_index) int argc;
-char *const *argv;
-const char *options;
-const struct option *long_options;
-int *opt_index;
-{
- return _getopt_internal(argc, argv, options, long_options, opt_index,
- 0);
-}
-
-/* Like getopt_long, but '-' as well as '--' can indicate a long option.
- If an option that starts with '-' (not '--') doesn't match a long option,
- but does match a short option, it is parsed as a short option
- instead. */
-
-int getopt_long_only(argc, argv, options, long_options, opt_index) int argc;
-char *const *argv;
-const char *options;
-const struct option *long_options;
-int *opt_index;
-{
- return _getopt_internal(argc, argv, options, long_options, opt_index,
- 1);
-}
-
-
-#endif /* Not ELIDE_CODE. */
-
-#ifdef TEST
-
-#include <stdio.h>
-
-int main(argc, argv) int argc;
-char **argv;
-{
- int c;
- int digit_optind = 0;
-
- while (1) {
- int this_option_optind = optind ? optind : 1;
- int option_index = 0;
- static struct option long_options[] = {
- {"add", 1, 0, 0}, {"append", 0, 0, 0},
- {"delete", 1, 0, 0}, {"verbose", 0, 0, 0},
- {"create", 0, 0, 0}, {"file", 1, 0, 0},
- {0, 0, 0, 0}};
-
- c = getopt_long(argc, argv, "abc:d:0123456789", long_options,
- &option_index);
- if (c == -1)
- break;
-
- switch (c) {
- case 0:
- printf("option %s", long_options[option_index].name);
- if (optarg)
- printf(" with arg %s", optarg);
- printf("\n");
- break;
-
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- if (digit_optind != 0
- && digit_optind != this_option_optind)
- printf("digits occur in two different argv-elements.\n");
- digit_optind = this_option_optind;
- printf("option %c\n", c);
- break;
-
- case 'a':
- printf("option a\n");
- break;
-
- case 'b':
- printf("option b\n");
- break;
-
- case 'c':
- printf("option c with value `%s'\n", optarg);
- break;
-
- case 'd':
- printf("option d with value `%s'\n", optarg);
- break;
-
- case '?':
- break;
-
- default:
- printf("?? getopt returned character code 0%o ??\n", c);
- }
- }
-
- if (optind < argc) {
- printf("non-option ARGV-elements: ");
- while (optind < argc)
- printf("%s ", argv[optind++]);
- printf("\n");
- }
-
- exit(0);
-}
-
-#endif /* TEST */
// 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>
#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,
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;
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]);
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;
}
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;
}
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);
}
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);
}
#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];
};
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
}
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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_ */
#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");
/**
* 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;
new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_prefix));
new->adv = adv;
- new->pref = p;
+ new->pref = *p;
return new;
}
/* Then remove Vertex from Link State Data Base and free memory */
vertices_del(&ted->vertices, vertex);
XFREE(MTYPE_LS_DB, vertex);
- vertex = NULL;
}
void ls_vertex_del_all(struct ls_ted *ted, struct ls_vertex *vertex)
}
}
-static uint64_t get_edge_key(struct ls_attributes *attr, bool dst)
+static struct ls_edge_key get_edge_key(struct ls_attributes *attr, bool dst)
{
- uint64_t key = 0;
+ struct ls_edge_key key = {.family = AF_UNSPEC};
struct ls_standard *std;
if (!attr)
std = &attr->standard;
if (dst) {
- /* Key is the IPv4 remote address */
- if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ADDR))
- key = ((uint64_t)ntohl(std->remote.s_addr))
- & 0xffffffff;
- /* or the 64 bits LSB of IPv6 remote address */
- else if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ADDR6))
- key = ((uint64_t)ntohl(std->remote6.s6_addr32[2]) << 32
- | (uint64_t)ntohl(std->remote6.s6_addr32[3]));
- /* of remote identifier if no IP addresses are defined */
- else if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ID))
- key = (((uint64_t)std->remote_id) & 0xffffffff)
- | ((uint64_t)std->local_id << 32);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ADDR)) {
+ /* Key is the IPv4 remote address */
+ key.family = AF_INET;
+ IPV4_ADDR_COPY(&key.k.addr, &std->remote);
+ } else if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ADDR6)) {
+ /* or the IPv6 remote address */
+ key.family = AF_INET6;
+ IPV6_ADDR_COPY(&key.k.addr6, &std->remote6);
+ } else if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ID)) {
+ /* or Remote identifier if IP addr. are not defined */
+ key.family = AF_LOCAL;
+ key.k.link_id =
+ (((uint64_t)std->remote_id) & 0xffffffff) |
+ ((uint64_t)std->local_id << 32);
+ }
} else {
- /* Key is the IPv4 local address */
- if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR))
- key = ((uint64_t)ntohl(std->local.s_addr)) & 0xffffffff;
- /* or the 64 bits LSB of IPv6 local address */
- else if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR6))
- key = ((uint64_t)ntohl(std->local6.s6_addr32[2]) << 32
- | (uint64_t)ntohl(std->local6.s6_addr32[3]));
- /* of local identifier if no IP addresses are defined */
- else if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ID))
- key = (((uint64_t)std->local_id) & 0xffffffff)
- | ((uint64_t)std->remote_id << 32);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR)) {
+ /* Key is the IPv4 local address */
+ key.family = AF_INET;
+ IPV4_ADDR_COPY(&key.k.addr, &std->local);
+ } else if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR6)) {
+ /* or the 64 bits LSB of IPv6 local address */
+ key.family = AF_INET6;
+ IPV6_ADDR_COPY(&key.k.addr6, &std->local6);
+ } else if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ID)) {
+ /* or Remote identifier if IP addr. are not defined */
+ key.family = AF_LOCAL;
+ key.k.link_id =
+ (((uint64_t)std->local_id) & 0xffffffff) |
+ ((uint64_t)std->remote_id << 32);
+ }
}
return key;
struct ls_attributes *attributes)
{
struct ls_edge *new;
- uint64_t key = 0;
+ struct ls_edge_key key;
if (attributes == NULL)
return NULL;
key = get_edge_key(attributes, false);
- if (key == 0)
+ if (key.family == AF_UNSPEC)
return NULL;
/* Create Edge and add it to the TED */
return new;
}
-struct ls_edge *ls_find_edge_by_key(struct ls_ted *ted, const uint64_t key)
+struct ls_edge *ls_find_edge_by_key(struct ls_ted *ted,
+ const struct ls_edge_key key)
{
struct ls_edge edge = {};
- if (key == 0)
+ if (key.family == AF_UNSPEC)
return NULL;
edge.key = key;
return NULL;
edge.key = get_edge_key(attributes, false);
- if (edge.key == 0)
+ if (edge.key.family == AF_UNSPEC)
return NULL;
return edges_find(&ted->edges, &edge);
return NULL;
edge.key = get_edge_key(attributes, true);
- if (edge.key == 0)
+ if (edge.key.family == AF_UNSPEC)
return NULL;
return edges_find(&ted->edges, &edge);
if (!e1 && !e2)
return 1;
- if (e1->key != e2->key)
+ if (edge_cmp(e1, e2) != 0)
return 0;
if (e1->attributes == e2->attributes)
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);
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;
+ if (!prefix)
+ return NULL;
+
+ prefix_copy(&subnet.key, prefix);
return subnets_find(&ted->subnets, &subnet);
}
case LS_MSG_EVENT_DELETE:
vertex = ls_find_vertex_by_id(ted, node->adv);
if (vertex) {
- if (delete)
+ if (delete) {
ls_vertex_del_all(ted, vertex);
- else
+ vertex = NULL;
+ } else
vertex->status = DELETE;
}
break;
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)
+ if (delete) {
ls_subnet_del_all(ted, subnet);
- else
+ subnet = NULL;
+ } else
subnet->status = DELETE;
}
break;
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);
}
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;
}
}
+static const char *edge_key_to_text(struct ls_edge_key key)
+{
+#define FORMAT_BUF_COUNT 4
+ static char buf_ring[FORMAT_BUF_COUNT][INET6_BUFSIZ];
+ static size_t cur_buf = 0;
+ char *rv;
+
+ rv = buf_ring[cur_buf];
+ cur_buf = (cur_buf + 1) % FORMAT_BUF_COUNT;
+
+ switch (key.family) {
+ case AF_INET:
+ snprintfrr(rv, INET6_BUFSIZ, "%pI4", &key.k.addr);
+ break;
+ case AF_INET6:
+ snprintfrr(rv, INET6_BUFSIZ, "%pI6", &key.k.addr6);
+ break;
+ case AF_LOCAL:
+ snprintfrr(rv, INET6_BUFSIZ, "%" PRIu64, key.k.link_id);
+ break;
+ default:
+ snprintfrr(rv, INET6_BUFSIZ, "(Unknown)");
+ break;
+ }
+
+ return rv;
+}
+
static void ls_show_edge_vty(struct ls_edge *edge, struct vty *vty,
bool verbose)
{
attr = edge->attributes;
sbuf_init(&sbuf, NULL, 0);
- sbuf_push(&sbuf, 2, "Edge (%" PRIu64 "): ", edge->key);
+ sbuf_push(&sbuf, 2, "Edge (%s): ", edge_key_to_text(edge->key));
if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR))
sbuf_push(&sbuf, 0, "%pI4", &attr->standard.local);
else if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR6))
attr = edge->attributes;
- json_object_int_add(json, "edge-id", edge->key);
+ json_object_string_add(json, "edge-id", edge_key_to_text(edge->key));
json_object_string_add(json, "status", status2txt[edge->status]);
json_object_string_add(json, "origin", origin2txt[attr->adv.origin]);
ls_node_id_to_text(attr->adv, buf, INET6_BUFSIZ);
for (ALL_LIST_ELEMENTS_RO(vertex->incoming_edges, lst_node,
vertex_edge)) {
zlog_debug(
- " inc edge key:%" PRIu64 " attr key:%pI4 loc:(%pI4) rmt:(%pI4)",
- vertex_edge->key,
+ " inc edge key:%s attr key:%pI4 loc:(%pI4) rmt:(%pI4)",
+ edge_key_to_text(vertex_edge->key),
&vertex_edge->attributes->adv.id.ip.addr,
&vertex_edge->attributes->standard.local,
&vertex_edge->attributes->standard.remote);
for (ALL_LIST_ELEMENTS_RO(vertex->outgoing_edges, lst_node,
vertex_edge)) {
zlog_debug(
- " out edge key:%" PRIu64 " attr key:%pI4 loc:(%pI4) rmt:(%pI4)",
- vertex_edge->key,
+ " out edge key:%s attr key:%pI4 loc:(%pI4) rmt:(%pI4)",
+ edge_key_to_text(vertex_edge->key),
&vertex_edge->attributes->adv.id.ip.addr,
&vertex_edge->attributes->standard.local,
&vertex_edge->attributes->standard.remote);
}
}
frr_each (edges, &ted->edges, edge) {
- zlog_debug(" Ted edge key:%" PRIu64 "src:%pI4 dst:%pI4", edge->key,
+ zlog_debug(" Ted edge key:%s src:%pI4 dst:%pI4",
+ edge_key_to_text(edge->key),
edge->source ? &edge->source->node->router_id
: &inaddr_any,
edge->destination
*/
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
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 */
};
*
* @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.
struct list *prefixes; /* List of advertised prefix */
};
+/* Link State Edge Key structure */
+struct ls_edge_key {
+ uint8_t family;
+ union {
+ struct in_addr addr;
+ struct in6_addr addr6;
+ uint64_t link_id;
+ } k;
+};
+
/* Link State Edge structure */
PREDECL_RBTREE_UNIQ(edges);
struct ls_edge {
enum ls_type type; /* Link State Type */
enum ls_status status; /* Status of the Edge in the TED */
struct edges_item entry; /* Entry in RB tree */
- uint64_t key; /* Unique Key identifier */
+ struct ls_edge_key key; /* Unique Key identifier */
struct ls_attributes *attributes; /* Link State attributes */
struct ls_vertex *source; /* Pointer to the source Vertex */
struct ls_vertex *destination; /* Pointer to the destination Vertex */
macro_inline int edge_cmp(const struct ls_edge *edge1,
const struct ls_edge *edge2)
{
- return numcmp(edge1->key, edge2->key);
+ if (edge1->key.family != edge2->key.family)
+ return numcmp(edge1->key.family, edge2->key.family);
+
+ switch (edge1->key.family) {
+ case AF_INET:
+ return memcmp(&edge1->key.k.addr, &edge2->key.k.addr, 4);
+ case AF_INET6:
+ return memcmp(&edge1->key.k.addr6, &edge2->key.k.addr6, 16);
+ case AF_LOCAL:
+ return numcmp(edge1->key.k.link_id, edge2->key.k.link_id);
+ default:
+ return 0;
+ }
}
DECLARE_RBTREE_UNIQ(edges, struct ls_edge, entry, edge_cmp);
/*
* Prefix comparison are done to the host part so, 10.0.0.1/24
- * and 10.0.0.2/24 are considered come different
+ * and 10.0.0.2/24 are considered different
*/
macro_inline int subnet_cmp(const struct ls_subnet *a,
const struct ls_subnet *b)
* @return Edge if found, NULL otherwise
*/
extern struct ls_edge *ls_find_edge_by_key(struct ls_ted *ted,
- const uint64_t key);
+ const struct ls_edge_key key);
/**
* Find Edge in the Link State Data Base by the source (local IPv4 or IPv6
* @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.
static struct zlog_cfg_file zt_file_cmdline = {
.prio_min = ZLOG_DISABLED,
+ .ts_subsec = LOG_TIMESTAMP_PRECISION,
};
static struct zlog_cfg_file zt_file = {
.prio_min = ZLOG_DISABLED,
+ .ts_subsec = LOG_TIMESTAMP_PRECISION,
};
static struct zlog_cfg_filterfile zt_filterfile = {
- .parent = {
- .prio_min = ZLOG_DISABLED,
- },
+ .parent =
+ {
+ .prio_min = ZLOG_DISABLED,
+ .ts_subsec = LOG_TIMESTAMP_PRECISION,
+ },
};
static struct zlog_cfg_file zt_stdout_file = {
.prio_min = ZLOG_DISABLED,
+ .ts_subsec = LOG_TIMESTAMP_PRECISION,
};
static struct zlog_cfg_5424 zt_stdout_journald = {
.prio_min = ZLOG_DISABLED,
*/
#include <zebra.h>
+#include "debug.h"
#include "libfrr.h"
#include "mgmtd/mgmt.h"
#include "mgmt_be_client.h"
#include "mgmt_msg.h"
#include "mgmt_pb.h"
#include "network.h"
+#include "northbound.h"
#include "stream.h"
#include "sockopt.h"
-#ifdef REDIRECT_DEBUG_TO_STDERR
-#define MGMTD_BE_CLIENT_DBG(fmt, ...) \
- fprintf(stderr, "%s: " fmt "\n", __func__, ##__VA_ARGS__)
-#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, ...) \
- do { \
- if (mgmt_debug_be_client) \
- zlog_debug("%s: " fmt, __func__, ##__VA_ARGS__); \
- } while (0)
-#define MGMTD_BE_CLIENT_ERR(fmt, ...) \
+#include "lib/mgmt_be_client_clippy.c"
+
+#define MGMTD_BE_CLIENT_DBG(fmt, ...) \
+ DEBUGD(&mgmt_dbg_be_client, "%s:" fmt, __func__, ##__VA_ARGS__)
+#define MGMTD_BE_CLIENT_ERR(fmt, ...) \
zlog_err("%s: ERROR: " fmt, __func__, ##__VA_ARGS__)
-#endif /* REDIRECT_DEBUG_TO_STDERR */
+#define MGMTD_DBG_BE_CLIENT_CHECK() \
+ DEBUG_MODE_CHECK(&mgmt_dbg_be_client, DEBUG_MODE_ALL)
DEFINE_MTYPE_STATIC(LIB, MGMTD_BE_BATCH,
"MGMTD backend transaction batch data");
struct nb_config *candidate_config;
struct nb_config *running_config;
- unsigned long num_batch_find;
- unsigned long avg_batch_find_tm;
unsigned long num_edit_nb_cfg;
unsigned long avg_edit_nb_cfg_tm;
unsigned long num_prep_nb_cfg;
#define FOREACH_BE_TXN_IN_LIST(client_ctx, txn) \
frr_each_safe (mgmt_be_txns, &(client_ctx)->txn_head, (txn))
-static bool mgmt_debug_be_client;
+struct debug mgmt_dbg_be_client = {0, "Management backend client operations"};
static struct mgmt_be_client_ctx mgmt_be_client_ctx = {
.conn_fd = -1,
bool error;
char err_buf[BUFSIZ];
size_t num_processed;
- bool debug_be = mgmt_debug_be_client;
int err;
assert(txn && txn->client_ctx);
* interested in validating it.
*/
error = false;
- if (debug_be)
- gettimeofday(&edit_nb_cfg_start, NULL);
+
+ gettimeofday(&edit_nb_cfg_start, NULL);
nb_candidate_edit_config_changes(
client_ctx->candidate_config,
txn_req->req.set_cfg.cfg_changes,
err_buf);
return -1;
}
- if (debug_be) {
- gettimeofday(&edit_nb_cfg_end, NULL);
- edit_nb_cfg_tm = timeval_elapsed(
- edit_nb_cfg_end, edit_nb_cfg_start);
- client_ctx->avg_edit_nb_cfg_tm =
- ((client_ctx->avg_edit_nb_cfg_tm
- * client_ctx->num_edit_nb_cfg)
- + edit_nb_cfg_tm)
- / (client_ctx->num_edit_nb_cfg + 1);
- }
+ gettimeofday(&edit_nb_cfg_end, NULL);
+ edit_nb_cfg_tm = timeval_elapsed(edit_nb_cfg_end,
+ edit_nb_cfg_start);
+ client_ctx->avg_edit_nb_cfg_tm =
+ ((client_ctx->avg_edit_nb_cfg_tm *
+ client_ctx->num_edit_nb_cfg) +
+ edit_nb_cfg_tm) /
+ (client_ctx->num_edit_nb_cfg + 1);
client_ctx->num_edit_nb_cfg++;
}
*/
nb_ctx.client = NB_CLIENT_CLI;
nb_ctx.user = (void *)client_ctx->client_params.user_data;
- if (debug_be)
- gettimeofday(&prep_nb_cfg_start, NULL);
+
+ gettimeofday(&prep_nb_cfg_start, NULL);
err = nb_candidate_commit_prepare(nb_ctx, client_ctx->candidate_config,
"MGMTD Backend Txn", &txn->nb_txn,
#ifdef MGMTD_LOCAL_VALIDATIONS_ENABLED
"Prepared configs for Txn %llx, %u Batches! successfully!",
(unsigned long long)txn->txn_id,
(uint32_t)num_processed);
- if (debug_be) {
- gettimeofday(&prep_nb_cfg_end, NULL);
- prep_nb_cfg_tm =
- timeval_elapsed(prep_nb_cfg_end, prep_nb_cfg_start);
- client_ctx->avg_prep_nb_cfg_tm =
- ((client_ctx->avg_prep_nb_cfg_tm
- * client_ctx->num_prep_nb_cfg)
- + prep_nb_cfg_tm)
- / (client_ctx->num_prep_nb_cfg + 1);
- }
+
+ gettimeofday(&prep_nb_cfg_end, NULL);
+ prep_nb_cfg_tm = timeval_elapsed(prep_nb_cfg_end, prep_nb_cfg_start);
+ client_ctx->avg_prep_nb_cfg_tm = ((client_ctx->avg_prep_nb_cfg_tm *
+ client_ctx->num_prep_nb_cfg) +
+ prep_nb_cfg_tm) /
+ (client_ctx->num_prep_nb_cfg + 1);
client_ctx->num_prep_nb_cfg++;
FOREACH_BE_TXN_BATCH_IN_LIST (txn, batch) {
}
}
- if (debug_be)
- 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);
+ 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);
if (error)
mgmt_be_txn_cfg_abort(txn);
char err_buf[BUFSIZ];
size_t num_processed;
static uint64_t batch_ids[MGMTD_BE_MAX_BATCH_IDS_IN_REQ];
- bool debug_be = mgmt_debug_be_client;
assert(txn && txn->client_ctx);
client_ctx = txn->client_ctx;
/*
* Now apply all the batches we have applied in one go.
*/
- if (debug_be)
- gettimeofday(&apply_nb_cfg_start, NULL);
+ gettimeofday(&apply_nb_cfg_start, NULL);
(void)nb_candidate_commit_apply(txn->nb_txn, true, &txn->nb_txn_id,
err_buf, sizeof(err_buf) - 1);
- if (debug_be) {
- gettimeofday(&apply_nb_cfg_end, NULL);
- apply_nb_cfg_tm =
- timeval_elapsed(apply_nb_cfg_end, apply_nb_cfg_start);
- client_ctx->avg_apply_nb_cfg_tm =
- ((client_ctx->avg_apply_nb_cfg_tm
- * client_ctx->num_apply_nb_cfg)
- + apply_nb_cfg_tm)
- / (client_ctx->num_apply_nb_cfg + 1);
- }
+ gettimeofday(&apply_nb_cfg_end, NULL);
+
+ apply_nb_cfg_tm = timeval_elapsed(apply_nb_cfg_end, apply_nb_cfg_start);
+ client_ctx->avg_apply_nb_cfg_tm = ((client_ctx->avg_apply_nb_cfg_tm *
+ client_ctx->num_apply_nb_cfg) +
+ apply_nb_cfg_tm) /
+ (client_ctx->num_apply_nb_cfg + 1);
client_ctx->num_apply_nb_cfg++;
txn->nb_txn = NULL;
mgmt_be_send_apply_reply(client_ctx, txn->txn_id, batch_ids,
num_processed, true, NULL);
- if (debug_be)
- MGMTD_BE_CLIENT_DBG("Nb-apply-duration %lu (avg: %lu) uSec",
- apply_nb_cfg_tm,
- client_ctx->avg_apply_nb_cfg_tm);
+ MGMTD_BE_CLIENT_DBG("Nb-apply-duration %lu (avg: %lu) uSec",
+ apply_nb_cfg_tm, client_ctx->avg_apply_nb_cfg_tm);
return 0;
}
struct mgmt_be_client_ctx *client_ctx = EVENT_ARG(thread);
if (mgmt_msg_procbufs(&client_ctx->mstate, mgmt_be_client_process_msg,
- client_ctx, mgmt_debug_be_client))
+ client_ctx, MGMTD_DBG_BE_CLIENT_CHECK()))
mgmt_be_client_register_event(client_ctx, MGMTD_BE_PROC_MSG);
}
enum mgmt_msg_rsched rv;
rv = mgmt_msg_read(&client_ctx->mstate, client_ctx->conn_fd,
- mgmt_debug_be_client);
+ MGMTD_DBG_BE_CLIENT_CHECK());
if (rv == MSR_DISCONNECT) {
mgmt_be_server_disconnect(client_ctx, true);
return;
&client_ctx->mstate, be_msg,
mgmtd__be_message__get_packed_size(be_msg),
(size_t(*)(void *, void *))mgmtd__be_message__pack,
- mgmt_debug_be_client);
+ MGMTD_DBG_BE_CLIENT_CHECK());
mgmt_be_client_sched_msg_write(client_ctx);
return rv;
}
enum mgmt_msg_wsched rv;
rv = mgmt_msg_write(&client_ctx->mstate, client_ctx->conn_fd,
- mgmt_debug_be_client);
+ MGMTD_DBG_BE_CLIENT_CHECK());
if (rv == MSW_SCHED_STREAM)
mgmt_be_client_register_event(client_ctx, MGMTD_BE_CONN_WRITE);
else if (rv == MSW_DISCONNECT)
static void mgmt_be_server_connect(struct mgmt_be_client_ctx *client_ctx)
{
- const char *dbgtag = mgmt_debug_be_client ? "BE-client" : NULL;
+ const char *dbgtag = MGMTD_DBG_BE_CLIENT_CHECK() ? "BE-client" : NULL;
assert(client_ctx->conn_fd == -1);
client_ctx->conn_fd = mgmt_msg_connect(
&client_ctx->conn_retry_tmr);
}
-extern struct nb_config *running_config;
+DEFPY(debug_mgmt_client_be, debug_mgmt_client_be_cmd,
+ "[no] debug mgmt client backend",
+ NO_STR DEBUG_STR MGMTD_STR
+ "client\n"
+ "backend\n")
+{
+ uint32_t mode = DEBUG_NODE2MODE(vty->node);
+
+ DEBUG_MODE_SET(&mgmt_dbg_be_client, mode, !no);
+
+ return CMD_SUCCESS;
+}
+
+static void mgmt_debug_client_be_set_all(uint32_t flags, bool set)
+{
+ DEBUG_FLAGS_SET(&mgmt_dbg_be_client, flags, set);
+}
+
+static int mgmt_debug_be_client_config_write(struct vty *vty)
+{
+ if (DEBUG_MODE_CHECK(&mgmt_dbg_be_client, DEBUG_MODE_CONF))
+ vty_out(vty, "debug mgmt client frontend\n");
+
+ return 1;
+}
+
+void mgmt_debug_be_client_show_debug(struct vty *vty)
+{
+ if (MGMTD_DBG_BE_CLIENT_CHECK())
+ vty_out(vty, "debug mgmt client backend\n");
+}
+
+static struct debug_callbacks mgmt_dbg_be_client_cbs = {
+ .debug_set_all = mgmt_debug_client_be_set_all};
+
+static struct cmd_node mgmt_dbg_node = {
+ .name = "mgmt backend client",
+ .node = DEBUG_NODE,
+ .prompt = "",
+ .config_write = mgmt_debug_be_client_config_write,
+};
/*
* Initialize library and try connecting with MGMTD.
return (uintptr_t)&mgmt_be_client_ctx;
}
+
+void mgmt_be_client_lib_vty_init(void)
+{
+ debug_init(&mgmt_dbg_be_client_cbs);
+ install_node(&mgmt_dbg_node);
+ install_element(ENABLE_NODE, &debug_mgmt_client_be_cmd);
+ install_element(CONFIG_NODE, &debug_mgmt_client_be_cmd);
+}
+
+
/*
* Subscribe with MGMTD for one or more YANG subtree(s).
*/
extern uintptr_t mgmt_be_client_lib_init(struct mgmt_be_client_params *params,
struct event_loop *master_thread);
+/*
+ * Initialize library vty (adds debug support).
+ *
+ * This call should be added to your component when enabling other vty code to
+ * enable mgmtd client debugs. When adding, one needs to also add a their
+ * component in `xref2vtysh.py` as well.
+ */
+extern void mgmt_be_client_lib_vty_init(void);
+
+/*
+ * Print enabled debugging commands.
+ */
+extern void mgmt_debug_be_client_show_debug(struct vty *vty);
+
/*
* Subscribe with MGMTD for one or more YANG subtree(s).
*
*/
#include <zebra.h>
+#include "debug.h"
#include "memory.h"
#include "libfrr.h"
#include "mgmt_fe_client.h"
#include "stream.h"
#include "sockopt.h"
-#ifdef REDIRECT_DEBUG_TO_STDERR
-#define MGMTD_FE_CLIENT_DBG(fmt, ...) \
- fprintf(stderr, "%s: " fmt "\n", __func__, ##__VA_ARGS__)
-#define MGMTD_FE_CLIENT_ERR(fmt, ...) \
- fprintf(stderr, "%s: ERROR, " fmt "\n", __func__, ##__VA_ARGS__)
-#else /* REDIRECT_DEBUG_TO_STDERR */
-#define MGMTD_FE_CLIENT_DBG(fmt, ...) \
- do { \
- if (mgmt_debug_fe_client) \
- zlog_debug("%s: " fmt, __func__, ##__VA_ARGS__); \
- } while (0)
-#define MGMTD_FE_CLIENT_ERR(fmt, ...) \
+#include "lib/mgmt_fe_client_clippy.c"
+
+#define MGMTD_FE_CLIENT_DBG(fmt, ...) \
+ DEBUGD(&mgmt_dbg_fe_client, "%s:" fmt, __func__, ##__VA_ARGS__)
+#define MGMTD_FE_CLIENT_ERR(fmt, ...) \
zlog_err("%s: ERROR: " fmt, __func__, ##__VA_ARGS__)
-#endif /* REDIRECT_DEBUG_TO_STDERR */
+#define MGMTD_DBG_FE_CLIENT_CHECK() \
+ DEBUG_MODE_CHECK(&mgmt_dbg_fe_client, DEBUG_MODE_ALL)
struct mgmt_fe_client_ctx;
#define FOREACH_SESSION_IN_LIST(client_ctx, session) \
frr_each_safe (mgmt_sessions, &(client_ctx)->client_sessions, (session))
-static bool mgmt_debug_fe_client;
+struct debug mgmt_dbg_fe_client = {0, "Management frontend client operations"};
static struct mgmt_fe_client_ctx mgmt_fe_client_ctx = {
.conn_fd = -1,
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;
}
}
&client_ctx->mstate, fe_msg,
mgmtd__fe_message__get_packed_size(fe_msg),
(size_t(*)(void *, void *))mgmtd__fe_message__pack,
- mgmt_debug_fe_client);
+ MGMTD_DBG_FE_CLIENT_CHECK());
mgmt_fe_client_sched_msg_write(client_ctx);
return rv;
}
client_ctx = (struct mgmt_fe_client_ctx *)EVENT_ARG(thread);
rv = mgmt_msg_write(&client_ctx->mstate, client_ctx->conn_fd,
- mgmt_debug_fe_client);
+ MGMTD_DBG_FE_CLIENT_CHECK());
if (rv == MSW_SCHED_STREAM)
mgmt_fe_client_register_event(client_ctx, MGMTD_FE_CONN_WRITE);
else if (rv == MSW_DISCONNECT)
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);
}
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;
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(
client_ctx = (struct mgmt_fe_client_ctx *)EVENT_ARG(thread);
if (mgmt_msg_procbufs(&client_ctx->mstate, mgmt_fe_client_process_msg,
- client_ctx, mgmt_debug_fe_client))
+ client_ctx, MGMTD_DBG_FE_CLIENT_CHECK()))
mgmt_fe_client_register_event(client_ctx, MGMTD_FE_PROC_MSG);
}
client_ctx = (struct mgmt_fe_client_ctx *)EVENT_ARG(thread);
rv = mgmt_msg_read(&client_ctx->mstate, client_ctx->conn_fd,
- mgmt_debug_fe_client);
+ MGMTD_DBG_FE_CLIENT_CHECK());
if (rv == MSR_DISCONNECT) {
mgmt_fe_server_disconnect(client_ctx, true);
return;
static void mgmt_fe_server_connect(struct mgmt_fe_client_ctx *client_ctx)
{
- const char *dbgtag = mgmt_debug_fe_client ? "FE-client" : NULL;
+ const char *dbgtag = MGMTD_DBG_FE_CLIENT_CHECK() ? "FE-client" : NULL;
assert(client_ctx->conn_fd == -1);
client_ctx->conn_fd = mgmt_msg_connect(
&client_ctx->conn_retry_tmr);
}
+DEFPY(debug_mgmt_client_fe, debug_mgmt_client_fe_cmd,
+ "[no] debug mgmt client frontend",
+ NO_STR DEBUG_STR MGMTD_STR
+ "client\n"
+ "frontend\n")
+{
+ uint32_t mode = DEBUG_NODE2MODE(vty->node);
+
+ DEBUG_MODE_SET(&mgmt_dbg_fe_client, mode, !no);
+
+ return CMD_SUCCESS;
+}
+
+static void mgmt_debug_client_fe_set_all(uint32_t flags, bool set)
+{
+ DEBUG_FLAGS_SET(&mgmt_dbg_fe_client, flags, set);
+}
+
+static int mgmt_debug_fe_client_config_write(struct vty *vty)
+{
+ if (DEBUG_MODE_CHECK(&mgmt_dbg_fe_client, DEBUG_MODE_CONF))
+ vty_out(vty, "debug mgmt client frontend\n");
+
+ return CMD_SUCCESS;
+}
+
+void mgmt_debug_fe_client_show_debug(struct vty *vty)
+{
+ if (MGMTD_DBG_FE_CLIENT_CHECK())
+ vty_out(vty, "debug mgmt client frontend\n");
+}
+
+static struct debug_callbacks mgmt_dbg_fe_client_cbs = {
+ .debug_set_all = mgmt_debug_client_fe_set_all};
+
+static struct cmd_node mgmt_dbg_node = {
+ .name = "mgmt client frontend",
+ .node = DEBUG_NODE,
+ .prompt = "",
+ .config_write = mgmt_debug_fe_client_config_write,
+};
+
/*
* Initialize library and try connecting with MGMTD.
*/
return (uintptr_t)&mgmt_fe_client_ctx;
}
+void mgmt_fe_client_lib_vty_init(void)
+{
+ debug_init(&mgmt_dbg_fe_client_cbs);
+ install_node(&mgmt_dbg_node);
+ install_element(ENABLE_NODE, &debug_mgmt_client_fe_cmd);
+ install_element(CONFIG_NODE, &debug_mgmt_client_fe_cmd);
+}
+
/*
* Create a new Session for a Frontend Client connection.
*/
extern uintptr_t mgmt_fe_client_lib_init(struct mgmt_fe_client_params *params,
struct event_loop *master_thread);
+/*
+ * Initialize library vty (adds debug support).
+ *
+ * This call should be added to your component when enabling other vty code to
+ * enable mgmtd client debugs. When adding, one needs to also add a their
+ * component in `xref2vtysh.py` as well.
+ */
+extern void mgmt_fe_client_lib_vty_init(void);
+
+/*
+ * Print enabled debugging commands.
+ */
+extern void mgmt_debug_fe_client_show_debug(struct vty *vty);
+
/*
* Create a new Session for a Frontend Client connection.
*
* 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;
* 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";
struct lyd_node *dnode;
char *strp;
uint32_t print_options = LYD_PRINT_WITHSIBLINGS;
+ int ret;
if (xml)
format = LYD_XML;
/* 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;
}
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);
if (IPV4_CLASS_D(ip))
return false;
- if (IPV4_CLASS_E(ip)) {
+ if (IPV4_NET0(ip) || IPV4_NET127(ip) || IPV4_CLASS_E(ip)) {
if (cmd_allow_reserved_ranges_get())
return true;
else
/* NOTE: This routine expects the address argument in network byte order. */
static inline bool ipv4_martian(const struct in_addr *addr)
{
- in_addr_t ip = ntohl(addr->s_addr);
-
- if (IPV4_NET0(ip) || IPV4_NET127(ip) || !ipv4_unicast_valid(addr)) {
+ if (!ipv4_unicast_valid(addr))
return true;
- }
return false;
}
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);
{
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,
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;
}
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;
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);
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);
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));
* 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);
else
table = map->ipv6_prefix_table;
- if (!table)
- return NULL;
-
do {
candidate_rmap_list =
route_map_get_index_list(&rn, prefix, table);
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);
struct route_map_index *index, afi_t afi,
const char *plist_name)
{
- struct route_map *rmap = NULL;
-
if (!index)
return;
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;
}
*/
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);
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,
/* 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,
}
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));
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);
{
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++) {
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(
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);
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);
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);
(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"))
(strmatch(A, "frr-bgp-route-map:set-extcommunity-none"))
#define IS_SET_EXTCOMMUNITY_RT(A) \
(strmatch(A, "frr-bgp-route-map:set-extcommunity-rt"))
+#define IS_SET_EXTCOMMUNITY_NT(A) \
+ (strmatch(A, "frr-bgp-route-map:set-extcommunity-nt"))
#define IS_SET_EXTCOMMUNITY_SOO(A) \
(strmatch(A, "frr-bgp-route-map:set-extcommunity-soo"))
#define IS_SET_EXTCOMMUNITY_LB(A) \
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,
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,
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",
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"));
yang_dnode_get_string(
dnode,
"./rmap-set-action/frr-bgp-route-map:extcommunity-rt"));
+ } else if (IS_SET_EXTCOMMUNITY_NT(action)) {
+ vty_out(vty, " set extcommunity nt %s\n",
+ yang_dnode_get_string(
+ dnode,
+ "./rmap-set-action/frr-bgp-route-map:extcommunity-nt"));
} else if (IS_SET_EXTCOMMUNITY_SOO(action)) {
vty_out(vty, " set extcommunity soo %s\n",
yang_dnode_get_string(
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);
return lib_route_map_entry_set_destroy(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
*/
.destroy = lib_route_map_entry_set_action_value_destroy,
}
},
+ {
+ .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 = {
--- /dev/null
+// 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";
+ }
+}
--- /dev/null
+// 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 */
lib/filter.c \
lib/filter_cli.c \
lib/filter_nb.c \
+ lib/flex_algo.c \
lib/frrcu.c \
lib/frrlua.c \
lib/frrscript.c \
lib/frr_pthread.c \
lib/frrstr.c \
- lib/getopt.c \
- lib/getopt1.c \
lib/grammar_sandbox.c \
lib/graph.c \
lib/hash.c \
lib/if_rmap.c \
lib/imsg-buffer.c \
lib/imsg.c \
+ lib/iso.c \
lib/jhash.c \
lib/json.c \
lib/keychain.c \
lib/sockopt.c \
lib/sockunion.c \
lib/spf_backoff.c \
+ lib/segment_routing.c \
lib/srcdest_table.c \
lib/stream.c \
lib/strformat.c \
nodist_lib_libfrr_la_SOURCES = \
yang/frr-affinity-map.yang.c \
yang/frr-filter.yang.c \
+ yang/frr-if-rmap.yang.c \
yang/frr-interface.yang.c \
yang/frr-route-map.yang.c \
yang/frr-route-types.yang.c \
lib/affinitymap_cli.c \
lib/if.c \
lib/filter_cli.c \
+ lib/if_rmap.c \
lib/log_vty.c \
+ lib/mgmt_be_client.c \
+ lib/mgmt_fe_client.c \
lib/nexthop_group.c \
lib/northbound_cli.c \
lib/plist.c \
lib/distribute.h \
lib/ferr.h \
lib/filter.h \
+ lib/flex_algo.h \
lib/freebsd-queue.h \
lib/frrlua.h \
lib/frrscript.h \
lib/frratomic.h \
lib/frrcu.h \
lib/frrstr.h \
- lib/getopt.h \
lib/graph.h \
lib/hash.h \
lib/hook.h \
lib/if_rmap.h \
lib/imsg.h \
lib/ipaddr.h \
+ lib/iso.h \
lib/jhash.h \
lib/json.h \
lib/keychain.h \
lib/sockopt.h \
lib/sockunion.h \
lib/spf_backoff.h \
+ lib/segment_routing.h \
lib/srcdest_table.h \
lib/srte.h \
lib/stream.h \
#include <zebra.h>
#include <stdio.h>
+#include "lib/json.h"
#include "printfrr.h"
#include "memory.h"
#include "termtable.h"
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;
+}
#define _TERMTABLE_H_
#include <zebra.h>
+#include "lib/json.h"
#ifdef __cplusplus
extern "C" {
*/
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
uint32_t newsize = head->count, i, j;
uint8_t newshift, delta;
+ /* note hash_grow is called after head->count++, so newsize is
+ * guaranteed to be >= 1. So the minimum argument to builtin_ctz
+ * below is 2, which returns 1, and that makes newshift >= 2.
+ *
+ * Calling hash_grow with a zero head->count would result in a
+ * malformed hash table that has tabshift == 1.
+ */
+ assert(head->count > 0);
+
hash_consistency_check(head);
newsize |= newsize >> 1;
#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, \
struct thash_item **entries;
uint32_t count;
+ /* tabshift can be 0 if the hash table is empty and entries is NULL.
+ * otherwise it will always be 2 or larger because it contains
+ * the shift value *plus 1*. This is a trick to make HASH_SIZE return
+ * the correct value (with the >> 1) for tabshift == 0, without needing
+ * a conditional branch.
+ */
uint8_t tabshift;
uint8_t minshift, maxshift;
};
((1U << (tabshift)) >> 1)
#define HASH_SIZE(head) \
_HASH_SIZE((head).tabshift)
-#define _HASH_KEY(tabshift, val) \
- ((val) >> (33 - (tabshift)))
+#define _HASH_KEY(tabshift, val) \
+ ({ \
+ assume((tabshift) >= 2 && (tabshift) <= 33); \
+ (val) >> (33 - (tabshift)); \
+ })
#define HASH_KEY(head, val) \
_HASH_KEY((head).tabshift, val)
#define HASH_GROW_THRESHOLD(head) \
macro_pure bool prefix ## _member(const struct prefix##_head *h, \
const type *item) \
{ \
+ if (!h->hh.tabshift) \
+ return NULL; \
uint32_t hval = item->field.hi.hashval, hbits = HASH_KEY(h->hh, hval); \
const struct thash_item *hitem = h->hh.entries[hbits]; \
while (hitem && hitem->hashval < hval) \
static uint64_t mgmt_client_id_next;
static uint64_t mgmt_last_req_id = UINT64_MAX;
+static bool vty_debug;
+#define VTY_DBG(fmt, ...) \
+ do { \
+ if (vty_debug) \
+ zlog_debug(fmt, ##__VA_ARGS__); \
+ } while (0)
+
PREDECL_DLIST(vtyservs);
struct vty_serv {
/* Integrated configuration file path */
static char integrate_default[] = SYSCONFDIR INTEGRATE_DEFAULT_CONFIG;
-static bool do_log_commands;
-static bool do_log_commands_perm;
+bool vty_log_commands;
+static bool vty_log_commands_perm;
void vty_mgmt_resume_response(struct vty *vty, bool success)
{
/*
* Log non empty command lines
*/
- if (do_log_commands &&
+ if (vty_log_commands &&
strncmp(buf, "echo PING", strlen("echo PING")) != 0)
cp = buf;
if (cp != NULL) {
}
/* 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;
"Log all commands\n")
{
if (no) {
- if (do_log_commands_perm) {
+ if (vty_log_commands_perm) {
vty_out(vty,
"Daemon started with permanent logging turned on for commands, ignoring\n");
return CMD_WARNING;
}
- do_log_commands = false;
+ vty_log_commands = false;
} else
- do_log_commands = true;
+ vty_log_commands = true;
return CMD_SUCCESS;
}
vty_endframe(vty, "exit\n");
- if (do_log_commands)
+ if (vty_log_commands)
vty_out(vty, "log commands\n");
vty_out(vty, "!\n");
static void vty_mgmt_server_connected(uintptr_t lib_hndl, uintptr_t usr_data,
bool connected)
{
- zlog_err("%sGot %sconnected %s MGMTD Frontend Server",
- !connected ? "ERROR: " : "", !connected ? "dis: " : "",
- !connected ? "from" : "to");
+ VTY_DBG("%sGot %sconnected %s MGMTD Frontend Server",
+ !connected ? "ERROR: " : "", !connected ? "dis: " : "",
+ !connected ? "from" : "to");
mgmt_fe_connected = connected;
vty = (struct vty *)session_ctx;
if (!success) {
- zlog_err("%s session for client %llu failed!",
- create ? "Creating" : "Destroying",
- (unsigned long long)client_id);
+ zlog_err("%s session for client %" PRIu64 " failed!",
+ create ? "Creating" : "Destroying", client_id);
return;
}
- zlog_err("%s session for client %llu successfully!",
- create ? "Created" : "Destroyed",
- (unsigned long long)client_id);
+ VTY_DBG("%s session for client %" PRIu64 " successfully",
+ create ? "Created" : "Destroyed", client_id);
if (create)
vty->mgmt_session_id = session_id;
}
vty = (struct vty *)session_ctx;
if (!success) {
- zlog_err("%socking for DS %u failed! Err: '%s'",
+ zlog_err("%socking for DS %u failed, Err: '%s'",
lock_ds ? "L" : "Unl", ds_id, errmsg_if_any);
- vty_out(vty, "ERROR: %socking for DS %u failed! Err: '%s'\n",
+ vty_out(vty, "ERROR: %socking for DS %u failed, Err: '%s'\n",
lock_ds ? "L" : "Unl", ds_id, errmsg_if_any);
} else {
- zlog_err("%socked DS %u successfully!", lock_ds ? "L" : "Unl",
- ds_id);
+ VTY_DBG("%socked DS %u successfully", lock_ds ? "L" : "Unl",
+ ds_id);
}
vty_mgmt_resume_response(vty, success);
vty = (struct vty *)session_ctx;
if (!success) {
- zlog_err(
- "SET_CONFIG request for client 0x%llx failed! Error: '%s'",
- (unsigned long long)client_id,
- errmsg_if_any ? errmsg_if_any : "Unknown");
- vty_out(vty, "ERROR: SET_CONFIG request failed! Error: %s\n",
+ zlog_err("SET_CONFIG request for client 0x%" PRIx64
+ " failed, Error: '%s'",
+ client_id, errmsg_if_any ? errmsg_if_any : "Unknown");
+ vty_out(vty, "ERROR: SET_CONFIG request failed, Error: %s\n",
errmsg_if_any ? errmsg_if_any : "Unknown");
} else {
- zlog_err(
- "SET_CONFIG request for client 0x%llx req-id %llu was successfull!",
- (unsigned long long)client_id,
- (unsigned long long)req_id);
+ VTY_DBG("SET_CONFIG request for client 0x%" PRIx64
+ " req-id %" PRIu64 " was successfull",
+ client_id, req_id);
}
vty_mgmt_resume_response(vty, success);
vty = (struct vty *)session_ctx;
if (!success) {
- zlog_err(
- "COMMIT_CONFIG request for client 0x%llx failed! Error: '%s'",
- (unsigned long long)client_id,
- errmsg_if_any ? errmsg_if_any : "Unknown");
- vty_out(vty, "ERROR: COMMIT_CONFIG request failed! Error: %s\n",
+ zlog_err("COMMIT_CONFIG request for client 0x%" PRIx64
+ " failed, Error: '%s'",
+ client_id, errmsg_if_any ? errmsg_if_any : "Unknown");
+ vty_out(vty, "ERROR: COMMIT_CONFIG request failed, Error: %s\n",
errmsg_if_any ? errmsg_if_any : "Unknown");
} else {
- zlog_err(
- "COMMIT_CONFIG request for client 0x%llx req-id %llu was successfull!",
- (unsigned long long)client_id,
- (unsigned long long)req_id);
+ VTY_DBG("COMMIT_CONFIG request for client 0x%" PRIx64
+ " req-id %" PRIu64 " was successfull",
+ client_id, req_id);
if (errmsg_if_any)
vty_out(vty, "MGMTD: %s\n", errmsg_if_any);
}
vty = (struct vty *)session_ctx;
if (!success) {
- zlog_err(
- "GET_DATA request for client 0x%llx failed! Error: '%s'",
- (unsigned long long)client_id,
- errmsg_if_any ? errmsg_if_any : "Unknown");
- vty_out(vty, "ERROR: GET_DATA request failed! Error: %s\n",
+ zlog_err("GET_DATA request for client 0x%" PRIx64
+ " failed, Error: '%s'",
+ client_id, errmsg_if_any ? errmsg_if_any : "Unknown");
+ vty_out(vty, "ERROR: GET_DATA request failed, Error: %s\n",
errmsg_if_any ? errmsg_if_any : "Unknown");
vty_mgmt_resume_response(vty, success);
return MGMTD_INTERNAL_ERROR;
}
- zlog_debug(
- "GET_DATA request for client 0x%llx req-id %llu was successfull!",
- (unsigned long long)client_id, (unsigned long long)req_id);
+ VTY_DBG("GET_DATA request succeeded, client 0x%" PRIx64
+ " req-id %" PRIu64,
+ client_id, req_id);
if (req_id != mgmt_last_req_id) {
mgmt_last_req_id = req_id;
ret = mgmt_fe_lock_ds(mgmt_lib_hndl, vty->mgmt_session_id,
vty->mgmt_req_id, ds_id, lock);
if (ret != MGMTD_SUCCESS) {
- zlog_err(
- "Failed to send %sLOCK-DS-REQ to MGMTD for req-id %llu.",
- lock ? "" : "UN",
- (unsigned long long)vty->mgmt_req_id);
- vty_out(vty, "Failed to send %sLOCK-DS-REQ to MGMTD!",
+ zlog_err("Failed sending %sLOCK-DS-REQ req-id %" PRIu64,
+ lock ? "" : "UN", vty->mgmt_req_id);
+ vty_out(vty, "Failed to send %sLOCK-DS-REQ to MGMTD!\n",
lock ? "" : "UN");
return -1;
}
MGMTD_DS_RUNNING) != MGMTD_SUCCESS) {
zlog_err("Failed to send %d Config Xpaths to MGMTD!!",
(int)indx);
+ vty_out(vty, "Failed to send SETCFG-REQ to MGMTD!\n");
return -1;
}
MGMTD_DS_CANDIDATE, MGMTD_DS_RUNNING, validate_only,
abort);
if (ret != MGMTD_SUCCESS) {
- zlog_err(
- "Failed to send COMMIT-REQ to MGMTD for req-id %llu.",
- (unsigned long long)vty->mgmt_req_id);
- vty_out(vty, "Failed to send COMMIT-REQ to MGMTD!");
+ zlog_err("Failed sending COMMIT-REQ req-id %" PRIu64,
+ vty->mgmt_req_id);
+ vty_out(vty, "Failed to send COMMIT-REQ to MGMTD!\n");
return -1;
}
num_req);
if (ret != MGMTD_SUCCESS) {
- zlog_err("Failed to send GET-CONFIG to MGMTD for req-id %llu.",
- (unsigned long long)vty->mgmt_req_id);
- vty_out(vty, "Failed to send GET-CONFIG to MGMTD!");
+ zlog_err(
+ "Failed to send GET-CONFIG to MGMTD for req-id %" PRIu64
+ ".",
+ vty->mgmt_req_id);
+ vty_out(vty, "Failed to send GET-CONFIG to MGMTD!\n");
return -1;
}
vty->mgmt_req_id, datastore, getreq, num_req);
if (ret != MGMTD_SUCCESS) {
- zlog_err("Failed to send GET-DATA to MGMTD for req-id %llu.",
- (unsigned long long)vty->mgmt_req_id);
- vty_out(vty, "Failed to send GET-DATA to MGMTD!");
+ zlog_err("Failed to send GET-DATA to MGMTD for req-id %" PRIu64
+ ".",
+ vty->mgmt_req_id);
+ vty_out(vty, "Failed to send GET-DATA to MGMTD!\n");
return -1;
}
install_element(CONFIG_NODE, &log_commands_cmd);
if (do_command_logging) {
- do_log_commands = true;
- do_log_commands_perm = true;
+ vty_log_commands = true;
+ vty_log_commands_perm = true;
}
install_element(ENABLE_NODE, &terminal_monitor_cmd);
#endif
extern struct nb_config *vty_mgmt_candidate_config;
+extern bool vty_log_commands;
/* Prototypes. */
extern void vty_init(struct event_loop *m, bool do_command_logging);
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 *);
}
}
+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;
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).
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__,
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__,
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);
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);
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);
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);
}
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;
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));
memset(api, 0, sizeof(*api));
+ api->safi = SAFI_UNICAST;
+
STREAM_GETL(s, api->cap);
switch (api->cap) {
case ZEBRA_CLIENT_GR_CAPABILITIES:
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);
*/
#include <zebra.h>
+#include "debug.h"
#include "mgmtd/mgmt.h"
#include "mgmtd/mgmt_be_server.h"
#include "mgmtd/mgmt_be_adapter.h"
#include "mgmtd/mgmt_history.h"
#include "mgmtd/mgmt_memory.h"
-bool mgmt_debug_be;
-bool mgmt_debug_fe;
-bool mgmt_debug_ds;
-bool mgmt_debug_txn;
+struct debug mgmt_debug_be = {.desc = "Management backend adapater"};
+struct debug mgmt_debug_ds = {.desc = "Management datastore"};
+struct debug mgmt_debug_fe = {.desc = "Management frontend adapater"};
+struct debug mgmt_debug_txn = {.desc = "Management transaction"};
/* MGMTD process wide configuration. */
static struct mgmt_master mgmt_master;
#ifndef _FRR_MGMTD_H
#define _FRR_MGMTD_H
+#include "debug.h"
#include "vrf.h"
#include "defaults.h"
#include "stream.h"
#define MGMTD_SOCKET_BUF_SIZE 65535
#define MGMTD_MAX_COMMIT_LIST 10
-extern bool mgmt_debug_be;
-extern bool mgmt_debug_fe;
-extern bool mgmt_debug_ds;
-extern bool mgmt_debug_txn;
+extern struct debug mgmt_debug_be;
+extern struct debug mgmt_debug_ds;
+extern struct debug mgmt_debug_fe;
+extern struct debug mgmt_debug_txn;
+
+#define MGMT_DEBUG_BE_CHECK() DEBUG_MODE_CHECK(&mgmt_debug_be, DEBUG_MODE_ALL)
+#define MGMT_DEBUG_DS_CHECK() DEBUG_MODE_CHECK(&mgmt_debug_ds, DEBUG_MODE_ALL)
+#define MGMT_DEBUG_FE_CHECK() DEBUG_MODE_CHECK(&mgmt_debug_fe, DEBUG_MODE_ALL)
+#define MGMT_DEBUG_TXN_CHECK() DEBUG_MODE_CHECK(&mgmt_debug_tx, DEBUG_MODE_ALL)
struct mgmt_txn_ctx;
};
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)
extern void mgmt_init(void);
extern void mgmt_vty_init(void);
-static inline char *mgmt_realtime_to_string(struct timeval *tv, char *buf,
- size_t sz)
-{
- struct tm tm;
- size_t n;
-
- localtime_r((const time_t *)&tv->tv_sec, &tm);
- n = strftime(buf, sz, "%Y-%m-%dT%H:%M:%S", &tm);
- snprintf(&buf[n], sz - n, ",%06u000", (unsigned int)tv->tv_usec);
- return buf;
-}
-
#endif /* _FRR_MGMTD_H */
#include "mgmt_be_client.h"
#include "mgmtd/mgmt_be_adapter.h"
-#ifdef REDIRECT_DEBUG_TO_STDERR
-#define MGMTD_BE_ADAPTER_DBG(fmt, ...) \
- fprintf(stderr, "%s: " fmt "\n", __func__, ##__VA_ARGS__)
-#define MGMTD_BE_ADAPTER_ERR(fmt, ...) \
- fprintf(stderr, "%s: ERROR, " fmt "\n", __func__, ##__VA_ARGS__)
-#else /* REDIRECT_DEBUG_TO_STDERR */
-#define MGMTD_BE_ADAPTER_DBG(fmt, ...) \
- do { \
- if (mgmt_debug_be) \
- zlog_debug("%s: " fmt, __func__, ##__VA_ARGS__); \
- } while (0)
+#define MGMTD_BE_ADAPTER_DBG(fmt, ...) \
+ DEBUGD(&mgmt_debug_be, "%s:" fmt, __func__, ##__VA_ARGS__)
#define MGMTD_BE_ADAPTER_ERR(fmt, ...) \
zlog_err("%s: ERROR: " fmt, __func__, ##__VA_ARGS__)
-#endif /* REDIRECT_DEBUG_TO_STDERR */
#define FOREACH_ADAPTER_IN_LIST(adapter) \
frr_each_safe (mgmt_be_adapters, &mgmt_be_adapters, (adapter))
.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}},
};
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
*/
&adapter->mstate, be_msg,
mgmtd__be_message__get_packed_size(be_msg),
(size_t(*)(void *, void *))mgmtd__be_message__pack,
- mgmt_debug_be);
+ MGMT_DEBUG_BE_CHECK());
mgmt_be_adapter_sched_msg_write(adapter);
return rv;
}
struct mgmt_be_client_adapter *adapter = EVENT_ARG(thread);
if (mgmt_msg_procbufs(&adapter->mstate, mgmt_be_adapter_process_msg,
- adapter, mgmt_debug_be))
+ adapter, MGMT_DEBUG_BE_CHECK()))
mgmt_be_adapter_register_event(adapter, MGMTD_BE_PROC_MSG);
}
adapter = (struct mgmt_be_client_adapter *)EVENT_ARG(thread);
- rv = mgmt_msg_read(&adapter->mstate, adapter->conn_fd, mgmt_debug_be);
+ rv = mgmt_msg_read(&adapter->mstate, adapter->conn_fd,
+ MGMT_DEBUG_BE_CHECK());
if (rv == MSR_DISCONNECT) {
mgmt_be_adapter_disconnect(adapter);
return;
struct mgmt_be_client_adapter *adapter = EVENT_ARG(thread);
enum mgmt_msg_wsched rv;
- rv = mgmt_msg_write(&adapter->mstate, adapter->conn_fd, mgmt_debug_be);
+ rv = mgmt_msg_write(&adapter->mstate, adapter->conn_fd,
+ MGMT_DEBUG_BE_CHECK());
if (rv == MSW_SCHED_STREAM)
mgmt_be_adapter_register_event(adapter, MGMTD_BE_CONN_WRITE);
else if (rv == MSW_DISCONNECT)
#include "mgmtd/mgmt_be_server.h"
#include "mgmtd/mgmt_be_adapter.h"
-#ifdef REDIRECT_DEBUG_TO_STDERR
-#define MGMTD_BE_SRVR_DBG(fmt, ...) \
- fprintf(stderr, "%s: " fmt "\n", __func__, ##__VA_ARGS__)
-#define MGMTD_BE_SRVR_ERR(fmt, ...) \
- fprintf(stderr, "%s: ERROR, " fmt "\n", __func__, ##__VA_ARGS__)
-#else /* REDIRECT_DEBUG_TO_STDERR */
#define MGMTD_BE_SRVR_DBG(fmt, ...) \
- do { \
- if (mgmt_debug_be) \
- zlog_debug("%s: " fmt, __func__, ##__VA_ARGS__); \
- } while (0)
+ DEBUGD(&mgmt_debug_be, "%s:" fmt, __func__, ##__VA_ARGS__)
#define MGMTD_BE_SRVR_ERR(fmt, ...) \
zlog_err("%s: ERROR: " fmt, __func__, ##__VA_ARGS__)
-#endif /* REDIRECT_DEBUG_TO_STDERR */
static int mgmt_be_listen_fd = -1;
static struct event_loop *mgmt_be_listen_tm;
return;
mgmt_be_server_start_failed:
- if (sock)
+ if (sock > 0)
close(sock);
mgmt_be_listen_fd = -1;
#include "mgmtd/mgmt_txn.h"
#include "libyang/libyang.h"
-#ifdef REDIRECT_DEBUG_TO_STDERR
#define MGMTD_DS_DBG(fmt, ...) \
- fprintf(stderr, "%s: " fmt "\n", __func__, ##__VA_ARGS__)
-#define MGMTD_DS_ERR(fmt, ...) \
- fprintf(stderr, "%s: ERROR, " fmt "\n", __func__, ##__VA_ARGS__)
-#else /* REDIRECT_DEBUG_TO_STDERR */
-#define MGMTD_DS_DBG(fmt, ...) \
- do { \
- if (mgmt_debug_ds) \
- zlog_err("%s: " fmt, __func__, ##__VA_ARGS__); \
- } while (0)
+ DEBUGD(&mgmt_debug_ds, "%s:" fmt, __func__, ##__VA_ARGS__)
#define MGMTD_DS_ERR(fmt, ...) \
zlog_err("%s: ERROR: " fmt, __func__, ##__VA_ARGS__)
-#endif /* REDIRECT_DEBUG_TO_STDERR */
struct mgmt_ds_ctx {
Mgmtd__DatastoreId ds_id;
struct mgmt_ds_ctx *dst)
{
struct lyd_node *dst_dnode, *src_dnode;
- struct ly_out *out;
if (!src || !dst)
return -1;
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;
{
int ret;
struct lyd_node **dst_dnode, *src_dnode;
- struct ly_out *out;
if (!src || !dst)
return -1;
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;
}
void mgmt_ds_reset_candidate(void)
{
struct lyd_node *dnode = mm->candidate_ds->root.cfg_root->dnode;
+
if (dnode)
yang_dnode_free(dnode);
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!");
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;
*/
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);
#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)++)
#define MGMTD_MAX_COMMIT_LIST 10
-#define MGMTD_MD5_HASH_LEN 16
-#define MGMTD_MD5_HASH_STR_HEX_LEN 33
#define MGMTD_COMMIT_FILE_PATH DAEMON_DB_DIR "/commit-%s.json"
#define MGMTD_COMMIT_INDEX_FILE_NAME DAEMON_DB_DIR "/commit-index.dat"
-#define MGMTD_COMMIT_TIME_STR_LEN 100
extern struct nb_config *running_config;
#include "mgmtd/mgmt_memory.h"
#include "mgmtd/mgmt_fe_adapter.h"
-#ifdef REDIRECT_DEBUG_TO_STDERR
-#define MGMTD_FE_ADAPTER_DBG(fmt, ...) \
- fprintf(stderr, "%s: " fmt "\n", __func__, ##__VA_ARGS__)
-#define MGMTD_FE_ADAPTER_ERR(fmt, ...) \
- fprintf(stderr, "%s: ERROR, " fmt "\n", __func__, ##__VA_ARGS__)
-#else /* REDIRECT_DEBUG_TO_STDERR */
-#define MGMTD_FE_ADAPTER_DBG(fmt, ...) \
- do { \
- if (mgmt_debug_fe) \
- zlog_debug("%s: " fmt, __func__, ##__VA_ARGS__); \
- } while (0)
+#define MGMTD_FE_ADAPTER_DBG(fmt, ...) \
+ DEBUGD(&mgmt_debug_fe, "%s:" fmt, __func__, ##__VA_ARGS__)
#define MGMTD_FE_ADAPTER_ERR(fmt, ...) \
zlog_err("%s: ERROR: " fmt, __func__, ##__VA_ARGS__)
-#endif /* REDIRECT_DEBUG_TO_STDERR */
#define FOREACH_ADAPTER_IN_LIST(adapter) \
frr_each_safe (mgmt_fe_adapters, &mgmt_fe_adapters, (adapter))
&adapter->mstate, fe_msg,
mgmtd__fe_message__get_packed_size(fe_msg),
(size_t(*)(void *, void *))mgmtd__fe_message__pack,
- mgmt_debug_fe);
+ MGMT_DEBUG_FE_CHECK());
mgmt_fe_adapter_sched_msg_write(adapter);
return rv;
}
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);
}
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
*/
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;
}
struct mgmt_fe_client_adapter *adapter = EVENT_ARG(thread);
if (mgmt_msg_procbufs(&adapter->mstate, mgmt_fe_adapter_process_msg,
- adapter, mgmt_debug_fe))
+ adapter, MGMT_DEBUG_FE_CHECK()))
mgmt_fe_adapter_register_event(adapter, MGMTD_FE_PROC_MSG);
}
struct mgmt_fe_client_adapter *adapter = EVENT_ARG(thread);
enum mgmt_msg_rsched rv;
- rv = mgmt_msg_read(&adapter->mstate, adapter->conn_fd, mgmt_debug_fe);
+ rv = mgmt_msg_read(&adapter->mstate, adapter->conn_fd,
+ MGMT_DEBUG_FE_CHECK());
if (rv == MSR_DISCONNECT) {
mgmt_fe_adapter_disconnect(adapter);
return;
struct mgmt_fe_client_adapter *adapter = EVENT_ARG(thread);
enum mgmt_msg_wsched rv;
- rv = mgmt_msg_write(&adapter->mstate, adapter->conn_fd, mgmt_debug_fe);
+ rv = mgmt_msg_write(&adapter->mstate, adapter->conn_fd,
+ MGMT_DEBUG_FE_CHECK());
if (rv == MSW_SCHED_STREAM)
mgmt_fe_adapter_register_event(adapter, MGMTD_FE_CONN_WRITE);
else if (rv == MSW_DISCONNECT)
mgmt_fe_adapter_cmt_stats_write(struct vty *vty,
struct mgmt_fe_client_adapter *adapter)
{
- char buf[100] = {0};
+ char buf[MGMT_LONG_TIME_MAX_LEN];
if (!mm->perf_stats_en)
return;
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,
mgmt_fe_adapter_setcfg_stats_write(struct vty *vty,
struct mgmt_fe_client_adapter *adapter)
{
- char buf[100] = {0};
+ char buf[MGMT_LONG_TIME_MAX_LEN];
if (!mm->perf_stats_en)
return;
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)));
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));
}
}
}
#include "mgmtd/mgmt_fe_server.h"
#include "mgmtd/mgmt_fe_adapter.h"
-#ifdef REDIRECT_DEBUG_TO_STDERR
-#define MGMTD_FE_SRVR_DBG(fmt, ...) \
- fprintf(stderr, "%s: " fmt "\n", __func__, ##__VA_ARGS__)
-#define MGMTD_FE_SRVR_ERR(fmt, ...) \
- fprintf(stderr, "%s: ERROR, " fmt "\n", __func__, ##__VA_ARGS__)
-#else /* REDIRECT_DEBUG_TO_STDERR */
#define MGMTD_FE_SRVR_DBG(fmt, ...) \
- do { \
- if (mgmt_debug_fe) \
- zlog_debug("%s: " fmt, __func__, ##__VA_ARGS__); \
- } while (0)
+ DEBUGD(&mgmt_debug_fe, "%s:" fmt, __func__, ##__VA_ARGS__)
#define MGMTD_FE_SRVR_ERR(fmt, ...) \
zlog_err("%s: ERROR: " fmt, __func__, ##__VA_ARGS__)
-#endif /* REDIRECT_DEBUG_TO_STDERR */
static int mgmt_fe_listen_fd = -1;
static struct event_loop *mgmt_fe_listen_tm;
return;
mgmt_fe_server_start_failed:
- if (sock)
+ if (sock > 0)
close(sock);
mgmt_fe_listen_fd = -1;
struct mgmt_cmt_info_t {
struct mgmt_cmt_infos_item cmts;
- char cmtid_str[MGMTD_MD5_HASH_STR_HEX_LEN];
- char time_str[MGMTD_COMMIT_TIME_STR_LEN];
+ char cmtid_str[MGMT_SHORT_TIME_MAX_LEN];
+ char time_str[MGMT_LONG_TIME_MAX_LEN];
char cmt_json_file[PATH_MAX];
};
* 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)
{
zlog_err("Old commit info deletion failed");
}
-static void mgmt_history_hash(const char *input_str, char *hash)
+static struct mgmt_cmt_info_t *mgmt_history_new_cmt_info(void)
{
- int i;
- unsigned char digest[MGMTD_MD5_HASH_LEN];
- MD5_CTX ctx;
-
- memset(&ctx, 0, sizeof(ctx));
- MD5Init(&ctx);
- MD5Update(&ctx, input_str, strlen(input_str));
- MD5Final(digest, &ctx);
-
- for (i = 0; i < MGMTD_MD5_HASH_LEN; i++)
- snprintf(&hash[i * 2], MGMTD_MD5_HASH_STR_HEX_LEN, "%02x",
- (unsigned int)digest[i]);
+ struct mgmt_cmt_info_t *new;
+ struct timespec tv;
+ struct tm tm;
+
+ new = XCALLOC(MTYPE_MGMTD_CMT_INFO, sizeof(struct mgmt_cmt_info_t));
+
+ clock_gettime(CLOCK_REALTIME, &tv);
+ localtime_r(&tv.tv_sec, &tm);
+
+ mgmt_time_to_string(&tv, true, new->time_str, sizeof(new->time_str));
+ mgmt_time_to_string(&tv, false, new->cmtid_str, sizeof(new->cmtid_str));
+ snprintf(new->cmt_json_file, sizeof(new->cmt_json_file),
+ MGMTD_COMMIT_FILE_PATH, new->cmtid_str);
+
+ return new;
}
static struct mgmt_cmt_info_t *mgmt_history_create_cmt_rec(void)
{
- struct mgmt_cmt_info_t *new;
+ struct mgmt_cmt_info_t *new = mgmt_history_new_cmt_info();
struct mgmt_cmt_info_t *cmt_info;
struct mgmt_cmt_info_t *last_cmt_info = NULL;
- struct timeval cmt_recd_tv;
-
- new = XCALLOC(MTYPE_MGMTD_CMT_INFO, sizeof(struct mgmt_cmt_info_t));
- gettimeofday(&cmt_recd_tv, NULL);
- 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),
- MGMTD_COMMIT_FILE_PATH, new->cmtid_str);
if (mgmt_cmt_infos_count(&mm->cmts) == MGMTD_MAX_COMMIT_LIST) {
FOREACH_CMT_REC (mm, cmt_info)
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;
FOREACH_CMT_REC (mm, cmt_info) {
- if (strncmp(cmt_info->cmtid_str, cmtid_str,
- MGMTD_MD5_HASH_STR_HEX_LEN) == 0)
+ if (strcmp(cmt_info->cmtid_str, cmtid_str) == 0)
return cmt_info;
}
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);
}
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);
+ if (strcmp(cmt_info->cmtid_str, cmtid_str) == 0) {
+ ret = mgmt_history_rollback_to_cmt(vty, cmt_info,
+ false);
return ret;
}
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;
}
int slno = 0;
vty_out(vty, "Last 10 commit history:\n");
- vty_out(vty, " Sl.No\tCommit-ID(HEX)\t\t\t Commit-Record-Time\n");
+ vty_out(vty, "Slot Commit-ID Commit-Record-Time\n");
FOREACH_CMT_REC (mm, cmt_info) {
- vty_out(vty, " %d\t%s %s\n", slno, cmt_info->cmtid_str,
+ vty_out(vty, "%4d %23s %s\n", slno, cmt_info->cmtid_str,
cmt_info->time_str);
slno++;
}
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();
}
// 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_
extern void mgmt_history_destroy(void);
extern void mgmt_history_init(void);
+/*
+ * 012345678901234567890123456789
+ * 2023-12-31T12:12:12,012345678
+ * 20231231121212012345678
+ */
+#define MGMT_LONG_TIME_FMT "%Y-%m-%dT%H:%M:%S"
+#define MGMT_LONG_TIME_MAX_LEN 30
+#define MGMT_SHORT_TIME_FMT "%Y%m%d%H%M%S"
+#define MGMT_SHORT_TIME_MAX_LEN 24
+
+static inline const char *
+mgmt_time_to_string(struct timespec *tv, bool long_fmt, char *buffer, size_t sz)
+{
+ struct tm tm;
+ size_t n;
+
+ localtime_r(&tv->tv_sec, &tm);
+
+ if (long_fmt) {
+ n = strftime(buffer, sz, MGMT_LONG_TIME_FMT, &tm);
+ snprintf(&buffer[n], sz - n, ",%09lu", tv->tv_nsec);
+ } else {
+ n = strftime(buffer, sz, MGMT_SHORT_TIME_FMT, &tm);
+ snprintf(&buffer[n], sz - n, "%09lu", tv->tv_nsec);
+ }
+
+ return buffer;
+}
+
+static inline const char *mgmt_realtime_to_string(struct timeval *tv, char *buf,
+ size_t sz)
+{
+ struct timespec ts = {.tv_sec = tv->tv_sec,
+ .tv_nsec = tv->tv_usec * 1000};
+
+ return mgmt_time_to_string(&ts, true, buf, sz);
+}
+
#endif /* _FRR_MGMTD_HISTORY_H_ */
#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'},
#include "mgmtd/mgmt_memory.h"
#include "mgmtd/mgmt_txn.h"
-#ifdef REDIRECT_DEBUG_TO_STDERR
-#define MGMTD_TXN_DBG(fmt, ...) \
- fprintf(stderr, "%s: " fmt "\n", __func__, ##__VA_ARGS__)
-#define MGMTD_TXN_ERR(fmt, ...) \
- fprintf(stderr, "%s: ERROR, " fmt "\n", __func__, ##__VA_ARGS__)
-#else /* REDIRECT_DEBUG_TO_STDERR */
-#define MGMTD_TXN_DBG(fmt, ...) \
- do { \
- if (mgmt_debug_txn) \
- zlog_err("%s: " fmt, __func__, ##__VA_ARGS__); \
- } while (0)
-#define MGMTD_TXN_ERR(fmt, ...) \
+#define MGMTD_TXN_DBG(fmt, ...) \
+ DEBUGD(&mgmt_debug_txn, "%s:" fmt, __func__, ##__VA_ARGS__)
+#define MGMTD_TXN_ERR(fmt, ...) \
zlog_err("%s: ERROR: " fmt, __func__, ##__VA_ARGS__)
-#endif /* REDIRECT_DEBUG_TO_STDERR */
#define MGMTD_TXN_LOCK(txn) mgmt_txn_lock(txn, __FILE__, __LINE__)
#define MGMTD_TXN_UNLOCK(txn) mgmt_txn_unlock(txn, __FILE__, __LINE__)
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) {
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) {
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;
}
#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"
return CMD_SUCCESS;
}
-static int config_write_mgmt_debug(struct vty *vty);
+int config_write_mgmt_debug(struct vty *vty);
static struct cmd_node debug_node = {
.name = "debug",
.node = DEBUG_NODE,
.config_write = config_write_mgmt_debug,
};
-static int config_write_mgmt_debug(struct vty *vty)
+static int 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;
- if (n == 4) {
- vty_out(vty, "debug mgmt all\n");
+ uint32_t mode = config ? DEBUG_MODE_CONF : DEBUG_MODE_ALL;
+ bool be = DEBUG_MODE_CHECK(&mgmt_debug_be, mode);
+ bool ds = DEBUG_MODE_CHECK(&mgmt_debug_ds, mode);
+ bool fe = DEBUG_MODE_CHECK(&mgmt_debug_fe, mode);
+ bool txn = DEBUG_MODE_CHECK(&mgmt_debug_txn, mode);
+
+ if (!(be || ds || fe || txn))
return 0;
- }
vty_out(vty, "debug mgmt");
- if (mgmt_debug_be)
+ if (be)
vty_out(vty, " backend");
- if (mgmt_debug_ds)
+ if (ds)
vty_out(vty, " datastore");
- if (mgmt_debug_fe)
+ if (fe)
vty_out(vty, " frontend");
- if (mgmt_debug_txn)
+ if (txn)
vty_out(vty, " transaction");
vty_out(vty, "\n");
return 0;
}
-DEFPY(debug_mgmt,
- debug_mgmt_cmd,
- "[no$no] debug mgmt <all$all|{backend$be|datastore$ds|frontend$fe|transaction$txn}>",
- NO_STR
- DEBUG_STR
- MGMTD_STR
- "All debug\n"
- "Back-end debug\n"
+int config_write_mgmt_debug(struct vty *vty)
+{
+ return write_mgmt_debug_helper(vty, true);
+}
+
+DEFPY_NOSH(show_debugging_mgmt, show_debugging_mgmt_cmd,
+ "show debugging [mgmt]", SHOW_STR DEBUG_STR "MGMT Information\n")
+{
+ vty_out(vty, "MGMT debugging status:\n");
+
+ write_mgmt_debug_helper(vty, false);
+
+ cmd_show_lib_debugs(vty);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(debug_mgmt, debug_mgmt_cmd,
+ "[no$no] debug mgmt {backend$be|datastore$ds|frontend$fe|transaction$txn}",
+ NO_STR DEBUG_STR MGMTD_STR
+ "Backend debug\n"
"Datastore debug\n"
- "Front-end debug\n"
+ "Frontend debug\n"
"Transaction debug\n")
{
- bool set = !no;
- if (all)
- be = fe = ds = txn = set ? all : NULL;
+ uint32_t mode = DEBUG_NODE2MODE(vty->node);
if (be)
- mgmt_debug_be = set;
+ DEBUG_MODE_SET(&mgmt_debug_be, mode, !no);
if (ds)
- mgmt_debug_ds = set;
+ DEBUG_MODE_SET(&mgmt_debug_ds, mode, !no);
if (fe)
- mgmt_debug_fe = set;
+ DEBUG_MODE_SET(&mgmt_debug_fe, mode, !no);
if (txn)
- mgmt_debug_txn = set;
+ DEBUG_MODE_SET(&mgmt_debug_txn, mode, !no);
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)
{
/*
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);
install_element(ENABLE_NODE, &mgmt_performance_measurement_cmd);
install_element(ENABLE_NODE, &mgmt_reset_performance_stats_cmd);
+ install_element(ENABLE_NODE, &show_debugging_mgmt_cmd);
+
+ mgmt_fe_client_lib_vty_init();
/*
* TODO: Register and handlers for auto-completion here.
*/
+++ /dev/null
-/*
- * MGMTD VTY Interface
- * Copyright (C) 2021 Vmware, Inc.
- * Pushpasis Sarkar <spushpasis@vmware.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; see the file COPYING; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <zebra.h>
-
-#include "command.h"
-#include "json.h"
-#include "mgmtd/mgmt.h"
-#include "mgmtd/mgmt_be_server.h"
-#include "mgmtd/mgmt_be_adapter.h"
-#include "mgmtd/mgmt_fe_server.h"
-#include "mgmtd/mgmt_fe_adapter.h"
-#include "mgmtd/mgmt_ds.h"
-#include "mgmtd/mgmt_history.h"
-
-#include "mgmtd/mgmt_vty_clippy.c"
-
-DEFPY(show_mgmt_be_adapter,
- show_mgmt_be_adapter_cmd,
- "show mgmt backend-adapter all",
- SHOW_STR
- MGMTD_STR
- MGMTD_BE_ADAPTER_STR
- "Display all Backend Adapters\n")
-{
- mgmt_be_adapter_status_write(vty);
-
- return CMD_SUCCESS;
-}
-
-DEFPY(show_mgmt_be_xpath_reg,
- show_mgmt_be_xpath_reg_cmd,
- "show mgmt backend-yang-xpath-registry",
- SHOW_STR
- MGMTD_STR
- "Backend Adapter YANG Xpath Registry\n")
-{
- mgmt_be_xpath_register_write(vty);
-
- return CMD_SUCCESS;
-}
-
-DEFPY(show_mgmt_fe_adapter,
- show_mgmt_fe_adapter_cmd,
- "show mgmt frontend-adapter all",
- SHOW_STR MGMTD_STR MGMTD_FE_ADAPTER_STR "Display all Frontend Adapters\n")
-{
- mgmt_fe_adapter_status_write(vty, false);
-
- return CMD_SUCCESS;
-}
-
-DEFPY(show_mgmt_fe_adapter_detail, show_mgmt_fe_adapter_detail_cmd,
- "show mgmt frontend-adapter all detail",
- SHOW_STR MGMTD_STR MGMTD_FE_ADAPTER_STR
- "Display all Frontend Adapters\n"
- "Details of commit stats\n")
-{
- mgmt_fe_adapter_status_write(vty, true);
-
- return CMD_SUCCESS;
-}
-
-DEFPY_HIDDEN(mgmt_performance_measurement,
- mgmt_performance_measurement_cmd,
- "[no] mgmt performance-measurement",
- NO_STR
- MGMTD_STR
- "Enable performance measurement\n")
-{
- if (no)
- mgmt_fe_adapter_perf_measurement(vty, false);
- else
- mgmt_fe_adapter_perf_measurement(vty, true);
-
- return CMD_SUCCESS;
-}
-
-DEFPY(mgmt_reset_performance_stats,
- mgmt_reset_performance_stats_cmd,
- "mgmt reset-statistics",
- MGMTD_STR
- "Reset the Performance measurement statistics\n")
-{
- mgmt_fe_adapter_reset_perf_stats(vty);
-
- return CMD_SUCCESS;
-}
-
-DEFPY(show_mgmt_txn,
- show_mgmt_txn_cmd,
- "show mgmt transaction all",
- SHOW_STR
- MGMTD_STR
- MGMTD_TXN_STR
- "Display all Transactions\n")
-{
- mgmt_txn_status_write(vty);
-
- return CMD_SUCCESS;
-}
-
-DEFPY(show_mgmt_ds,
- show_mgmt_ds_cmd,
- "show mgmt datastore [all|candidate|operational|running]$dsname",
- SHOW_STR
- MGMTD_STR
- MGMTD_DS_STR
- "All datastores (default)\n"
- "Candidate datastore\n"
- "Operational datastore\n"
- "Running datastore\n")
-{
- struct mgmt_ds_ctx *ds_ctx;
-
- if (!dsname || dsname[0] == 'a') {
- mgmt_ds_status_write(vty);
- return CMD_SUCCESS;
- }
- ds_ctx = mgmt_ds_get_ctx_by_id(mm, mgmt_ds_name2id(dsname));
- if (!ds_ctx) {
- vty_out(vty, "ERROR: Could not access %s datastore!\n", dsname);
- return CMD_ERR_NO_MATCH;
- }
- mgmt_ds_status_write_one(vty, ds_ctx);
-
- return CMD_SUCCESS;
-}
-
-DEFPY(mgmt_commit,
- mgmt_commit_cmd,
- "mgmt commit <check|apply|abort>$type",
- MGMTD_STR
- "Commit action\n"
- "Validate the set of config commands\n"
- "Validate and apply the set of config commands\n"
- "Abort and drop the set of config commands recently added\n")
-{
- bool validate_only = type[0] == 'c';
- bool abort = type[1] == 'b';
-
- if (vty_mgmt_send_commit_config(vty, validate_only, abort) != 0)
- return CMD_WARNING_CONFIG_FAILED;
- return CMD_SUCCESS;
-}
-
-DEFPY(mgmt_set_config_data, mgmt_set_config_data_cmd,
- "mgmt set-config WORD$path VALUE",
- MGMTD_STR
- "Set configuration data\n"
- "XPath expression specifying the YANG data path\n"
- "Value of the data to set\n")
-{
- strlcpy(vty->cfg_changes[0].xpath, path,
- sizeof(vty->cfg_changes[0].xpath));
- vty->cfg_changes[0].value = value;
- vty->cfg_changes[0].operation = NB_OP_CREATE;
- vty->num_cfg_changes = 1;
-
- vty->no_implicit_commit = true;
- vty_mgmt_send_config_data(vty);
- vty->no_implicit_commit = false;
- return CMD_SUCCESS;
-}
-
-DEFPY(mgmt_delete_config_data, mgmt_delete_config_data_cmd,
- "mgmt delete-config WORD$path",
- MGMTD_STR
- "Delete configuration data\n"
- "XPath expression specifying the YANG data path\n")
-{
-
- strlcpy(vty->cfg_changes[0].xpath, path,
- sizeof(vty->cfg_changes[0].xpath));
- vty->cfg_changes[0].value = NULL;
- vty->cfg_changes[0].operation = NB_OP_DESTROY;
- vty->num_cfg_changes = 1;
-
- vty->no_implicit_commit = true;
- vty_mgmt_send_config_data(vty);
- vty->no_implicit_commit = false;
- return CMD_SUCCESS;
-}
-
-DEFPY(show_mgmt_get_config, show_mgmt_get_config_cmd,
- "show mgmt get-config [candidate|operational|running]$dsname WORD$path",
- SHOW_STR MGMTD_STR
- "Get configuration data from a specific configuration datastore\n"
- "Candidate datastore (default)\n"
- "Operational datastore\n"
- "Running datastore\n"
- "XPath expression specifying the YANG data path\n")
-{
- const char *xpath_list[VTY_MAXCFGCHANGES] = {0};
- Mgmtd__DatastoreId datastore = MGMTD_DS_CANDIDATE;
-
- if (dsname)
- datastore = mgmt_ds_name2id(dsname);
-
- xpath_list[0] = path;
- vty_mgmt_send_get_config(vty, datastore, xpath_list, 1);
- return CMD_SUCCESS;
-}
-
-DEFPY(show_mgmt_get_data, show_mgmt_get_data_cmd,
- "show mgmt get-data [candidate|operational|running]$dsname WORD$path",
- SHOW_STR MGMTD_STR
- "Get data from a specific datastore\n"
- "Candidate datastore\n"
- "Operational datastore (default)\n"
- "Running datastore\n"
- "XPath expression specifying the YANG data path\n")
-{
- const char *xpath_list[VTY_MAXCFGCHANGES] = {0};
- Mgmtd__DatastoreId datastore = MGMTD_DS_OPERATIONAL;
-
- if (dsname)
- datastore = mgmt_ds_name2id(dsname);
-
- xpath_list[0] = path;
- vty_mgmt_send_get_data(vty, datastore, xpath_list, 1);
- return CMD_SUCCESS;
-}
-
-DEFPY(show_mgmt_dump_data,
- show_mgmt_dump_data_cmd,
- "show mgmt datastore-contents [candidate|operational|running]$dsname [xpath WORD$path] [file WORD$filepath] <json|xml>$fmt",
- SHOW_STR
- MGMTD_STR
- "Get Datastore contents from a specific datastore\n"
- "Candidate datastore (default)\n"
- "Operational datastore\n"
- "Running datastore\n"
- "XPath expression specifying the YANG data path\n"
- "XPath string\n"
- "Dump the contents to a file\n"
- "Full path of the file\n"
- "json output\n"
- "xml output\n")
-{
- struct mgmt_ds_ctx *ds_ctx;
- Mgmtd__DatastoreId datastore = MGMTD_DS_CANDIDATE;
- LYD_FORMAT format = fmt[0] == 'j' ? LYD_JSON : LYD_XML;
- FILE *f = NULL;
-
- if (datastore)
- datastore = mgmt_ds_name2id(dsname);
-
- ds_ctx = mgmt_ds_get_ctx_by_id(mm, datastore);
- if (!ds_ctx) {
- vty_out(vty, "ERROR: Could not access datastore!\n");
- return CMD_ERR_NO_MATCH;
- }
-
- if (filepath) {
- f = fopen(filepath, "w");
- if (!f) {
- vty_out(vty,
- "Could not open file pointed by filepath %s\n",
- filepath);
- return CMD_SUCCESS;
- }
- }
-
- mgmt_ds_dump_tree(vty, ds_ctx, path, f, format);
-
- if (f)
- fclose(f);
- return CMD_SUCCESS;
-}
-
-DEFPY(show_mgmt_map_xpath,
- show_mgmt_map_xpath_cmd,
- "show mgmt yang-xpath-subscription WORD$path",
- SHOW_STR
- MGMTD_STR
- "Get YANG Backend Subscription\n"
- "XPath expression specifying the YANG data path\n")
-{
- mgmt_be_xpath_subscr_info_write(vty, path);
- return CMD_SUCCESS;
-}
-
-DEFPY(mgmt_load_config,
- mgmt_load_config_cmd,
- "mgmt load-config WORD$filepath <merge|replace>$type",
- MGMTD_STR
- "Load configuration onto Candidate Datastore\n"
- "Full path of the file\n"
- "Merge configuration with contents of Candidate Datastore\n"
- "Replace the existing contents of Candidate datastore\n")
-{
- bool merge = type[0] == 'm' ? true : false;
- struct mgmt_ds_ctx *ds_ctx;
- int ret;
-
- if (access(filepath, F_OK) == -1) {
- vty_out(vty, "ERROR: File %s : %s\n", filepath,
- strerror(errno));
- return CMD_ERR_NO_FILE;
- }
-
- ds_ctx = mgmt_ds_get_ctx_by_id(mm, MGMTD_DS_CANDIDATE);
- if (!ds_ctx) {
- vty_out(vty, "ERROR: Could not access Candidate datastore!\n");
- return CMD_ERR_NO_MATCH;
- }
-
- ret = mgmt_ds_load_config_from_file(ds_ctx, filepath, merge);
- if (ret != 0)
- vty_out(vty, "Error with parsing the file with error code %d\n",
- ret);
- return CMD_SUCCESS;
-}
-
-DEFPY(mgmt_save_config,
- mgmt_save_config_cmd,
- "mgmt save-config <candidate|running>$dsname WORD$filepath",
- MGMTD_STR
- "Save configuration from datastore\n"
- "Candidate datastore\n"
- "Running datastore\n"
- "Full path of the file\n")
-{
- Mgmtd__DatastoreId datastore = mgmt_ds_name2id(dsname);
- struct mgmt_ds_ctx *ds_ctx;
- FILE *f;
-
- ds_ctx = mgmt_ds_get_ctx_by_id(mm, datastore);
- if (!ds_ctx) {
- vty_out(vty, "ERROR: Could not access the '%s' datastore!\n",
- dsname);
- return CMD_ERR_NO_MATCH;
- }
-
- if (!filepath) {
- vty_out(vty, "ERROR: No file path mentioned!\n");
- return CMD_ERR_NO_MATCH;
- }
-
- f = fopen(filepath, "w");
- if (!f) {
- vty_out(vty, "Could not open file pointed by filepath %s\n",
- filepath);
- return CMD_SUCCESS;
- }
-
- mgmt_ds_dump_tree(vty, ds_ctx, "/", f, LYD_JSON);
-
- fclose(f);
-
- return CMD_SUCCESS;
-}
-
-DEFPY(show_mgmt_cmt_hist,
- show_mgmt_cmt_hist_cmd,
- "show mgmt commit-history",
- SHOW_STR
- MGMTD_STR
- "Show commit history\n")
-{
- show_mgmt_cmt_history(vty);
- return CMD_SUCCESS;
-}
-
-DEFPY(mgmt_rollback,
- mgmt_rollback_cmd,
- "mgmt rollback <commit-id WORD$commit | last [(1-10)]$last>",
- MGMTD_STR
- "Rollback commits\n"
- "Rollback to commit ID\n"
- "Commit-ID\n"
- "Rollbak n commits\n"
- "Number of commits\n")
-{
- if (commit)
- mgmt_history_rollback_by_id(vty, commit);
- else
- mgmt_history_rollback_n(vty, last);
-
- return CMD_SUCCESS;
-}
-
-static int config_write_mgmt_debug(struct vty *vty);
-static struct cmd_node debug_node = {
- .name = "debug",
- .node = DEBUG_NODE,
- .prompt = "",
- .config_write = config_write_mgmt_debug,
-};
-
-static int config_write_mgmt_debug(struct vty *vty)
-{
- int n = mgmt_debug_be + mgmt_debug_fe + mgmt_debug_ds + mgmt_debug_txn;
- if (!n)
- return 0;
- if (n == 4) {
- vty_out(vty, "debug mgmt all\n");
- return 0;
- }
-
- vty_out(vty, "debug mgmt");
- if (mgmt_debug_be)
- vty_out(vty, " backend");
- if (mgmt_debug_ds)
- vty_out(vty, " datastore");
- if (mgmt_debug_fe)
- vty_out(vty, " frontend");
- if (mgmt_debug_txn)
- vty_out(vty, " transaction");
-
- vty_out(vty, "\n");
-
- return 0;
-}
-
-DEFPY(debug_mgmt,
- debug_mgmt_cmd,
- "[no$no] debug mgmt <all$all|{backend$be|datastore$ds|frontend$fe|transaction$txn}>",
- NO_STR
- DEBUG_STR
- MGMTD_STR
- "All debug\n"
- "Back-end debug\n"
- "Datastore debug\n"
- "Front-end debug\n"
- "Transaction debug\n")
-{
- bool set = !no;
- if (all)
- be = fe = ds = txn = set ? all : NULL;
-
- if (be)
- mgmt_debug_be = set;
- if (ds)
- mgmt_debug_ds = set;
- if (fe)
- mgmt_debug_fe = set;
- if (txn)
- mgmt_debug_txn = set;
-
- return CMD_SUCCESS;
-}
-
-void mgmt_vty_init(void)
-{
- /*
- * Initialize command handling from VTYSH connection.
- * Call command initialization routines defined by
- * backend components that are moved to new MGMTD infra
- * here one by one.
- */
-#if HAVE_STATICD
- extern void static_vty_init(void);
- static_vty_init();
-#endif
-
- install_node(&debug_node);
-
- install_element(VIEW_NODE, &show_mgmt_be_adapter_cmd);
- install_element(VIEW_NODE, &show_mgmt_be_xpath_reg_cmd);
- install_element(VIEW_NODE, &show_mgmt_fe_adapter_cmd);
- install_element(VIEW_NODE, &show_mgmt_fe_adapter_detail_cmd);
- install_element(VIEW_NODE, &show_mgmt_txn_cmd);
- install_element(VIEW_NODE, &show_mgmt_ds_cmd);
- install_element(VIEW_NODE, &show_mgmt_get_config_cmd);
- install_element(VIEW_NODE, &show_mgmt_get_data_cmd);
- install_element(VIEW_NODE, &show_mgmt_dump_data_cmd);
- install_element(VIEW_NODE, &show_mgmt_map_xpath_cmd);
- install_element(VIEW_NODE, &show_mgmt_cmt_hist_cmd);
-
- install_element(CONFIG_NODE, &mgmt_commit_cmd);
- install_element(CONFIG_NODE, &mgmt_set_config_data_cmd);
- install_element(CONFIG_NODE, &mgmt_delete_config_data_cmd);
- install_element(CONFIG_NODE, &mgmt_load_config_cmd);
- install_element(CONFIG_NODE, &mgmt_save_config_cmd);
- install_element(CONFIG_NODE, &mgmt_rollback_cmd);
-
- install_element(VIEW_NODE, &debug_mgmt_cmd);
- install_element(CONFIG_NODE, &debug_mgmt_cmd);
-
- /* Enable view */
- install_element(ENABLE_NODE, &mgmt_performance_measurement_cmd);
- install_element(ENABLE_NODE, &mgmt_reset_performance_stats_cmd);
-
- /*
- * TODO: Register and handlers for auto-completion here.
- */
-}
#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)
&nifp->afi[afi].nhslist_head, nhs)
nhrp_nhs_free(nifp, afi, nhs);
}
+ nhrp_peer_interface_del(ifp);
}
}
(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);
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))
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);
* 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);
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;
+}
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 */
}
static void ospf_area_range_add(struct ospf_area *area,
+ struct route_table *ranges,
struct ospf_area_range *range)
{
struct route_node *rn;
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
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;
}
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;
}
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;
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;
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;
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);
}
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;
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;
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)
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);
}
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;
}
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__);
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;
}
}
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)
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(
}
}
- /* 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,
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);
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;
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;
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__);
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)
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;
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__);
* 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 */
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)
{
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
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;
/* 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
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__);
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)
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",
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__);
#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 {
/* 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 *);
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);
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;
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)
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;
}
/* 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
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,
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 {
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();
{
int rc = 0;
struct route_node *rn;
+ struct ospf_if_info *oii;
+
rc = ospf_opaque_del_if(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;
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);
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;
ospf_if_down(oi);
}
+ /* Close per-interface write socket if configured */
+ ospf_ifp_sock_close(ifp);
+
return 0;
}
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;
if (best_default.s_addr != INADDR_ANY)
return best_default;
- if (best_default.s_addr != INADDR_ANY)
- return best_default;
-
return fwd;
}
}
/* 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;
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;
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) {
* 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",
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;
}
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;
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",
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;
}
/* 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)) {
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;
/* 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
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,
#include "sockopt.h"
#include "privs.h"
#include "lib_errors.h"
+#include "lib/table.h"
#include "ospfd/ospfd.h"
#include "ospfd/ospf_network.h"
"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);
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;
}
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;
+}
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 */
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;
"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,
{
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)
}
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?",
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) {
/* 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;
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);
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) {
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
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;
}
/* 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);
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);
#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(
/* 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);
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) {
/* 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;
}
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) {
__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;
}
/* 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__);
/* 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__);
{
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;
}
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;
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__);
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);
}
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 ;
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;
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 *);
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);
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);
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);
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);
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;
}
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
ei = object;
/* Set metric out value. */
- ei->route_map_set.metric_type = *metric_type;
+ ROUTEMAP_METRIC_TYPE(ei) = *metric_type;
return RMAP_OKAY;
}
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);
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);
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);
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;
/* 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);
__func__);
return rc;
}
- new->vrf_id = top->vrf_id;
/* Install this LSA into LSDB. */
if (ospf_lsa_install(top, NULL /*oi */, new) == NULL) {
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) {
static struct ls_edge *get_edge(struct ls_ted *ted, struct ls_node_id adv,
struct in_addr link_id)
{
- uint64_t key;
+ struct ls_edge_key key;
struct ls_edge *edge;
struct ls_attributes *attr;
/* Search Edge that corresponds to the Link ID */
- key = ((uint64_t)ntohl(link_id.s_addr)) & 0xffffffff;
+ key.family = AF_INET;
+ IPV4_ADDR_COPY(&key.k.addr, &link_id);
edge = ls_find_edge_by_key(ted, key);
/* Create new one if not exist */
* @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;
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) {
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 */
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:
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) {
struct ls_attributes *attr;
struct tlv_header *tlvh;
struct in_addr addr;
- uint64_t key = 0;
+ struct ls_edge_key key = {.family = AF_UNSPEC};
uint16_t len, sum;
uint8_t lsa_id;
for (tlvh = TLV_DATA(tlvh); sum < len; tlvh = TLV_HDR_NEXT(tlvh)) {
if (ntohs(tlvh->type) == TE_LINK_SUBTLV_LCLIF_IPADDR) {
memcpy(&addr, TLV_DATA(tlvh), TE_LINK_SUBTLV_DEF_SIZE);
- key = ((uint64_t)ntohl(addr.s_addr)) & 0xffffffff;
+ key.family = AF_INET;
+ IPV4_ADDR_COPY(&key.k.addr, &addr);
break;
}
sum += TLV_SIZE(tlvh);
}
- if (key == 0)
+ if (key.family == AF_UNSPEC)
return 0;
/* Search Edge that corresponds to the Link ID */
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);
}
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)
struct ls_edge *edge;
struct ls_attributes *atr;
struct ext_tlv_link *ext;
- uint64_t key;
+ struct ls_edge_key key;
/* Search for corresponding Edge from Link State Data Base */
ext = (struct ext_tlv_link *)TLV_HDR_TOP(lsa->data);
- key = ((uint64_t)ntohl(ext->link_data.s_addr)) & 0xffffffff;
+ key.family = AF_INET;
+ IPV4_ADDR_COPY(&key.k.addr, &ext->link_data);
edge = ls_find_edge_by_key(ted, key);
/* Check if there is a corresponding Edge */
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;
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")
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);
struct ls_edge *edge;
struct ls_subnet *subnet;
uint64_t key;
+ struct ls_edge_key ekey;
bool verbose = false;
bool uj = use_json(argc, argv);
json_object *json = NULL;
return CMD_WARNING_CONFIG_FAILED;
}
/* Get the Edge from the Link State Database */
- key = ((uint64_t)ntohl(ip_addr.s_addr)) & 0xffffffff;
- edge = ls_find_edge_by_key(OspfMplsTE.ted, key);
+ ekey.family = AF_INET;
+ IPV4_ADDR_COPY(&ekey.k.addr, &ip_addr);
+ edge = ls_find_edge_by_key(OspfMplsTE.ted, ekey);
if (!edge) {
vty_out(vty, "No edge found for ID %pI4\n",
&ip_addr);
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);
#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", },
"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;
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;
"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;
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;
"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;
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;
}
"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;
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;
}
"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;
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;
}
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),
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 {
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);
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)",
/* 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);
/* Interface name is specified. */
ifp = if_lookup_by_name(intf_name, ospf->vrf_id);
if (ifp == NULL) {
- if (use_json)
+ if (use_json) {
json_object_boolean_true_add(json_vrf,
"noSuchIface");
- else
+ json_object_free(json_interface);
+ } else
vty_out(vty, "No such interface name\n");
} else {
- if (use_json) {
+ if (use_json)
json_interface_sub = json_object_new_object();
- json_interface = json_object_new_object();
- }
show_ip_ospf_interface_sub(
vty, ospf, ifp, json_interface_sub, use_json);
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",
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);
"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));
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 &&
}
}
- /* 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) {
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);
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)
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);
/* 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++;
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);
/* "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);
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();
#include "log.h"
#include "route_opaque.h"
#include "lib/bfd.h"
+#include "lib/lib_errors.h"
#include "nexthop.h"
#include "ospfd/ospfd.h"
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;
}
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:
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;
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)
{
[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,
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 *);
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;
}
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;
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))
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);
}
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;
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;
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)
{
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));
#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;
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) \
/* 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);
#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;
#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;
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);
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);
{
uint32_t sid = MPLS_LABEL_NONE;
struct ls_edge *edge;
- uint64_t key;
+ struct ls_edge_key key;
if (!path_ted_is_initialized())
return MPLS_LABEL_NONE;
case IPADDR_V4:
/* We have local and remote ip */
/* so check all attributes in ted */
- key = ((uint64_t)ntohl(local->ip._v4_addr.s_addr)) & 0xffffffff;
+ key.family = AF_INET;
+ IPV4_ADDR_COPY(&key.k.addr, &local->ip._v4_addr);
edge = ls_find_edge_by_key(ted_state_g.ted, key);
if (edge) {
if (edge->attributes->standard.remote.s_addr
}
break;
case IPADDR_V6:
- key = (uint64_t)ntohl(local->ip._v6_addr.s6_addr32[2]) << 32 |
- (uint64_t)ntohl(local->ip._v6_addr.s6_addr32[3]);
+ key.family = AF_INET6;
+ IPV6_ADDR_COPY(&key.k.addr6, &local->ip._v6_addr);
edge = ls_find_edge_by_key(ted_state_g.ted, key);
if (edge) {
if ((0 == memcmp(&edge->attributes->standard.remote6,
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))
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 */
"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;
}
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;
}
* 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)
{
};
/* clang-format on */
-CPP_NOTICE("TODO: S,G entries in EXCLUDE (i.e. prune) unsupported");
+/* TODO: S,G entries in EXCLUDE (i.e. prune) unsupported" */
+
/* tib_sg_gm_prune() below is an "un-join", it doesn't prune S,G when *,G is
* joined. Whether we actually want/need to support this is a separate
* question - it is almost never used. In fact this is exactly what RFC5790
* everything else is thrown into pkt for creation of state in pass 2
*/
static void gm_handle_v2_pass1(struct gm_packet_state *pkt,
- struct mld_v2_rec_hdr *rechdr)
+ struct mld_v2_rec_hdr *rechdr, size_t n_src)
{
/* NB: pkt->subscriber can be NULL here if the subscriber was not
* previously seen!
struct gm_sg *grp;
struct gm_packet_sg *old_grp = NULL;
struct gm_packet_sg *item;
- size_t n_src = ntohs(rechdr->n_src);
size_t j;
bool is_excl = false;
*/
gm_packet_sg_drop(old_grp);
gm_sg_update(grp, false);
- CPP_NOTICE("need S,G PRUNE => NO_INFO transition here");
+/* TODO "need S,G PRUNE => NO_INFO transition here" */
}
break;
gm_sg_update(sg_grp, false);
}
-CPP_NOTICE("TODO: QRV/QQIC are not copied from queries to local state");
+/* TODO: QRV/QQIC are not copied from queries to local state" */
+
/* on receiving a query, we need to update our robustness/query interval to
* match, so we correctly process group/source specific queries after last
* member leaves
return;
}
- /* errors after this may at least partially process the packet */
- gm_ifp->stats.rx_new_report++;
-
hdr = (struct mld_v2_report_hdr *)data;
data += sizeof(*hdr);
len -= sizeof(*hdr);
+ n_records = ntohs(hdr->n_records);
+ if (n_records > len / sizeof(struct mld_v2_rec_hdr)) {
+ /* note this is only an upper bound, records with source lists
+ * are larger. This is mostly here to make coverity happy.
+ */
+ zlog_warn(log_pkt_src(
+ "malformed MLDv2 report (infeasible record count)"));
+ gm_ifp->stats.rx_drop_malformed++;
+ return;
+ }
+
+ /* errors after this may at least partially process the packet */
+ gm_ifp->stats.rx_new_report++;
+
/* can't have more *,G and S,G items than there is space for ipv6
* addresses, so just use this to allocate temporary buffer
*/
pkt->iface = gm_ifp;
pkt->subscriber = gm_subscriber_findref(gm_ifp, pkt_src->sin6_addr);
- n_records = ntohs(hdr->n_records);
-
/* validate & remove state in v2_pass1() */
for (i = 0; i < n_records; i++) {
struct mld_v2_rec_hdr *rechdr;
data += record_size;
len -= record_size;
- gm_handle_v2_pass1(pkt, rechdr);
+ gm_handle_v2_pass1(pkt, rechdr, n_src);
}
if (!pkt->n_active) {
item = gm_packet_sg_setup(pkt, grp, true, false);
item->n_exclude = 0;
- CPP_NOTICE("set v1-seen timer on grp here");
+
+/* TODO "set v1-seen timer on grp here" */
/* } */
if (old_grp) {
gm_packet_sg_drop(old_grp);
gm_sg_update(grp, false);
- CPP_NOTICE("need S,G PRUNE => NO_INFO transition here");
+
+/* TODO "need S,G PRUNE => NO_INFO transition here" */
+
}
}
gm_handle_q_group(gm_ifp, &timers, hdr->grp);
gm_ifp->stats.rx_query_new_group++;
} else {
+ /* this is checked above:
+ * if (len >= sizeof(struct mld_v2_query_hdr)) {
+ * size_t src_space = ntohs(hdr->n_src) * sizeof(pim_addr);
+ * if (len < sizeof(struct mld_v2_query_hdr) + src_space) {
+ */
+ assume(ntohs(hdr->n_src) <=
+ (len - sizeof(struct mld_v2_query_hdr)) /
+ sizeof(pim_addr));
+
gm_handle_q_groupsrc(gm_ifp, &timers, hdr->grp, hdr->srcs,
ntohs(hdr->n_src));
gm_ifp->stats.rx_query_new_groupsrc++;
if (!pim_ifp->mld) {
changed = true;
gm_start(ifp);
+ assume(pim_ifp->mld != NULL);
}
gm_ifp = pim_ifp->mld;
struct gm_if *gm_ifp = pim_ifp->mld;
bool querier;
+ assume(js_if || tt);
+
querier = IPV6_ADDR_SAME(&gm_ifp->querier, &pim_ifp->ll_lowest);
if (js_if) {
if (js) {
js_if = json_object_new_object();
+ /*
+ * If we have js as true and detail as false
+ * and if Coverity thinks that js_if is NULL
+ * because of a failed call to new then
+ * when we call gm_show_if_one below
+ * the tt can be deref'ed and as such
+ * FRR will crash. But since we know
+ * that json_object_new_object never fails
+ * then let's tell Coverity that this assumption
+ * is true. I'm not worried about fast path
+ * here at all.
+ */
+ assert(js_if);
json_object_object_add(js_vrf, ifp->name, js_if);
}
*/
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:
*
#endif
extern void gm_cli_init(void);
+bool in6_multicast_nofwd(const pim_addr *addr);
#endif /* PIM6_MLD_H */
#define PIM_MROUTE_DBG "mroute"
#define PIMREG "pimreg"
#define GM "IGMP"
+#define IPPROTO_GM IPPROTO_IGMP
#define PIM_ADDR_FUNCNAME(name) ipv4_##name
#define PIM_MROUTE_DBG "mroute6"
#define PIMREG "pim6reg"
#define GM "MLD"
+#define IPPROTO_GM IPPROTO_ICMPV6
#define PIM_ADDR_FUNCNAME(name) ipv6_##name
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;
}
}
+ /* 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
};
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;
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;
}
}
}
-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);
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 */
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,
} /* 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) {
/* 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);
}
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;
"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;
"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;
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;
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;
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;
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");
"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;
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;
}
"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;
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;
"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;
"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;
"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;
"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;
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__);
"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;
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;
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;
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;
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;
{
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;
{
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;
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;
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;
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;
void pim_cmd_init(void);
+#define PIM_TIME_STRLEN 10
#endif /* PIM_CMD_H */
#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)
{
/* 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) {
"<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 */
zlog_warn("%s: ifindex=%d < 1 on interface %s", __func__,
ifp->ifindex, ifp->name);
return -2;
+ } else if ((ifp->ifindex == 0) &&
+ ((strncmp(ifp->name, "pimreg", 6)) &&
+ (strncmp(ifp->name, "pim6reg", 7)))) {
+ zlog_warn("%s: ifindex=%d == 0 on interface %s", __func__,
+ ifp->ifindex, ifp->name);
+ return -2;
}
ifaddr = pim_ifp->primary_address;
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;
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++;
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;
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;
}
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;
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;
}
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;
return 0;
}
+#if PIM_IPV == 4
__attribute__((unused))
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
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);
#ifndef PIM_IGMP_JOIN_H
#define PIM_IGMP_JOIN_H
+#include "pim_addr.h"
+
/* required headers #include'd by caller */
#ifndef SOL_IP
};
#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 */
/*
* We need to create the VRF table for the pim mroute_socket
*/
- if (pim->vrf->vrf_id != VRF_DEFAULT) {
+ if (enable && pim->vrf->vrf_id != VRF_DEFAULT) {
frr_with_privs (&pimd_privs) {
data = pim->vrf->data.l.table_id;
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;
pim_ifchannel_membership_clear(ifp);
+#if PIM_IPV == 4
/*
* Then restore PIM (S,G) membership from all IGMPv3 (S,G) entries on
* the interface
} /* 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
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;
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;
}
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) {
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;
}
#include "pim_jp_agg.h"
#include "pim_bfd.h"
#include "pim_register.h"
+#include "pim_oil.h"
static void dr_election_by_addr(struct interface *ifp)
{
pim_if_update_could_assert(ifp);
pim_if_update_assert_tracking_desired(ifp);
- if (PIM_I_am_DR(pim_ifp))
+ if (PIM_I_am_DR(pim_ifp)) {
pim_ifp->am_i_dr = true;
- else {
+ pim_clear_nocache_state(pim_ifp);
+ } else {
if (pim_ifp->am_i_dr == true) {
pim_reg_del_on_couldreg_fail(ifp);
pim_ifp->am_i_dr = false;
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]);
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 */
return c_oil;
}
+
+/*
+ * Clean up mroute and channel oil created for dropping pkts from directly
+ * connected source when the interface was non DR.
+ */
+void pim_clear_nocache_state(struct pim_interface *pim_ifp)
+{
+ struct channel_oil *c_oil;
+
+ frr_each_safe (rb_pim_oil, &pim_ifp->pim->channel_oil_head, c_oil) {
+
+ if ((!c_oil->up) ||
+ !(PIM_UPSTREAM_FLAG_TEST_SRC_NOCACHE(c_oil->up->flags)))
+ continue;
+
+ if (*oil_parent(c_oil) != pim_ifp->mroute_vif_index)
+ continue;
+
+ EVENT_OFF(c_oil->up->t_ka_timer);
+ PIM_UPSTREAM_FLAG_UNSET_SRC_NOCACHE(c_oil->up->flags);
+ PIM_UPSTREAM_FLAG_UNSET_SRC_STREAM(c_oil->up->flags);
+ pim_upstream_del(pim_ifp->pim, c_oil->up, __func__);
+ }
+}
+
struct channel_oil *pim_channel_oil_del(struct channel_oil *c_oil,
const char *name)
{
pim_sgaddr *sg);
struct channel_oil *pim_channel_oil_add(struct pim_instance *pim,
pim_sgaddr *sg, const char *name);
+void pim_clear_nocache_state(struct pim_interface *pim_ifp);
struct channel_oil *pim_channel_oil_del(struct channel_oil *c_oil,
const char *name);
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;
}
}
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
vrf_id, new_vrf_id);
pim = pim_get_pim_instance(new_vrf_id);
+ if (!pim)
+ return 0;
if_update_to_new_vrf(ifp, new_vrf_id);
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
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;
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",
"lib/filter_cli.c": "VTYSH_ACL",
"lib/if.c": "VTYSH_INTERFACE",
"lib/keychain.c": "VTYSH_RIPD|VTYSH_EIGRPD|VTYSH_OSPF6D",
+ "lib/mgmt_be_client.c": "VTYSH_STATICD",
+ "lib/mgmt_fe_client.c": "VTYSH_MGMTD",
"lib/lib_vty.c": "VTYSH_ALL",
"lib/log_vty.c": "VTYSH_ALL",
"lib/nexthop_group.c": "VTYSH_NH_GROUP",
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * RIP BFD integration.
+ * Copyright (C) 2021-2023 Network Device Education Foundation, Inc. ("NetDEF")
+ */
+
+#include <zebra.h>
+
+#include "lib/zclient.h"
+#include "lib/bfd.h"
+
+#include "ripd/ripd.h"
+#include "ripd/rip_bfd.h"
+#include "ripd/rip_debug.h"
+
+DEFINE_MTYPE(RIPD, RIP_BFD_PROFILE, "RIP BFD profile name");
+
+extern struct zclient *zclient;
+
+static const char *rip_bfd_interface_profile(struct rip_interface *ri)
+{
+ struct rip *rip = ri->rip;
+
+ if (ri->bfd.profile)
+ return ri->bfd.profile;
+
+ if (rip->default_bfd_profile)
+ return rip->default_bfd_profile;
+
+ return NULL;
+}
+
+static void rip_bfd_session_change(struct bfd_session_params *bsp,
+ const struct bfd_session_status *bss,
+ void *arg)
+{
+ struct rip_peer *rp = arg;
+
+ /* BFD peer went down. */
+ if (bss->state == BFD_STATUS_DOWN &&
+ bss->previous_state == BFD_STATUS_UP) {
+ if (IS_RIP_DEBUG_EVENT)
+ zlog_debug("%s: peer %pI4: BFD Down", __func__,
+ &rp->addr);
+
+ rip_peer_delete_routes(rp);
+ listnode_delete(rp->rip->peer_list, rp);
+ rip_peer_free(rp);
+ return;
+ }
+
+ /* BFD peer went up. */
+ if (bss->state == BSS_UP && bss->previous_state == BSS_DOWN)
+ if (IS_RIP_DEBUG_EVENT)
+ zlog_debug("%s: peer %pI4: BFD Up", __func__,
+ &rp->addr);
+}
+
+void rip_bfd_session_update(struct rip_peer *rp)
+{
+ struct rip_interface *ri = rp->ri;
+
+ /* BFD configuration was removed. */
+ if (ri == NULL || !ri->bfd.enabled) {
+ bfd_sess_free(&rp->bfd_session);
+ return;
+ }
+
+ /* New BFD session. */
+ if (rp->bfd_session == NULL) {
+ rp->bfd_session = bfd_sess_new(rip_bfd_session_change, rp);
+ bfd_sess_set_ipv4_addrs(rp->bfd_session, NULL, &rp->addr);
+ bfd_sess_set_interface(rp->bfd_session, ri->ifp->name);
+ bfd_sess_set_vrf(rp->bfd_session, rp->rip->vrf->vrf_id);
+ }
+
+ /* Set new configuration. */
+ bfd_sess_set_timers(rp->bfd_session, BFD_DEF_DETECT_MULT,
+ BFD_DEF_MIN_RX, BFD_DEF_MIN_TX);
+ bfd_sess_set_profile(rp->bfd_session, rip_bfd_interface_profile(ri));
+
+ bfd_sess_install(rp->bfd_session);
+}
+
+void rip_bfd_interface_update(struct rip_interface *ri)
+{
+ struct rip *rip;
+ struct rip_peer *rp;
+ struct listnode *node;
+
+ rip = ri->rip;
+ if (!rip)
+ return;
+
+ for (ALL_LIST_ELEMENTS_RO(rip->peer_list, node, rp)) {
+ if (rp->ri != ri)
+ continue;
+
+ rip_bfd_session_update(rp);
+ }
+}
+
+void rip_bfd_instance_update(struct rip *rip)
+{
+ struct interface *ifp;
+
+ FOR_ALL_INTERFACES (rip->vrf, ifp) {
+ struct rip_interface *ri;
+
+ ri = ifp->info;
+ if (!ri)
+ continue;
+
+ rip_bfd_interface_update(ri);
+ }
+}
+
+void rip_bfd_init(struct event_loop *tm)
+{
+ bfd_protocol_integration_init(zclient, tm);
+}
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * RIP BFD integration.
+ * Copyright (C) 2021-2023 Network Device Education Foundation, Inc. ("NetDEF")
+ */
+
+#ifndef _RIP_BFD_
+#define _RIP_BFD_
+
+#include "frrevent.h"
+
+DECLARE_MTYPE(RIP_BFD_PROFILE);
+
+struct rip;
+struct rip_interface;
+struct rip_peer;
+
+void rip_bfd_session_update(struct rip_peer *rp);
+void rip_bfd_interface_update(struct rip_interface *ri);
+void rip_bfd_instance_update(struct rip *rip);
+void rip_bfd_init(struct event_loop *tm);
+
+#endif /* _RIP_BFD_ */
}
}
+/*
+ * XPath: /frr-ripd:ripd/instance/default-bfd-profile
+ */
+DEFPY_YANG(rip_bfd_default_profile, rip_bfd_default_profile_cmd,
+ "bfd default-profile BFDPROF$profile",
+ "Bidirectional Forwarding Detection\n"
+ "BFD default profile\n"
+ "Profile name\n")
+{
+ nb_cli_enqueue_change(vty, "./default-bfd-profile", NB_OP_MODIFY,
+ profile);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY_YANG(no_rip_bfd_default_profile, no_rip_bfd_default_profile_cmd,
+ "no bfd default-profile [BFDPROF]",
+ NO_STR
+ "Bidirectional Forwarding Detection\n"
+ "BFD default profile\n"
+ "Profile name\n")
+{
+ nb_cli_enqueue_change(vty, "./default-bfd-profile", NB_OP_DESTROY,
+ NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void cli_show_ripd_instance_default_bfd_profile(struct vty *vty,
+ const struct lyd_node *dnode,
+ bool show_defaults)
+{
+ vty_out(vty, " bfd default-profile %s\n",
+ yang_dnode_get_string(dnode, NULL));
+}
+
/*
* XPath: /frr-interface:lib/interface/frr-ripd:rip/split-horizon
*/
yang_dnode_get_string(dnode, NULL));
}
+/*
+ * XPath: /frr-interface:lib/interface/frr-ripd:rip/bfd-monitoring/enable
+ */
+DEFPY_YANG(ip_rip_bfd, ip_rip_bfd_cmd, "[no] ip rip bfd",
+ NO_STR IP_STR
+ "Routing Information Protocol\n"
+ "Enable BFD support\n")
+{
+ nb_cli_enqueue_change(vty, "./bfd-monitoring/enable", NB_OP_MODIFY,
+ no ? "false" : "true");
+
+ return nb_cli_apply_changes(vty, "./frr-ripd:rip");
+}
+
+void cli_show_ip_rip_bfd_enable(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults)
+{
+ vty_out(vty, " ip rip bfd\n");
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-ripd:rip/bfd/profile
+ */
+DEFPY_YANG(ip_rip_bfd_profile, ip_rip_bfd_profile_cmd,
+ "[no] ip rip bfd profile BFDPROF$profile",
+ NO_STR IP_STR
+ "Routing Information Protocol\n"
+ "Enable BFD support\n"
+ "Use a pre-configured profile\n"
+ "Profile name\n")
+{
+ if (no)
+ nb_cli_enqueue_change(vty, "./bfd-monitoring/profile",
+ NB_OP_DESTROY, NULL);
+ else
+ nb_cli_enqueue_change(vty, "./bfd-monitoring/profile",
+ NB_OP_MODIFY, profile);
+
+ return nb_cli_apply_changes(vty, "./frr-ripd:rip");
+}
+
+DEFPY_YANG(no_ip_rip_bfd_profile, no_ip_rip_bfd_profile_cmd,
+ "no ip rip bfd profile",
+ NO_STR IP_STR
+ "Routing Information Protocol\n"
+ "Enable BFD support\n"
+ "Use a pre-configured profile\n")
+{
+ nb_cli_enqueue_change(vty, "./bfd-monitoring/profile", NB_OP_DESTROY,
+ NULL);
+ return nb_cli_apply_changes(vty, "./frr-ripd:rip");
+}
+
+void cli_show_ip_rip_bfd_profile(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults)
+{
+ vty_out(vty, " ip rip bfd profile %s\n",
+ yang_dnode_get_string(dnode, NULL));
+}
+
/*
* XPath: /frr-ripd:clear-rip-route
*/
install_element(RIP_NODE, &no_rip_timers_cmd);
install_element(RIP_NODE, &rip_version_cmd);
install_element(RIP_NODE, &no_rip_version_cmd);
+ install_element(RIP_NODE, &rip_bfd_default_profile_cmd);
+ install_element(RIP_NODE, &no_rip_bfd_default_profile_cmd);
install_element(INTERFACE_NODE, &ip_rip_split_horizon_cmd);
install_element(INTERFACE_NODE, &ip_rip_v2_broadcast_cmd);
install_element(INTERFACE_NODE, &ip_rip_authentication_key_chain_cmd);
install_element(INTERFACE_NODE,
&no_ip_rip_authentication_key_chain_cmd);
+ install_element(INTERFACE_NODE, &ip_rip_bfd_cmd);
+ install_element(INTERFACE_NODE, &ip_rip_bfd_profile_cmd);
+ install_element(INTERFACE_NODE, &no_ip_rip_bfd_profile_cmd);
install_element(ENABLE_NODE, &clear_ip_rip_cmd);
}
#include "zebra/connected.h"
#include "ripd/ripd.h"
+#include "ripd/rip_bfd.h"
#include "ripd/rip_debug.h"
#include "ripd/rip_interface.h"
ri->sent_updates = 0;
ri->passive = 0;
+ XFREE(MTYPE_RIP_BFD_PROFILE, ri->bfd.profile);
rip_interface_clean(ri);
}
struct rip_interface *ri;
ri = ifp->info;
- if (ri)
+ if (ri) {
ri->rip = ifp->vrf->info;
+ ri->ifp = ifp;
+ }
}
/* Called when interface structure allocated. */
#include "if_rmap.h"
#include "libfrr.h"
#include "routemap.h"
+#include "bfd.h"
#include "ripd/ripd.h"
+#include "ripd/rip_bfd.h"
#include "ripd/rip_nb.h"
#include "ripd/rip_errors.h"
{
zlog_notice("Terminating on signal");
+ bfd_protocol_integration_set_shutdown(true);
rip_vrf_terminate();
if_rmap_terminate();
rip_zclient_stop();
rip_if_init();
rip_cli_init();
rip_zclient_init(master);
+ rip_bfd_init(master);
frr_config_fork();
frr_run(master);
#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 = {
.modify = ripd_instance_redistribute_metric_modify,
},
},
+ {
+ .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 = {
.modify = ripd_instance_version_send_modify,
},
},
+ {
+ .xpath = "/frr-ripd:ripd/instance/default-bfd-profile",
+ .cbs = {
+ .modify = ripd_instance_default_bfd_profile_modify,
+ .destroy = ripd_instance_default_bfd_profile_destroy,
+ .cli_show = cli_show_ripd_instance_default_bfd_profile,
+ },
+ },
{
.xpath = "/frr-interface:lib/interface/frr-ripd:rip/split-horizon",
.cbs = {
.modify = lib_interface_rip_authentication_key_chain_modify,
},
},
+ {
+ .xpath = "/frr-interface:lib/interface/frr-ripd:rip/bfd-monitoring",
+ .cbs = {
+ .create = lib_interface_rip_bfd_create,
+ .destroy = lib_interface_rip_bfd_destroy,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-ripd:rip/bfd-monitoring/enable",
+ .cbs = {
+ .cli_show = cli_show_ip_rip_bfd_enable,
+ .modify = lib_interface_rip_bfd_enable_modify,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-ripd:rip/bfd-monitoring/profile",
+ .cbs = {
+ .cli_show = cli_show_ip_rip_bfd_profile,
+ .modify = lib_interface_rip_bfd_profile_modify,
+ .destroy = lib_interface_rip_bfd_profile_destroy,
+ },
+ },
{
.xpath = "/frr-ripd:ripd/instance/state/neighbors/neighbor",
.cbs = {
.get_elem = ripd_instance_state_routes_route_interface_get_elem,
},
},
+ {
+ .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 = {
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);
int ripd_instance_timers_update_interval_modify(struct nb_cb_modify_args *args);
int ripd_instance_version_receive_modify(struct nb_cb_modify_args *args);
int ripd_instance_version_send_modify(struct nb_cb_modify_args *args);
+int ripd_instance_default_bfd_profile_modify(struct nb_cb_modify_args *args);
+int ripd_instance_default_bfd_profile_destroy(struct nb_cb_destroy_args *args);
const void *ripd_instance_state_neighbors_neighbor_get_next(
struct nb_cb_get_next_args *args);
int ripd_instance_state_neighbors_neighbor_get_keys(
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);
struct nb_cb_modify_args *args);
int lib_interface_rip_authentication_key_chain_destroy(
struct nb_cb_destroy_args *args);
+int lib_interface_rip_bfd_create(struct nb_cb_create_args *args);
+int lib_interface_rip_bfd_destroy(struct nb_cb_destroy_args *args);
+int lib_interface_rip_bfd_enable_modify(struct nb_cb_modify_args *args);
+int lib_interface_rip_bfd_enable_destroy(struct nb_cb_destroy_args *args);
+int lib_interface_rip_bfd_profile_modify(struct nb_cb_modify_args *args);
+int lib_interface_rip_bfd_profile_destroy(struct nb_cb_destroy_args *args);
/* Optional 'apply_finish' callbacks. */
void ripd_instance_redistribute_apply_finish(
bool show_defaults);
void cli_show_ip_rip_send_version(struct vty *vty, const struct lyd_node *dnode,
bool show_defaults);
+void cli_show_ripd_instance_default_bfd_profile(struct vty *vty,
+ const struct lyd_node *dnode,
+ bool show_defaults);
void cli_show_ip_rip_authentication_scheme(struct vty *vty,
const struct lyd_node *dnode,
bool show_defaults);
void cli_show_ip_rip_authentication_key_chain(struct vty *vty,
const struct lyd_node *dnode,
bool show_defaults);
+void cli_show_ip_rip_bfd_enable(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ip_rip_bfd_profile(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults);
/* Notifications. */
extern void ripd_notif_send_auth_type_failure(const char *ifname);
* 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>
#include "prefix.h"
#include "table.h"
#include "command.h"
+#include "if_rmap.h"
#include "routemap.h"
#include "northbound.h"
#include "libfrr.h"
#include "ripd/rip_nb.h"
#include "ripd/rip_debug.h"
#include "ripd/rip_interface.h"
+#include "ripd/rip_bfd.h"
/*
* XPath: /frr-ripd:ripd/instance
rip = nb_running_get_entry(args->dnode, NULL, true);
ifname = yang_dnode_get_string(args->dnode, NULL);
- return rip_passive_nondefault_unset(rip, ifname);
+ return rip_passive_nondefault_set(rip, ifname);
}
int ripd_instance_non_passive_interface_destroy(struct nb_cb_destroy_args *args)
rip = nb_running_get_entry(args->dnode, NULL, true);
ifname = yang_dnode_get_string(args->dnode, NULL);
- return rip_passive_nondefault_set(rip, ifname);
+ return rip_passive_nondefault_unset(rip, ifname);
}
/*
return NB_OK;
}
+/*
+ * 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
*/
return NB_OK;
}
+/*
+ * XPath: /frr-ripd:ripd/instance/default-bfd-profile
+ */
+int ripd_instance_default_bfd_profile_modify(struct nb_cb_modify_args *args)
+{
+ struct rip *rip;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(args->dnode, NULL, true);
+ XFREE(MTYPE_RIP_BFD_PROFILE, rip->default_bfd_profile);
+ rip->default_bfd_profile =
+ XSTRDUP(MTYPE_RIP_BFD_PROFILE,
+ yang_dnode_get_string(args->dnode, NULL));
+ rip_bfd_instance_update(rip);
+
+ return NB_OK;
+}
+
+int ripd_instance_default_bfd_profile_destroy(struct nb_cb_destroy_args *args)
+{
+ struct rip *rip;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(args->dnode, NULL, true);
+ XFREE(MTYPE_RIP_BFD_PROFILE, rip->default_bfd_profile);
+ rip_bfd_instance_update(rip);
+
+ return NB_OK;
+}
+
/*
* XPath: /frr-interface:lib/interface/frr-ripd:rip/split-horizon
*/
return NB_OK;
}
+/*
+ * XPath: /frr-interface:lib/interface/frr-ripd:rip/bfd-monitoring
+ */
+int lib_interface_rip_bfd_create(struct nb_cb_create_args *args)
+{
+ struct interface *ifp;
+ struct rip_interface *ri;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ifp = nb_running_get_entry(args->dnode, NULL, true);
+ ri = ifp->info;
+ ri->bfd.enabled = yang_dnode_get_bool(args->dnode, "./enable");
+ XFREE(MTYPE_RIP_BFD_PROFILE, ri->bfd.profile);
+ if (yang_dnode_exists(args->dnode, "./profile"))
+ ri->bfd.profile = XSTRDUP(
+ MTYPE_RIP_BFD_PROFILE,
+ yang_dnode_get_string(args->dnode, "./profile"));
+
+ rip_bfd_interface_update(ri);
+
+ return NB_OK;
+}
+
+int lib_interface_rip_bfd_destroy(struct nb_cb_destroy_args *args)
+{
+ struct interface *ifp;
+ struct rip_interface *ri;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ifp = nb_running_get_entry(args->dnode, NULL, true);
+ ri = ifp->info;
+ ri->bfd.enabled = false;
+ XFREE(MTYPE_RIP_BFD_PROFILE, ri->bfd.profile);
+ rip_bfd_interface_update(ri);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-ripd:rip/bfd-monitoring/enable
+ */
+int lib_interface_rip_bfd_enable_modify(struct nb_cb_modify_args *args)
+{
+ struct interface *ifp;
+ struct rip_interface *ri;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ifp = nb_running_get_entry(args->dnode, NULL, true);
+ ri = ifp->info;
+ ri->bfd.enabled = yang_dnode_get_bool(args->dnode, NULL);
+ rip_bfd_interface_update(ri);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-ripd:rip/bfd-monitoring/profile
+ */
+int lib_interface_rip_bfd_profile_modify(struct nb_cb_modify_args *args)
+{
+ struct interface *ifp;
+ struct rip_interface *ri;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ifp = nb_running_get_entry(args->dnode, NULL, true);
+ ri = ifp->info;
+ XFREE(MTYPE_RIP_BFD_PROFILE, ri->bfd.profile);
+ ri->bfd.profile = XSTRDUP(MTYPE_RIP_BFD_PROFILE,
+ yang_dnode_get_string(args->dnode, NULL));
+ rip_bfd_interface_update(ri);
+
+ return NB_OK;
+}
+
+int lib_interface_rip_bfd_profile_destroy(struct nb_cb_destroy_args *args)
+{
+ struct interface *ifp;
+ struct rip_interface *ri;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ifp = nb_running_get_entry(args->dnode, NULL, true);
+ ri = ifp->info;
+ XFREE(MTYPE_RIP_BFD_PROFILE, ri->bfd.profile);
+ rip_bfd_interface_update(ri);
+
+ return NB_OK;
+}
+
/*
* XPath: /frr-interface:lib/interface/frr-ripd:rip/authentication-key-chain
*/
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
*/
#include "linklist.h"
#include "frrevent.h"
#include "memory.h"
+#include "table.h"
#include "ripd/ripd.h"
+#include "ripd/rip_bfd.h"
DEFINE_MTYPE_STATIC(RIPD, RIP_PEER, "RIP peer");
return XCALLOC(MTYPE_RIP_PEER, sizeof(struct rip_peer));
}
-static void rip_peer_free(struct rip_peer *peer)
+void rip_peer_free(struct rip_peer *peer)
{
+ bfd_sess_free(&peer->bfd_session);
EVENT_OFF(peer->t_timeout);
XFREE(MTYPE_RIP_PEER, peer);
}
}
/* Get RIP peer. At the same time update timeout thread. */
-static struct rip_peer *rip_peer_get(struct rip *rip, struct in_addr *addr)
+static struct rip_peer *rip_peer_get(struct rip *rip, struct rip_interface *ri,
+ struct in_addr *addr)
{
struct rip_peer *peer;
} else {
peer = rip_peer_new();
peer->rip = rip;
+ peer->ri = ri;
peer->addr = *addr;
+ rip_bfd_session_update(peer);
listnode_add_sort(rip->peer_list, peer);
}
return peer;
}
-void rip_peer_update(struct rip *rip, struct sockaddr_in *from, uint8_t version)
+void rip_peer_update(struct rip *rip, struct rip_interface *ri,
+ struct sockaddr_in *from, uint8_t version)
{
struct rip_peer *peer;
- peer = rip_peer_get(rip, &from->sin_addr);
+ peer = rip_peer_get(rip, ri, &from->sin_addr);
peer->version = version;
}
-void rip_peer_bad_route(struct rip *rip, struct sockaddr_in *from)
+void rip_peer_bad_route(struct rip *rip, struct rip_interface *ri,
+ struct sockaddr_in *from)
{
struct rip_peer *peer;
- peer = rip_peer_get(rip, &from->sin_addr);
+ peer = rip_peer_get(rip, ri, &from->sin_addr);
peer->recv_badroutes++;
}
-void rip_peer_bad_packet(struct rip *rip, struct sockaddr_in *from)
+void rip_peer_bad_packet(struct rip *rip, struct rip_interface *ri,
+ struct sockaddr_in *from)
{
struct rip_peer *peer;
- peer = rip_peer_get(rip, &from->sin_addr);
+ peer = rip_peer_get(rip, ri, &from->sin_addr);
peer->recv_badpackets++;
}
char timebuf[RIP_UPTIME_LEN];
for (ALL_LIST_ELEMENTS(rip->peer_list, node, nnode, peer)) {
- vty_out(vty, " %-16pI4 %9d %9d %9d %s\n",
+ vty_out(vty, " %-17pI4 %9d %9d %9d %11s\n",
&peer->addr, peer->recv_badpackets,
peer->recv_badroutes, ZEBRA_RIP_DISTANCE_DEFAULT,
rip_peer_uptime(peer, timebuf, RIP_UPTIME_LEN));
{
rip_peer_free(arg);
}
+
+void rip_peer_delete_routes(const struct rip_peer *peer)
+{
+ struct route_node *route_node;
+
+ for (route_node = route_top(peer->rip->table); route_node;
+ route_node = route_next(route_node)) {
+ struct rip_info *route_entry;
+ struct listnode *listnode;
+ struct listnode *listnode_next;
+ struct list *list;
+
+ list = route_node->info;
+ if (list == NULL)
+ continue;
+
+ for (ALL_LIST_ELEMENTS(list, listnode, listnode_next,
+ route_entry)) {
+ if (!rip_route_rte(route_entry))
+ continue;
+ if (route_entry->from.s_addr != peer->addr.s_addr)
+ continue;
+
+ if (listcount(list) == 1) {
+ EVENT_OFF(route_entry->t_timeout);
+ EVENT_OFF(route_entry->t_garbage_collect);
+ listnode_delete(list, route_entry);
+ if (list_isempty(list)) {
+ list_delete((struct list **)&route_node
+ ->info);
+ route_unlock_node(route_node);
+ }
+ rip_info_free(route_entry);
+
+ /* Signal the output process to trigger an
+ * update (see section 2.5). */
+ rip_event(peer->rip, RIP_TRIGGERED_UPDATE, 0);
+ } else
+ rip_ecmp_delete(peer->rip, route_entry);
+ break;
+ }
+ }
+}
#include "zclient.h"
#include "log.h"
#include "vrf.h"
+#include "bfd.h"
#include "ripd/ripd.h"
#include "ripd/rip_debug.h"
#include "ripd/rip_interface.h"
vrf->name, vrf->vrf_id);
zclient_send_reg_requests(zclient, vrf->vrf_id);
+ bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, vrf->vrf_id);
}
void rip_zebra_vrf_deregister(struct vrf *vrf)
vrf->name, vrf->vrf_id);
zclient_send_dereg_requests(zclient, vrf->vrf_id);
+ bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_DEREGISTER, vrf->vrf_id);
}
static void rip_zebra_connected(struct zclient *zclient)
{
zclient_send_reg_requests(zclient, VRF_DEFAULT);
+ bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, VRF_DEFAULT);
}
zclient_handler *const rip_handlers[] = {
if (from->sin_port != htons(RIP_PORT_DEFAULT)) {
zlog_info("response doesn't come from RIP port: %d",
from->sin_port);
- rip_peer_bad_packet(rip, from);
+ rip_peer_bad_packet(rip, ri, from);
return;
}
zlog_info(
"This datagram doesn't come from a valid neighbor: %pI4",
&from->sin_addr);
- rip_peer_bad_packet(rip, from);
+ rip_peer_bad_packet(rip, ri, from);
return;
}
; /* Alredy done in rip_read () */
/* Update RIP peer. */
- rip_peer_update(rip, from, packet->version);
+ rip_peer_update(rip, ri, from, packet->version);
/* Set RTE pointer. */
rte = packet->rte;
continue;
}
+ if (packet->version == RIPv1 && rte->tag != 0) {
+ zlog_warn("RIPv1 reserved field is nonzero: %d",
+ ntohs(rte->tag));
+ continue;
+ }
+
/* - is the destination address valid (e.g., unicast; not net 0
or 127) */
if (!rip_destination_check(rte->prefix)) {
zlog_info(
"Network is net 0 or net 127 or it is not unicast network");
- rip_peer_bad_route(rip, from);
+ rip_peer_bad_route(rip, ri, from);
continue;
}
/* - is the metric valid (i.e., between 1 and 16, inclusive) */
if (!(rte->metric >= 1 && rte->metric <= 16)) {
zlog_info("Route's metric is not in the 1-16 range.");
- rip_peer_bad_route(rip, from);
+ rip_peer_bad_route(rip, ri, from);
continue;
}
&& rte->nexthop.s_addr != INADDR_ANY) {
zlog_info("RIPv1 packet with nexthop value %pI4",
&rte->nexthop);
- rip_peer_bad_route(rip, from);
+ rip_peer_bad_route(rip, ri, from);
continue;
}
zlog_warn(
"RIPv2 address %pI4 is not mask /%d applied one",
&rte->prefix, ip_masklen(rte->mask));
- rip_peer_bad_route(rip, from);
+ rip_peer_bad_route(rip, ri, from);
continue;
}
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)
return;
/* RIP peer update. */
- rip_peer_update(rip, from, packet->version);
+ rip_peer_update(rip, ri, from, packet->version);
lim = ((caddr_t)packet) + size;
rte = packet->rte;
socklen_t fromlen;
struct interface *ifp = NULL;
struct connected *ifc;
- struct rip_interface *ri;
+ struct rip_interface *ri = NULL;
struct prefix p;
/* Fetch socket then register myself. */
/* Which interface is this packet comes from. */
ifc = if_lookup_address((void *)&from.sin_addr, AF_INET,
rip->vrf->vrf_id);
- if (ifc)
+ if (ifc) {
ifp = ifc->ifp;
+ ri = ifp->info;
+ }
/* RIP packet received */
if (IS_RIP_DEBUG_EVENT)
ifp ? ifp->name : "unknown", rip->vrf_name);
/* If this packet come from unknown interface, ignore it. */
- if (ifp == NULL) {
+ if (ifp == NULL || ri == NULL) {
zlog_info(
"%s: cannot find interface for packet from %pI4 port %d (VRF %s)",
__func__, &from.sin_addr, ntohs(from.sin_port),
if (len < RIP_PACKET_MINSIZ) {
zlog_warn("packet size %d is smaller than minimum size %d", len,
RIP_PACKET_MINSIZ);
- rip_peer_bad_packet(rip, &from);
+ rip_peer_bad_packet(rip, ri, &from);
return;
}
if (len > RIP_PACKET_MAXSIZ) {
zlog_warn("packet size %d is larger than max size %d", len,
RIP_PACKET_MAXSIZ);
- rip_peer_bad_packet(rip, &from);
+ rip_peer_bad_packet(rip, ri, &from);
return;
}
if ((len - RIP_PACKET_MINSIZ) % 20) {
zlog_warn("packet size %d is wrong for RIP packet alignment",
len);
- rip_peer_bad_packet(rip, &from);
+ rip_peer_bad_packet(rip, ri, &from);
return;
}
if (packet->version == 0) {
zlog_info("version 0 with command %d received.",
packet->command);
- rip_peer_bad_packet(rip, &from);
+ rip_peer_bad_packet(rip, ri, &from);
return;
}
packet->version = RIPv2;
/* Is RIP running or is this RIP neighbor ?*/
- ri = ifp->info;
if (!ri->running && !rip_neighbor_lookup(rip, &from)) {
if (IS_RIP_DEBUG_EVENT)
zlog_debug("RIP is not enabled on interface %s.",
ifp->name);
- rip_peer_bad_packet(rip, &from);
+ rip_peer_bad_packet(rip, ri, &from);
return;
}
zlog_debug(
" packet's v%d doesn't fit to if version spec",
packet->version);
- rip_peer_bad_packet(rip, &from);
+ rip_peer_bad_packet(rip, ri, &from);
return;
}
"packet RIPv%d is dropped because authentication disabled",
packet->version);
ripd_notif_send_auth_type_failure(ifp->name);
- rip_peer_bad_packet(rip, &from);
+ rip_peer_bad_packet(rip, ri, &from);
return;
}
zlog_debug(
"RIPv1 dropped because authentication enabled");
ripd_notif_send_auth_type_failure(ifp->name);
- rip_peer_bad_packet(rip, &from);
+ rip_peer_bad_packet(rip, ri, &from);
return;
}
} else if (ri->auth_type != RIP_NO_AUTH) {
zlog_debug(
"RIPv2 authentication failed: no auth RTE in packet");
ripd_notif_send_auth_type_failure(ifp->name);
- rip_peer_bad_packet(rip, &from);
+ rip_peer_bad_packet(rip, ri, &from);
return;
}
zlog_debug(
"RIPv2 dropped because authentication enabled");
ripd_notif_send_auth_type_failure(ifp->name);
- rip_peer_bad_packet(rip, &from);
+ rip_peer_bad_packet(rip, ri, &from);
return;
}
zlog_debug("RIPv2 %s authentication failure",
auth_desc);
ripd_notif_send_auth_failure(ifp->name);
- rip_peer_bad_packet(rip, &from);
+ rip_peer_bad_packet(rip, ri, &from);
return;
}
}
zlog_info(
"Obsolete command %s received, please sent it to routed",
lookup_msg(rip_msg, packet->command, NULL));
- rip_peer_bad_packet(rip, &from);
+ rip_peer_bad_packet(rip, ri, &from);
break;
case RIP_POLL_ENTRY:
zlog_info("Obsolete command %s received",
lookup_msg(rip_msg, packet->command, NULL));
- rip_peer_bad_packet(rip, &from);
+ rip_peer_bad_packet(rip, ri, &from);
break;
default:
zlog_info("Unknown RIP command %d received", packet->command);
- rip_peer_bad_packet(rip, &from);
+ rip_peer_bad_packet(rip, ri, &from);
break;
}
}
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)
/* 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;
route_table_finish(rip->distance_table);
RB_REMOVE(rip_instance_head, &rip_instances, rip);
+ XFREE(MTYPE_TMP, rip->default_bfd_profile);
XFREE(MTYPE_RIP_VRF_NAME, rip->vrf_name);
XFREE(MTYPE_RIP, rip);
}
#include "nexthop.h"
#include "distribute.h"
#include "memory.h"
+#include "bfd.h"
/* RIP version number. */
#define RIPv1 1
/* RIP queries. */
long queries;
} counters;
+
+ /* Default BFD profile to use with BFD sessions. */
+ char *default_bfd_profile;
};
RB_HEAD(rip_instance_head, rip);
RB_PROTOTYPE(rip_instance_head, rip, entry, rip_instance_compare)
/* Parent routing instance. */
struct rip *rip;
+ /* Interface data from zebra. */
+ struct interface *ifp;
+
/* RIP is enabled on this interface. */
int enable_network;
int enable_interface;
/* Passive interface. */
int passive;
+
+ /* BFD information. */
+ struct {
+ bool enabled;
+ char *profile;
+ } bfd;
};
/* RIP peer information. */
/* Parent routing instance. */
struct rip *rip;
+ /* Back-pointer to RIP interface. */
+ struct rip_interface *ri;
+
/* Peer address. */
struct in_addr addr;
/* Timeout thread. */
struct event *t_timeout;
+
+ /* BFD information */
+ struct bfd_session_params *bfd_session;
};
struct rip_distance {
extern int rip_show_network_config(struct vty *vty, struct rip *rip);
extern void rip_show_redistribute_config(struct vty *vty, struct rip *rip);
-extern void rip_peer_update(struct rip *rip, struct sockaddr_in *from,
- uint8_t version);
-extern void rip_peer_bad_route(struct rip *rip, struct sockaddr_in *from);
-extern void rip_peer_bad_packet(struct rip *rip, struct sockaddr_in *from);
+extern void rip_peer_free(struct rip_peer *peer);
+extern void rip_peer_update(struct rip *rip, struct rip_interface *ri,
+ struct sockaddr_in *from, uint8_t version);
+extern void rip_peer_bad_route(struct rip *rip, struct rip_interface *ri,
+ struct sockaddr_in *from);
+extern void rip_peer_bad_packet(struct rip *rip, struct rip_interface *ri,
+ struct sockaddr_in *from);
extern void rip_peer_display(struct vty *vty, struct rip *rip);
extern struct rip_peer *rip_peer_lookup(struct rip *rip, struct in_addr *addr);
extern struct rip_peer *rip_peer_lookup_next(struct rip *rip,
struct in_addr *addr);
extern int rip_peer_list_cmp(struct rip_peer *p1, struct rip_peer *p2);
extern void rip_peer_list_del(void *arg);
+void rip_peer_delete_routes(const struct rip_peer *peer);
extern void rip_info_free(struct rip_info *);
extern struct rip *rip_info_get_instance(const struct rip_info *rinfo);
endif
ripd_ripd_SOURCES = \
+ ripd/rip_bfd.c \
ripd/rip_cli.c \
ripd/rip_debug.c \
ripd/rip_errors.c \
# end
clippy_scan += \
+ ripd/rip_bfd.c \
ripd/rip_cli.c \
# end
noinst_HEADERS += \
+ ripd/rip_bfd.h \
ripd/rip_debug.h \
ripd/rip_errors.h \
ripd/rip_interface.h \
#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 = {
.modify = ripngd_instance_redistribute_metric_modify,
},
},
+ {
+ .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 = {
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);
* Copyright (C) 1998 Kunihiro Ishiguro
* Copyright (C) 2018 NetDEF, Inc.
* Renato Westphal
+ * Copyright (C) 2023 LabN Consulting, L.L.C.
*/
#include <zebra.h>
#include "prefix.h"
#include "table.h"
#include "command.h"
+#include "if_rmap.h"
#include "routemap.h"
#include "agg_table.h"
#include "northbound.h"
return NB_OK;
}
+/*
+ * 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
*/
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");
struct ls_edge *edge;
struct ls_subnet *subnet;
uint64_t key;
+ struct ls_edge_key ekey;
bool verbose = false;
bool uj = use_json(argc, argv);
json_object *json = NULL;
return CMD_WARNING_CONFIG_FAILED;
}
/* Get the Edge from the Link State Database */
- key = ((uint64_t)ip_addr.s_addr) & 0xffffffff;
- edge = ls_find_edge_by_key(sg.ted, key);
+ ekey.family = AF_INET;
+ IPV4_ADDR_COPY(&ekey.k.addr, &ip_addr);
+ edge = ls_find_edge_by_key(sg.ted, ekey);
if (!edge) {
vty_out(vty, "No edge found for ID %pI4\n",
&ip_addr);
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);
}
if (path->status != SUCCESS) {
vty_out(vty, "Path computation failed: %d\n", path->status);
+ cpath_del(path);
return CMD_SUCCESS;
}
&edge->attributes->standard.remote6);
}
vty_out(vty, "\n");
-
+ cpath_del(path);
return CMD_SUCCESS;
}
zclient->session_id, info.type);
if (info.type == LINK_STATE_UPDATE) {
- lse = ls_stream2ted(sg.ted, s, false);
+ lse = ls_stream2ted(sg.ted, s, true);
if (lse) {
zlog_debug(" |- Got %s %s from Link State Database",
status2txt[lse->status],
#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.",
.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)
{
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);
#include "nexthop.h"
#include "table.h"
#include "srcdest_table.h"
+#include "mgmt_be_client.h"
#include "mpls.h"
#include "northbound.h"
#include "libfrr.h"
install_element(ENABLE_NODE, &debug_staticd_cmd);
install_element(CONFIG_NODE, &debug_staticd_cmd);
+
+ mgmt_be_client_lib_vty_init();
}
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);
}
{
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);
}
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;
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,
/* 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);
/* 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. */
/* 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);
/* 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. */
/* 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);
/* 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. */
* 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);
#include "lib/frr_pthread.h"
#include "lib/frratomic.h"
#include "lib/frrstr.h"
-#include "lib/getopt.h"
#include "lib/graph.h"
#include "lib/hash.h"
#include "lib/hook.h"
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
##############################################################################
ts_hash("init", "df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119");
+#if !IS_ATOMIC(REALTYPE)
+ assert(!list_member(&head, &itm[0]));
+ assert(!list_member(&head, &itm[1]));
+#endif
+
#if IS_SORTED(REALTYPE)
prng = prng_new(0);
k = 0;
log file ospf6d.log
!
-debug ospf6 lsa unknown
-debug ospf6 zebra
-debug ospf6 interface
-debug ospf6 neighbor
+!debug ospf6 lsa unknown
+!debug ospf6 zebra
+!debug ospf6 interface
+!debug ospf6 neighbor
!
interface r1-eth4
!
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")
-debug bfd network
-debug bfd peer
-debug bfd zebra
+!debug bfd network
+!debug bfd peer
+!debug bfd zebra
!
bfd
profile slow-tx
-debug bfd network
-debug bfd peer
-debug bfd zebra
+!debug bfd network
+!debug bfd peer
+!debug bfd zebra
!
bfd
profile slow-tx
!
-debug bgp updates
+!debug bgp updates
!
router bgp 65010
no bgp ebgp-requires-policy
!
-debug bgp updates
+!debug bgp updates
!
router bgp 65020
no bgp ebgp-requires-policy
!
-debug bgp updates
-debug bgp vpn leak-from-vrf
-debug bgp vpn leak-to-vrf
-debug bgp nht
+!debug bgp updates
+!debug bgp vpn leak-from-vrf
+!debug bgp vpn leak-to-vrf
+!debug bgp nht
!
router bgp 65001
bgp router-id 10.10.10.10
!
-debug bgp updates
+!debug bgp updates
!
router bgp 65001
bgp router-id 10.10.10.101
no bgp ebgp-requires-policy
neighbor 192.168.255.2 remote-as external
neighbor 192.168.255.2 timers 3 10
+ neighbor 192.168.255.2 timers connect 1
neighbor 192.168.255.2 bfd
+ neighbor 192.168.255.2 passive
address-family ipv4
redistribute connected
exit-address-family
no bgp ebgp-requires-policy
neighbor 192.168.255.1 remote-as external
neighbor 192.168.255.1 timers 3 10
+ neighbor 192.168.255.1 timers connect 1
neighbor 192.168.255.1 bfd
address-family ipv4
redistribute connected
expected = {
"192.168.255.1": {
"lastNotificationReason": "Cease/BFD Down",
+ "lastNotificationHardReset": True,
}
}
return topotest.json_cmp(output, expected)
step("Initial BGP converge")
test_func = functools.partial(_bgp_converge)
- _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
assert result is None, "Failed to see BGP convergence on R2"
step("Kill bfdd on R2")
step("Check if we received Cease/BFD Down notification message")
test_func = functools.partial(_bgp_bfd_down_notification)
- _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
assert result is None, "Failed to see BGP Cease/BFD Down notification message on R2"
!
-debug bgp updates
+!debug bgp updates
!
router bgp 65002
no bgp ebgp-requires-policy
-debug bgp neighbor-events
-debug bgp nht
-debug bgp updates in
-debug bgp updates out
+!debug bgp neighbor-events
+!debug bgp nht
+!debug bgp updates in
+!debug bgp updates out
!
router bgp 100
no bgp ebgp-requires-policy
-debug bgp neighbor-events
-debug bgp nht
-debug bgp updates in
-debug bgp updates out
+!debug bgp neighbor-events
+!debug bgp nht
+!debug bgp updates in
+!debug bgp updates out
!
router bgp 200
no bgp ebgp-requires-policy
-debug bgp neighbor-events
-debug bgp nht
-debug bgp updates in
-debug bgp updates out
+!debug bgp neighbor-events
+!debug bgp nht
+!debug bgp updates in
+!debug bgp updates out
!
router bgp 300
no bgp ebgp-requires-policy
-debug bgp neighbor-events
-debug bgp nht
-debug bgp updates in
-debug bgp updates out
+!debug bgp neighbor-events
+!debug bgp nht
+!debug bgp updates in
+!debug bgp updates out
!
router bgp 400
no bgp ebgp-requires-policy
!
-debug bgp neighbor
+!debug bgp neighbor
!
router bgp 65001
no bgp ebgp-requires-policy
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]
# 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")
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"
"Teardown the pytest environment"
tgen = get_topogen()
- #tgen.mininet_cli()
+ # tgen.mininet_cli()
# This function tears down the whole topology.
tgen.stop_topology()
)
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"
_, 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,
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)
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"
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)
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":
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"]:
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")
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))
return None
+
def test_dvni():
"test Downstream VNI works as expected importing into PE1"
_, 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():
_, 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,
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)
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")
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")
"neighbors":{
"192.168.0.2":[
{
- "priority":5,
+ "nbrPriority":5,
"converged":"Full"
}
],
"192.168.0.3":[
{
- "priority":5,
+ "nbrPriority":5,
"converged":"Full"
}
]
"neighbors":{
"192.168.0.1":[
{
- "priority":10,
+ "nbrPriority":10,
"converged":"Full"
}
],
"192.168.0.3":[
{
- "priority":5,
+ "nbrPriority":5,
"converged":"Full"
}
]
"neighbors":{
"192.168.0.1":[
{
- "priority":10,
+ "nbrPriority":10,
"converged":"Full"
}
],
"192.168.0.2":[
{
- "priority":10,
+ "nbrPriority":10,
"converged":"Full"
}
]
reset_config_on_routers(tgen)
logger.info(
- "[Phase 1] : Test Setup " "[Disable Mode]R1-----R2[Restart Mode] initialized "
+ "[Phase 1] : Test Setup " "[Disable Mode]R1-----R2[Helper Mode] initialized "
)
# Configure graceful-restart
tc_name, result
)
- logger.info("[Phase 2] : R2 Goes from Disable to Restart Mode ")
+ logger.info("[Phase 2] : R1 Goes from Disable to Restart Mode ")
# Configure graceful-restart
input_dict = {
},
}
- # here the verify_graceful_restart fro the neighbor would be
- # "NotReceived" as the latest GR config is not yet applied.
- for addr_type in ADDR_TYPES:
- result = verify_graceful_restart(
- tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
- )
- assert result is True, "Testcase {} : Failed \n Error {}".format(
- tc_name, result
- )
-
- for addr_type in ADDR_TYPES:
- # Verifying RIB routes
- next_hop = next_hop_per_address_family(
- tgen, dut, peer, addr_type, NEXT_HOP_IP_2
- )
- input_topo = {key: topo["routers"][key] for key in ["r2"]}
- result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
- assert result is True, "Testcase {} : Failed \n Error {}".format(
- tc_name, result
- )
-
- logger.info("[Phase 6] : R1 is about to come up now ")
- start_router_daemons(tgen, "r1", ["bgpd"])
-
- logger.info("[Phase 4] : R1 is UP now, so time to collect GR stats ")
+ logger.info("[Phase 4] : R1 is UP and GR state is correct ")
for addr_type in ADDR_TYPES:
result = verify_graceful_restart(
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)
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)
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)
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)
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)
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)
"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(
r3 = tgen.gears["r3"]
r4 = tgen.gears["r4"]
- def _bgp_check_advertised_routes(prefix_num):
- output = json.loads(
- r3.vtysh_cmd(
- "show bgp ipv4 labeled-unicast neighbors 192.168.34.4 advertised-routes json"
- )
- )
+ def _bgp_check_received_routes(pfxcount):
+ output = json.loads(r4.vtysh_cmd("show bgp ipv4 labeled-unicast summary json"))
expected = {
- "advertisedRoutes": {
- "10.0.0.1/32": {
- "appliedStatusSymbols": {
- "*": True,
- ">": True,
- "=": True,
- }
+ "peers": {
+ "192.168.34.3": {
+ "pfxRcd": pfxcount,
+ "state": "Established",
}
- },
- "totalPrefixCounter": prefix_num,
+ }
}
return topotest.json_cmp(output, expected)
- test_func = functools.partial(_bgp_check_advertised_routes, 2)
+ test_func = functools.partial(_bgp_check_received_routes, 2)
_, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
assert (
result is None
- ), "Failed to advertise labeled-unicast with addpath (multipath)"
-
- def _bgp_check_received_routes():
- output = json.loads(r4.vtysh_cmd("show bgp ipv4 labeled-unicast json"))
- expected = {
- "routes": {
- "10.0.0.1/32": [
- {
- "valid": True,
- "path": "65003 65001",
- },
- {
- "valid": True,
- "path": "65003 65002",
- },
- ]
- }
- }
- return topotest.json_cmp(output, expected)
-
- test_func = functools.partial(_bgp_check_received_routes)
- _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
- assert result is None, "Failed to receive labeled-unicast with addpath (multipath)"
+ ), "Failed to receive labeled-unicast with addpath (multipath=2)"
step("Enable BGP session for R5")
r3.vtysh_cmd(
"""
)
- test_func = functools.partial(_bgp_check_advertised_routes, 3)
+ test_func = functools.partial(_bgp_check_received_routes, 3)
_, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
assert (
result is None
- ), "Failed to advertise labeled-unicast with addpath (multipath)"
+ ), "Failed to receive labeled-unicast with addpath (multipath=3)"
step("Disable BGP session for R5")
r3.vtysh_cmd(
"""
)
- test_func = functools.partial(_bgp_check_advertised_routes, 2)
+ test_func = functools.partial(_bgp_check_received_routes, 2)
_, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
assert (
result is None
- ), "Failed to advertise labeled-unicast with addpath (multipath)"
+ ), "Failed to receive labeled-unicast with addpath (multipath=2)"
if __name__ == "__main__":
--- /dev/null
+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
+!
--- /dev/null
+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
--- /dev/null
+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
+!
--- /dev/null
+interface r2-eth0
+ ip address 192.0.2.2/24
+!
+interface r2-eth1
+ ip address 192.168.2.2/32
+!
--- /dev/null
+#!/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))
--- /dev/null
+!
+int r1-eth0
+ ip address 192.168.1.1/24
+!
+router bgp 65001
+ bgp router-id 192.168.1.1
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 192.168.1.2 remote-as external
+ neighbor 192.168.1.3 remote-as external
+ neighbor 192.168.1.4 remote-as external
+ address-family ipv4 unicast
+ network 10.10.10.10/32
+ neighbor 192.168.1.2 route-map rmap out
+ neighbor 192.168.1.3 route-map rmap out
+ neighbor 192.168.1.4 route-map rmap out
+ exit-address-family
+!
+route-map rmap permit 10
+ set extcommunity nt 192.168.1.3:0 192.168.1.4:0
+exit
--- /dev/null
+!
+int r2-eth0
+ ip address 192.168.1.2/24
+!
+router bgp 65002
+ bgp router-id 192.168.1.2
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.1 remote-as external
--- /dev/null
+!
+int r3-eth0
+ ip address 192.168.1.3/24
+!
+router bgp 65003
+ bgp router-id 192.168.1.3
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.1 remote-as external
--- /dev/null
+!
+int r4-eth0
+ ip address 192.168.1.4/24
+!
+router bgp 65004
+ bgp router-id 192.168.1.4
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.1 remote-as external
--- /dev/null
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright (c) 2023 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+"""
+Test if Node Target Extended Communities works.
+
+At r1 we set NT to 192.168.1.3 and 192.168.1.4 (this is the R3/R4 router-id),
+and that means 10.10.10.10/32 MUST be installed on R3 and R4, but not on R2,
+because this route does not have NT:192.168.1.2.
+"""
+
+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.bgpd]
+
+
+def build_topo(tgen):
+ for routern in range(1, 5):
+ tgen.add_router("r{}".format(routern))
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+ switch.add_link(tgen.gears["r3"])
+ switch.add_link(tgen.gears["r4"])
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (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_bgp_node_target_extended_communities():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r1 = tgen.gears["r1"]
+ r2 = tgen.gears["r2"]
+ r3 = tgen.gears["r3"]
+ r4 = tgen.gears["r4"]
+
+ def _bgp_converge():
+ output = json.loads(r1.vtysh_cmd("show bgp summary json"))
+ expected = {
+ "ipv4Unicast": {
+ "peers": {
+ "192.168.1.2": {
+ "pfxSnt": 1,
+ "state": "Established",
+ },
+ "192.168.1.3": {
+ "pfxSnt": 1,
+ "state": "Established",
+ },
+ "192.168.1.4": {
+ "pfxSnt": 1,
+ "state": "Established",
+ },
+ }
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_converge)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assert result is None, "Failed announcing 10.10.10.10/32 to r2, r3, and r4"
+
+ def _bgp_check_route(router, exists):
+ output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast json"))
+ if exists:
+ expected = {
+ "routes": {
+ "10.10.10.10/32": [
+ {
+ "valid": True,
+ }
+ ]
+ }
+ }
+ else:
+ expected = {
+ "routes": {
+ "10.10.10.10/32": None,
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_check_route, r3, True)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assert result is None, "10.10.10.10/32 is not installed, but SHOULD be"
+
+ test_func = functools.partial(_bgp_check_route, r4, True)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assert result is None, "10.10.10.10/32 is not installed, but SHOULD be"
+
+ test_func = functools.partial(_bgp_check_route, r2, False)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assert result is None, "10.10.10.10/32 is installed, but SHOULD NOT be"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
!
-debug bgp updates
+!debug bgp updates
!
router bgp 65002
no bgp ebgp-requires-policy
--- /dev/null
+!
+!debug bgp updates
+!debug bgp neighbor
+!
+bgp route-map delay-timer 5
+!
+router bgp 65001
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 192.168.1.2 remote-as external
+ address-family ipv4 unicast
+ network 10.10.10.1/32
+ network 10.10.10.2/32
+ network 10.10.10.3/32
+ aggregate-address 10.10.10.0/24 summary-only
+ neighbor 192.168.1.2 unsuppress-map r2
+ exit-address-family
+!
+ip prefix-list r1 seq 5 permit 10.10.10.1/32
+ip prefix-list r1 seq 10 permit 10.10.10.2/32
+!
+route-map r2 permit 10
+ match ip address prefix-list r1
+exit
--- /dev/null
+!
+int r1-eth0
+ ip address 192.168.1.1/24
+!
--- /dev/null
+router bgp 65002
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.1 remote-as external
+!
--- /dev/null
+!
+int r2-eth0
+ ip address 192.168.1.2/24
+!
--- /dev/null
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright (c) 2023 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+"""
+
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+pytestmark = pytest.mark.bgpd
+
+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.bgpd]
+
+
+def setup_module(mod):
+ topodef = {"s1": ("r1", "r2")}
+ tgen = Topogen(topodef, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_route_map_delay_timer():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r1 = tgen.gears["r1"]
+ r2 = tgen.gears["r2"]
+
+ def _bgp_converge_1():
+ output = json.loads(
+ r1.vtysh_cmd(
+ "show bgp ipv4 unicast neighbor 192.168.1.2 advertised-routes json"
+ )
+ )
+ expected = {
+ "advertisedRoutes": {
+ "10.10.10.0/24": {},
+ "10.10.10.1/32": {},
+ "10.10.10.2/32": {},
+ "10.10.10.3/32": None,
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_converge_1)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "10.10.10.3/32 should not be advertised to r2"
+
+ # Set route-map delay-timer to max value and remove 10.10.10.2/32.
+ # After this, r1 MUST do not announce updates immediately, and wait
+ # 600 seconds before withdrawing 10.10.10.2/32.
+ r2.vtysh_cmd(
+ """
+ configure terminal
+ bgp route-map delay-timer 600
+ no ip prefix-list r1 seq 10 permit 10.10.10.2/32
+ """
+ )
+
+ def _bgp_converge_2():
+ output = json.loads(
+ r1.vtysh_cmd(
+ "show bgp ipv4 unicast neighbor 192.168.1.2 advertised-routes json"
+ )
+ )
+ expected = {
+ "advertisedRoutes": {
+ "10.10.10.0/24": {},
+ "10.10.10.1/32": {},
+ "10.10.10.2/32": None,
+ "10.10.10.3/32": None,
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ # We are checking `not None` here to wait count*wait time and if we have different
+ # results than expected, it means good - 10.10.10.2/32 wasn't withdrawn immediately.
+ test_func = functools.partial(_bgp_converge_2)
+ _, result = topotest.run_and_expect(test_func, not None, count=60, wait=0.5)
+ assert (
+ result is not None
+ ), "10.10.10.2/32 advertised, but should not be advertised to r2"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
!
-debug bgp updates
-debug bgp vpn leak-from-vrf
-debug bgp vpn leak-to-vrf
-debug bgp nht
-debug route-map
+!debug bgp updates
+!debug bgp vpn leak-from-vrf
+!debug bgp vpn leak-to-vrf
+!debug bgp nht
+!debug route-map
!
router bgp 65001
bgp router-id 10.10.10.10
--- /dev/null
+router bgp 65001
+ neighbor 192.168.2.1 remote-as external
--- /dev/null
+#!/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))
!
-debug bgp updates
+!debug bgp updates
!
router bgp 65002
no bgp ebgp-requires-policy
--- /dev/null
+frr defaults traditional
+!
+hostname ce1
+password zebra
+!
+log stdout notifications
+log commands
+log file bgpd.log
--- /dev/null
+{
+ "::/0": [
+ {
+ "prefix": "::/0",
+ "protocol": "static",
+ "vrfId": 0,
+ "vrfName": "default",
+ "selected": true,
+ "destSelected": true,
+ "distance": 1,
+ "metric": 0,
+ "installed": true,
+ "table": 254,
+ "internalStatus": 16,
+ "internalFlags": 73,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "ip": "2001:1::1",
+ "afi": "ipv6",
+ "interfaceName": "eth0",
+ "active": true,
+ "weight": 1
+ }
+ ]
+ }
+ ],
+ "2001:1::/64": [
+ {
+ "prefix": "2001:1::/64",
+ "protocol": "connected",
+ "vrfId": 0,
+ "vrfName": "default",
+ "selected": true,
+ "destSelected": true,
+ "distance": 0,
+ "metric": 0,
+ "installed": true,
+ "table": 254,
+ "internalStatus": 16,
+ "internalFlags": 8,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "directlyConnected": true,
+ "interfaceName": "eth0",
+ "active": true
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+log file zebra.log
+!
+hostname ce1
+!
+interface eth0
+ ipv6 address 2001:1::2/64
+ ip address 192.168.1.2/24
+!
+ip forwarding
+ipv6 forwarding
+!
+ipv6 route ::/0 2001:1::1
+ip route 0.0.0.0/0 192.168.1.1
+!
+line vty
+!
--- /dev/null
+frr defaults traditional
+!
+hostname ce2
+password zebra
+!
+log stdout notifications
+log commands
+log file bgpd.log
--- /dev/null
+{
+ "::/0": [
+ {
+ "prefix": "::/0",
+ "protocol": "static",
+ "vrfId": 0,
+ "vrfName": "default",
+ "selected": true,
+ "destSelected": true,
+ "distance": 1,
+ "metric": 0,
+ "installed": true,
+ "table": 254,
+ "internalStatus": 16,
+ "internalFlags": 73,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "ip": "2001:2::1",
+ "afi": "ipv6",
+ "interfaceName": "eth0",
+ "active": true,
+ "weight": 1
+ }
+ ]
+ }
+ ],
+ "2001:2::/64": [
+ {
+ "prefix": "2001:2::/64",
+ "protocol": "connected",
+ "vrfId": 0,
+ "vrfName": "default",
+ "selected": true,
+ "destSelected": true,
+ "distance": 0,
+ "metric": 0,
+ "installed": true,
+ "table": 254,
+ "internalStatus": 16,
+ "internalFlags": 8,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "directlyConnected": true,
+ "interfaceName": "eth0",
+ "active": true
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+log file zebra.log
+!
+hostname ce2
+!
+interface eth0
+ ipv6 address 2001:2::2/64
+ ip address 192.168.2.2/24
+!
+ip forwarding
+ipv6 forwarding
+!
+ipv6 route ::/0 2001:2::1
+ip route 0.0.0.0/0 192.168.2.1
+!
+line vty
+!
--- /dev/null
+frr defaults traditional
+!
+hostname ce3
+password zebra
+!
+log stdout notifications
+log commands
+log file bgpd.log
--- /dev/null
+{
+ "::/0": [
+ {
+ "prefix": "::/0",
+ "protocol": "static",
+ "vrfId": 0,
+ "vrfName": "default",
+ "selected": true,
+ "destSelected": true,
+ "distance": 1,
+ "metric": 0,
+ "installed": true,
+ "table": 254,
+ "internalStatus": 16,
+ "internalFlags": 73,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "ip": "2001:3::1",
+ "afi": "ipv6",
+ "interfaceName": "eth0",
+ "active": true,
+ "weight": 1
+ }
+ ]
+ }
+ ],
+ "2001:3::/64": [
+ {
+ "prefix": "2001:3::/64",
+ "protocol": "connected",
+ "vrfId": 0,
+ "vrfName": "default",
+ "selected": true,
+ "destSelected": true,
+ "distance": 0,
+ "metric": 0,
+ "installed": true,
+ "table": 254,
+ "internalStatus": 16,
+ "internalFlags": 8,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "directlyConnected": true,
+ "interfaceName": "eth0",
+ "active": true
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+log file zebra.log
+!
+hostname ce3
+!
+interface eth0
+ ipv6 address 2001:3::2/64
+!
+ip forwarding
+ipv6 forwarding
+!
+ipv6 route ::/0 2001:3::1
+!
+line vty
+!
--- /dev/null
+frr defaults traditional
+!
+hostname ce4
+password zebra
+!
+log stdout notifications
+log commands
+log file bgpd.log
--- /dev/null
+{
+ "::/0": [
+ {
+ "prefix": "::/0",
+ "protocol": "static",
+ "vrfId": 0,
+ "vrfName": "default",
+ "selected": true,
+ "destSelected": true,
+ "distance": 1,
+ "metric": 0,
+ "installed": true,
+ "table": 254,
+ "internalStatus": 16,
+ "internalFlags": 73,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "ip": "2001:4::1",
+ "afi": "ipv6",
+ "interfaceName": "eth0",
+ "active": true,
+ "weight": 1
+ }
+ ]
+ }
+ ],
+ "2001:4::/64": [
+ {
+ "prefix": "2001:4::/64",
+ "protocol": "connected",
+ "vrfId": 0,
+ "vrfName": "default",
+ "selected": true,
+ "destSelected": true,
+ "distance": 0,
+ "metric": 0,
+ "installed": true,
+ "table": 254,
+ "internalStatus": 16,
+ "internalFlags": 8,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "directlyConnected": true,
+ "interfaceName": "eth0",
+ "active": true
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+log file zebra.log
+!
+hostname ce4
+!
+interface eth0
+ ipv6 address 2001:4::2/64
+!
+ip forwarding
+ipv6 forwarding
+!
+ipv6 route ::/0 2001:4::1
+!
+line vty
+!
--- /dev/null
+frr defaults traditional
+!
+hostname ce5
+password zebra
+!
+log stdout notifications
+log commands
+log file bgpd.log
--- /dev/null
+{
+ "::/0": [
+ {
+ "prefix": "::/0",
+ "protocol": "static",
+ "vrfId": 0,
+ "vrfName": "default",
+ "selected": true,
+ "destSelected": true,
+ "distance": 1,
+ "metric": 0,
+ "installed": true,
+ "table": 254,
+ "internalStatus": 16,
+ "internalFlags": 73,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "ip": "2001:5::1",
+ "afi": "ipv6",
+ "interfaceName": "eth0",
+ "active": true,
+ "weight": 1
+ }
+ ]
+ }
+ ],
+ "2001:5::/64": [
+ {
+ "prefix": "2001:5::/64",
+ "protocol": "connected",
+ "vrfId": 0,
+ "vrfName": "default",
+ "selected": true,
+ "destSelected": true,
+ "distance": 0,
+ "metric": 0,
+ "installed": true,
+ "table": 254,
+ "internalStatus": 16,
+ "internalFlags": 8,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "directlyConnected": true,
+ "interfaceName": "eth0",
+ "active": true
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+log file zebra.log
+!
+hostname ce5
+!
+interface eth0
+ ipv6 address 2001:5::2/64
+!
+ip forwarding
+ipv6 forwarding
+!
+ipv6 route ::/0 2001:5::1
+!
+line vty
+!
--- /dev/null
+frr defaults traditional
+!
+hostname ce6
+password zebra
+!
+log stdout notifications
+log commands
+log file bgpd.log
--- /dev/null
+{
+ "::/0": [
+ {
+ "prefix": "::/0",
+ "protocol": "static",
+ "vrfId": 0,
+ "vrfName": "default",
+ "selected": true,
+ "destSelected": true,
+ "distance": 1,
+ "metric": 0,
+ "installed": true,
+ "table": 254,
+ "internalStatus": 16,
+ "internalFlags": 73,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "ip": "2001:6::1",
+ "afi": "ipv6",
+ "interfaceName": "eth0",
+ "active": true,
+ "weight": 1
+ }
+ ]
+ }
+ ],
+ "2001:6::/64": [
+ {
+ "prefix": "2001:6::/64",
+ "protocol": "connected",
+ "vrfId": 0,
+ "vrfName": "default",
+ "selected": true,
+ "destSelected": true,
+ "distance": 0,
+ "metric": 0,
+ "installed": true,
+ "table": 254,
+ "internalStatus": 16,
+ "internalFlags": 8,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "directlyConnected": true,
+ "interfaceName": "eth0",
+ "active": true
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+log file zebra.log
+!
+hostname ce6
+!
+interface eth0
+ ipv6 address 2001:6::2/64
+!
+ip forwarding
+ipv6 forwarding
+!
+ipv6 route ::/0 2001:6::1
+!
+line vty
+!
--- /dev/null
+frr defaults traditional
+!
+hostname r1
+password zebra
+!
+log stdout notifications
+log monitor notifications
+log commands
+!
+!debug bgp neighbor-events
+!debug bgp zebra
+!debug bgp vnc verbose
+!debug bgp update-groups
+!debug bgp updates in
+!debug bgp updates out
+!debug bgp vpn label
+!debug bgp vpn leak-from-vrf
+!debug bgp vpn leak-to-vrf
+!debug bgp vpn rmap-event
+!
+router bgp 1
+ bgp router-id 192.0.2.1
+ no bgp ebgp-requires-policy
+ no bgp default ipv4-unicast
+ neighbor 2001::2 remote-as 2
+ neighbor 2001::2 timers 3 10
+ neighbor 2001::2 timers connect 1
+ neighbor 2001::2 update-source 2001::1
+ neighbor 2001::2 capability extended-nexthop
+ !
+ address-family ipv4 vpn
+ neighbor 2001::2 activate
+ exit-address-family
+ !
+ address-family ipv6 vpn
+ neighbor 2001::2 activate
+ exit-address-family
+ !
+ segment-routing srv6
+ locator loc1
+ !
+!
+router bgp 1 vrf vrf10
+ bgp router-id 192.0.2.1
+ no bgp ebgp-requires-policy
+ !
+ address-family ipv6 unicast
+ sid vpn export auto
+ rd vpn export 1:10
+ rt vpn both 99:99
+ import vpn
+ export vpn
+ redistribute connected
+ exit-address-family
+!
+ address-family ipv4 unicast
+ network 192.168.1.0/24
+ sid vpn export auto
+ rd vpn export 11:10
+ rt vpn both 77:77
+ import vpn
+ export vpn
+ redistribute connected
+ exit-address-family
+!
+router bgp 1 vrf vrf20
+ bgp router-id 192.0.2.1
+ no bgp ebgp-requires-policy
+ no bgp default ipv4-unicast
+ !
+ address-family ipv6 unicast
+ sid vpn export auto
+ rd vpn export 1:20
+ rt vpn both 88:88
+ import vpn
+ export vpn
+ redistribute connected
+ exit-address-family
+!
--- /dev/null
+{
+ "vrfName": "default",
+ "tableVersion": 2,
+ "routerId": "192.0.2.1",
+ "defaultLocPrf": 100,
+ "localAS": 1,
+ "routes": {
+ "routeDistinguishers": {
+ "1:10": {
+ "2001:1::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:1::",
+ "prefixLen": 64,
+ "network": "2001:1::/64",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "vrf10",
+ "nexthops": [
+ {
+ "ip": "::",
+ "hostname": "r1",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "2001:3::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:3::",
+ "prefixLen": 64,
+ "network": "2001:3::/64",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "vrf10",
+ "nexthops": [
+ {
+ "ip": "::",
+ "hostname": "r1",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ },
+ "1:20": {
+ "2001:5::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:5::",
+ "prefixLen": 64,
+ "network": "2001:5::/64",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "vrf20",
+ "nexthops": [
+ {
+ "ip": "::",
+ "hostname": "r1",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ },
+ "2:10": {
+ "2001:2::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:2::",
+ "prefixLen": 64,
+ "network": "2001:2::/64",
+ "metric": 0,
+ "weight": 0,
+ "peerId": "2001::2",
+ "path": "2",
+ "origin": "incomplete",
+ "nexthops": [
+ {
+ "ip": "2001::2",
+ "hostname": "r2",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ },
+ "2:20": {
+ "2001:4::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:4::",
+ "prefixLen": 64,
+ "network": "2001:4::/64",
+ "metric": 0,
+ "weight": 0,
+ "peerId": "2001::2",
+ "path": "2",
+ "origin": "incomplete",
+ "nexthops": [
+ {
+ "ip": "2001::2",
+ "hostname": "r2",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "2001:6::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:6::",
+ "prefixLen": 64,
+ "network": "2001:6::/64",
+ "metric": 0,
+ "weight": 0,
+ "peerId": "2001::2",
+ "path": "2",
+ "origin": "incomplete",
+ "nexthops": [
+ {
+ "ip": "2001::2",
+ "hostname": "r2",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+}
--- /dev/null
+{
+ "192.168.1.0\/24":[
+ {
+ "prefix":"192.168.1.0\/24",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ }
+ ]
+}
+
--- /dev/null
+{
+ "192.168.1.0\/24":[
+ {
+ "prefix":"192.168.1.0\/24",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ }
+ ],
+ "192.168.2.0\/24":[
+ {
+ "prefix":"192.168.2.0\/24",
+ "protocol":"bgp",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":20,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "afi":"ipv6",
+ "labels":[
+ 16
+ ],
+ "weight":1,
+ "seg6local":{
+ "action":"unspec"
+ },
+ "seg6":{
+ "segs":"2001:db8:2:2:1::"
+ }
+ }
+ ]
+ }
+ ]
+}
+
--- /dev/null
+{
+ "192.168.1.0\/24":[
+ {
+ "prefix":"192.168.1.0\/24",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ }
+ ]
+}
+
--- /dev/null
+{
+ "192.168.1.0\/24":[
+ {
+ "prefix":"192.168.1.0\/24",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ }
+ ],
+ "192.168.2.0\/24":[
+ {
+ "prefix":"192.168.2.0\/24",
+ "protocol":"bgp",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":20,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"2001::2",
+ "afi":"ipv6",
+ "labels":[
+ 128
+ ],
+ "weight":1,
+ "seg6local":{
+ "action":"unspec"
+ },
+ "seg6":{
+ "segs":"2001:db8:2:2:8::"
+ }
+ }
+ ]
+ }
+ ]
+}
+
--- /dev/null
+{
+ "2001:1::\/64":[
+ {
+ "prefix":"2001:1::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ }
+ ],
+ "2001:3::\/64":[
+ {
+ "prefix":"2001:3::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ }
+ ],
+ "fe80::\/64":[
+ {
+ "prefix":"fe80::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ },
+ {
+ "prefix":"fe80::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ }
+ ]
+}
+
--- /dev/null
+{
+ "2001:1::\/64":[
+ {
+ "prefix":"2001:1::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ }
+ ],
+ "2001:2::\/64":[
+ {
+ "prefix":"2001:2::\/64",
+ "protocol":"bgp",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":20,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "afi":"ipv6",
+ "labels":[
+ 32
+ ],
+ "weight":1,
+ "seg6local":{
+ "action":"unspec"
+ },
+ "seg6":{
+ "segs":"2001:db8:2:2:2::"
+ }
+ }
+ ]
+ }
+ ],
+ "2001:3::\/64":[
+ {
+ "prefix":"2001:3::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ }
+ ],
+ "fe80::\/64":[
+ {
+ "prefix":"fe80::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ },
+ {
+ "prefix":"fe80::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ }
+ ]
+}
+
--- /dev/null
+{
+ "2001:1::\/64":[
+ {
+ "prefix":"2001:1::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ }
+ ],
+ "2001:3::\/64":[
+ {
+ "prefix":"2001:3::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ }
+ ],
+ "fe80::\/64":[
+ {
+ "prefix":"fe80::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ },
+ {
+ "prefix":"fe80::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ }
+ ]
+}
+
--- /dev/null
+{
+ "2001:1::\/64":[
+ {
+ "prefix":"2001:1::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ }
+ ],
+ "2001:2::\/64":[
+ {
+ "prefix":"2001:2::\/64",
+ "protocol":"bgp",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":20,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "afi":"ipv6",
+ "labels":[
+ 128
+ ],
+ "weight":1,
+ "seg6local":{
+ "action":"unspec"
+ },
+ "seg6":{
+ "segs":"2001:db8:2:2:8::"
+ }
+ }
+ ]
+ }
+ ],
+ "2001:3::\/64":[
+ {
+ "prefix":"2001:3::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ }
+ ],
+ "fe80::\/64":[
+ {
+ "prefix":"fe80::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ },
+ {
+ "prefix":"fe80::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+{
+ "192.168.1.0\/24":[
+ {
+ "prefix":"192.168.1.0\/24",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ }
+ ],
+ "192.168.2.0\/24":[
+ {
+ "prefix":"192.168.2.0\/24",
+ "protocol":"bgp",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":20,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"2001::2",
+ "afi":"ipv6",
+ "labels":[
+ 16
+ ],
+ "weight":1,
+ "seg6local":{
+ "action":"unspec"
+ },
+ "seg6":{
+ "segs":"2001:db8:2:2:1::"
+ }
+ }
+ ]
+ }
+ ]
+}
+
--- /dev/null
+{
+ "192.168.1.0\/24":[
+ {
+ "prefix":"192.168.1.0\/24",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ }
+ ],
+ "192.168.2.0\/24":[
+ {
+ "prefix":"192.168.2.0\/24",
+ "protocol":"bgp",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":20,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"2001::2",
+ "afi":"ipv6",
+ "labels":[
+ 128
+ ],
+ "weight":1,
+ "seg6local":{
+ "action":"unspec"
+ },
+ "seg6":{
+ "segs":"2001:db8:2:2:8::"
+ }
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+{
+ "2001:1::\/64":[
+ {
+ "prefix":"2001:1::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ }
+ ],
+ "2001:2::\/64":[
+ {
+ "prefix":"2001:2::\/64",
+ "protocol":"bgp",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":20,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "afi":"ipv6",
+ "labels":[
+ 16
+ ],
+ "weight":1,
+ "seg6local":{
+ "action":"unspec"
+ },
+ "seg6":{
+ "segs":"2001:db8:2:2:1::"
+ }
+ }
+ ]
+ }
+ ],
+ "2001:3::\/64":[
+ {
+ "prefix":"2001:3::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ }
+ ],
+ "fe80::\/64":[
+ {
+ "prefix":"fe80::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ },
+ {
+ "prefix":"fe80::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+{
+ "2001:1::\/64":[
+ {
+ "prefix":"2001:1::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ }
+ ],
+ "2001:2::\/64":[
+ {
+ "prefix":"2001:2::\/64",
+ "protocol":"bgp",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":20,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "afi":"ipv6",
+ "labels":[
+ 128
+ ],
+ "weight":1,
+ "seg6local":{
+ "action":"unspec"
+ },
+ "seg6":{
+ "segs":"2001:db8:2:2:8::"
+ }
+ }
+ ]
+ }
+ ],
+ "2001:3::\/64":[
+ {
+ "prefix":"2001:3::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ }
+ ],
+ "fe80::\/64":[
+ {
+ "prefix":"fe80::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ },
+ {
+ "prefix":"fe80::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+{
+ "2001:1::\/64":[
+ {
+ "prefix":"2001:1::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ }
+ ],
+ "2001:3::\/64":[
+ {
+ "prefix":"2001:3::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ }
+ ],
+ "fe80::\/64":[
+ {
+ "prefix":"fe80::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ },
+ {
+ "prefix":"fe80::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ }
+ ]
+}
+
--- /dev/null
+{
+ "192.168.1.0\/24":[
+ {
+ "prefix":"192.168.1.0\/24",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+{
+ "2001:1::\/64":[
+ {
+ "prefix":"2001:1::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:2::\/64":[
+ {
+ "prefix":"2001:2::\/64",
+ "protocol":"bgp",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":20,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "afi":"ipv6",
+ "vrf":"default",
+ "active":true,
+ "labels":[
+ 32
+ ],
+ "weight":1,
+ "seg6local":{
+ "action":"unspec"
+ },
+ "seg6":{
+ "segs":"2001:db8:2:2:2::"
+ }
+ }
+ ]
+ }
+ ],
+ "2001:3::\/64":[
+ {
+ "prefix":"2001:3::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fe80::\/64":[
+ {
+ "prefix":"fe80::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"fe80::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "active":true
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+{
+ "2001:4::\/64":[
+ {
+ "prefix":"2001:4::\/64",
+ "protocol":"bgp",
+ "vrfName":"vrf20",
+ "selected":true,
+ "destSelected":true,
+ "distance":20,
+ "metric":0,
+ "installed":true,
+ "table":20,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "afi":"ipv6",
+ "vrf":"default",
+ "active":true,
+ "labels":[
+ 48
+ ],
+ "weight":1,
+ "seg6local":{
+ "action":"unspec"
+ },
+ "seg6":{
+ "segs":"2001:db8:2:2:3::"
+ }
+ }
+ ]
+ }
+ ],
+ "2001:5::\/64":[
+ {
+ "prefix":"2001:5::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf20",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":20,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:6::\/64":[
+ {
+ "prefix":"2001:6::\/64",
+ "protocol":"bgp",
+ "vrfName":"vrf20",
+ "selected":true,
+ "destSelected":true,
+ "distance":20,
+ "metric":0,
+ "installed":true,
+ "table":20,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "afi":"ipv6",
+ "vrf":"default",
+ "active":true,
+ "labels":[
+ 48
+ ],
+ "weight":1,
+ "seg6local":{
+ "action":"unspec"
+ },
+ "seg6":{
+ "segs":"2001:db8:2:2:3::"
+ }
+ }
+ ]
+ }
+ ],
+ "fe80::\/64":[
+ {
+ "prefix":"fe80::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf20",
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":20,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "active":true
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+log file zebra.log
+!
+hostname r1
+password zebra
+!
+log stdout notifications
+log monitor notifications
+log commands
+!
+!debug zebra packet
+!debug zebra dplane
+!debug zebra kernel
+!
+interface eth0
+ ipv6 address 2001::1/64
+!
+interface eth1 vrf vrf10
+ ipv6 address 2001:1::1/64
+ ip address 192.168.1.1/24
+!
+interface eth2 vrf vrf10
+ ipv6 address 2001:3::1/64
+!
+interface eth3 vrf vrf20
+ ipv6 address 2001:5::1/64
+!
+segment-routing
+ srv6
+ locators
+ locator loc1
+ prefix 2001:db8:1:1::/64 block-len 40 node-len 24 func-bits 16
+ !
+ !
+!
+ip forwarding
+ipv6 forwarding
+!
+ipv6 route 2001:db8:2:1::/64 2001::2
+ipv6 route 2001:db8:2:2::/64 2001::2
+ipv6 route 2001:db8:2:3::/64 2001::2
+!
+line vty
+!
--- /dev/null
+frr defaults traditional
+!
+hostname r2
+password zebra
+!
+log stdout notifications
+log monitor notifications
+log commands
+!
+!debug bgp neighbor-events
+!debug bgp zebra
+!debug bgp vnc verbose
+!debug bgp update-groups
+!debug bgp updates in
+!debug bgp updates out
+!debug bgp updates
+!debug bgp vpn label
+!debug bgp vpn leak-from-vrf
+!debug bgp vpn leak-to-vrf
+!debug bgp vpn rmap-event
+!
+router bgp 2
+ bgp router-id 192.0.2.2
+ no bgp ebgp-requires-policy
+ no bgp default ipv4-unicast
+ neighbor 2001::1 remote-as 1
+ neighbor 2001::1 update-source 2001::2
+ neighbor 2001::1 timers 3 10
+ neighbor 2001::1 timers connect 1
+ neighbor 2001::1 capability extended-nexthop
+ !
+ address-family ipv4 vpn
+ neighbor 2001::1 activate
+ exit-address-family
+ !
+ address-family ipv6 vpn
+ neighbor 2001::1 activate
+ exit-address-family
+ !
+ segment-routing srv6
+ locator loc1
+ !
+!
+router bgp 2 vrf vrf10
+ bgp router-id 192.0.2.2
+ no bgp ebgp-requires-policy
+ !
+ address-family ipv6 unicast
+ sid vpn export auto
+ rd vpn export 2:10
+ rt vpn both 99:99
+ import vpn
+ export vpn
+ redistribute connected
+ exit-address-family
+!
+ address-family ipv4 unicast
+ network 192.168.2.0/24
+ sid vpn export auto
+ rd vpn export 22:10
+ rt vpn both 77:77
+ import vpn
+ export vpn
+ redistribute connected
+ exit-address-family
+!
+router bgp 2 vrf vrf20
+ bgp router-id 192.0.2.2
+ no bgp ebgp-requires-policy
+ no bgp default ipv4-unicast
+ !
+ address-family ipv6 unicast
+ sid vpn export auto
+ rd vpn export 2:20
+ rt vpn both 88:88
+ import vpn
+ export vpn
+ redistribute connected
+ exit-address-family
+!!
--- /dev/null
+{
+ "vrfName": "default",
+ "tableVersion": 2,
+ "routerId": "192.0.2.2",
+ "defaultLocPrf": 100,
+ "localAS": 2,
+ "routes": {
+ "routeDistinguishers": {
+ "1:10": {
+ "2001:1::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:1::",
+ "prefixLen": 64,
+ "network": "2001:1::/64",
+ "metric": 0,
+ "weight": 0,
+ "peerId": "2001::1",
+ "path": "1",
+ "origin": "incomplete",
+ "nexthops": [
+ {
+ "ip": "2001::1",
+ "hostname": "r1",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "2001:3::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:3::",
+ "prefixLen": 64,
+ "network": "2001:3::/64",
+ "metric": 0,
+ "weight": 0,
+ "peerId": "2001::1",
+ "path": "1",
+ "origin": "incomplete",
+ "nexthops": [
+ {
+ "ip": "2001::1",
+ "hostname": "r1",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ },
+ "1:20": {
+ "2001:5::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:5::",
+ "prefixLen": 64,
+ "network": "2001:5::/64",
+ "metric": 0,
+ "weight": 0,
+ "peerId": "2001::1",
+ "path": "1",
+ "origin": "incomplete",
+ "nexthops": [
+ {
+ "ip": "2001::1",
+ "hostname": "r1",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ },
+ "2:10": {
+ "2001:2::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:2::",
+ "prefixLen": 64,
+ "network": "2001:2::/64",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "vrf10",
+ "nexthops": [
+ {
+ "ip": "::",
+ "hostname": "r2",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ },
+ "2:20": {
+ "2001:4::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:4::",
+ "prefixLen": 64,
+ "network": "2001:4::/64",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "vrf20",
+ "nexthops": [
+ {
+ "ip": "::",
+ "hostname": "r2",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "2001:6::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:6::",
+ "prefixLen": 64,
+ "network": "2001:6::/64",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "vrf20",
+ "nexthops": [
+ {
+ "ip": "::",
+ "hostname": "r2",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+}
--- /dev/null
+{
+ "2001:1::\/64":[
+ {
+ "prefix":"2001:1::\/64",
+ "protocol":"bgp",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":20,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "afi":"ipv6",
+ "vrf":"default",
+ "active":true,
+ "labels":[
+ 32
+ ],
+ "weight":1,
+ "seg6local":{
+ "action":"unspec"
+ },
+ "seg6":{
+ "segs":"2001:db8:1:1:2::"
+ }
+ }
+ ]
+ }
+ ],
+ "2001:2::\/64":[
+ {
+ "prefix":"2001:2::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:3::\/64":[
+ {
+ "prefix":"2001:3::\/64",
+ "protocol":"bgp",
+ "vrfName":"vrf10",
+ "selected":true,
+ "destSelected":true,
+ "distance":20,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "afi":"ipv6",
+ "vrf":"default",
+ "active":true,
+ "labels":[
+ 32
+ ],
+ "weight":1,
+ "seg6local":{
+ "action":"unspec"
+ },
+ "seg6":{
+ "segs":"2001:db8:1:1:2::"
+ }
+ }
+ ]
+ }
+ ],
+ "fe80::\/64":[
+ {
+ "prefix":"fe80::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf10",
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "active":true
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+{
+ "2001:4::\/64":[
+ {
+ "prefix":"2001:4::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf20",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":20,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:5::\/64":[
+ {
+ "prefix":"2001:5::\/64",
+ "protocol":"bgp",
+ "vrfName":"vrf20",
+ "selected":true,
+ "destSelected":true,
+ "distance":20,
+ "metric":0,
+ "installed":true,
+ "table":20,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "afi":"ipv6",
+ "vrf":"default",
+ "active":true,
+ "labels":[
+ 48
+ ],
+ "weight":1,
+ "seg6local":{
+ "action":"unspec"
+ },
+ "seg6":{
+ "segs":"2001:db8:1:1:3::"
+ }
+ }
+ ]
+ }
+ ],
+ "2001:6::\/64":[
+ {
+ "prefix":"2001:6::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf20",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":20,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "active":true
+ }
+ ]
+ }
+ ],
+ "fe80::\/64":[
+ {
+ "prefix":"fe80::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf20",
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":20,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"fe80::\/64",
+ "protocol":"connected",
+ "vrfName":"vrf20",
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":20,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "active":true
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+log file zebra.log
+!
+hostname r2
+password zebra
+!
+log stdout notifications
+log monitor notifications
+log commands
+!
+!debug zebra packet
+!debug zebra dplane
+!debug zebra kernel
+!
+interface eth0
+ ipv6 address 2001::2/64
+!
+interface eth1 vrf vrf10
+ ipv6 address 2001:2::1/64
+ ip address 192.168.2.1/24
+!
+interface eth2 vrf vrf20
+ ipv6 address 2001:4::1/64
+!
+interface eth3 vrf vrf20
+ ipv6 address 2001:6::1/64
+!
+segment-routing
+ srv6
+ locators
+ locator loc1
+ prefix 2001:db8:2:2::/64 block-len 40 node-len 24 func-bits 16
+ !
+ !
+!
+ip forwarding
+ipv6 forwarding
+!
+ipv6 route 2001:db8:1:1::/64 2001::1
+ipv6 route 2001:db8:1:2::/64 2001::1
+ipv6 route 2001:db8:1:3::/64 2001::1
+!
+line vty
+!
--- /dev/null
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+#
+# Copyright 2023 6WIND S.A.
+# Authored by Dmytro Shytyi <dmytro.shytyi@6wind.com>
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+import os
+import re
+import sys
+import json
+import functools
+import pytest
+
+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
+from lib.common_config import required_linux_kernel_version
+
+
+def build_topo(tgen):
+ """
+ CE1 CE3 CE5
+ (eth0) (eth0) (eth0)
+ :2 :2 :2
+ | | |
+ 192.168.1.0 | |
+ /24 | |
+ 2001: 2001: 2001:
+ 1::/64 3::/64 5::/64
+ | | |
+ :1 :1 :1
+ +-(eth1)--(eth2)---(eth3)-+
+ | \ / | |
+ | (vrf10) (vrf20) |
+ | R1 |
+ +----------(eth0)---------+
+ :1
+ |
+ 2001::/64
+ |
+ :2
+ (eth0)
+ +----------(eth0)--------------+
+ | R2 |
+ | (vrf10) (vrf20) |
+ | / / \ |
+ +-(eth1)-----(eth2)-----(eth3)-+
+ :1 :1 :1
+ | | |
+ +------+ +------+ +------+
+ / 2001: \ / 2001: \ / 2001: \
+ / 2::/64 \ 4::/64 / \ 6::/64 /
+ /192.168.2.0| / \ /
+ \ /24 / \ | | |
+ +------+ +------+ +------+
+ | | |
+ :2 :2 :2
+ (eth0) (eth0) (eth0)
+ CE2 CE4 CE6
+ """
+
+ tgen.add_router("r1")
+ tgen.add_router("r2")
+ tgen.add_router("ce1")
+ tgen.add_router("ce2")
+ tgen.add_router("ce3")
+ tgen.add_router("ce4")
+ tgen.add_router("ce5")
+ tgen.add_router("ce6")
+
+ tgen.add_link(tgen.gears["r1"], tgen.gears["r2"], "eth0", "eth0")
+ tgen.add_link(tgen.gears["ce1"], tgen.gears["r1"], "eth0", "eth1")
+ tgen.add_link(tgen.gears["ce2"], tgen.gears["r2"], "eth0", "eth1")
+ tgen.add_link(tgen.gears["ce3"], tgen.gears["r1"], "eth0", "eth2")
+ tgen.add_link(tgen.gears["ce4"], tgen.gears["r2"], "eth0", "eth2")
+ tgen.add_link(tgen.gears["ce5"], tgen.gears["r1"], "eth0", "eth3")
+ tgen.add_link(tgen.gears["ce6"], tgen.gears["r2"], "eth0", "eth3")
+
+
+def setup_module(mod):
+ result = required_linux_kernel_version("5.11")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+ router_list = tgen.routers()
+ for i, (rname, router) in enumerate(tgen.routers().items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.gears["r1"].run("modprobe vrf")
+ tgen.gears["r1"].run("ip link add vrf10 type vrf table 10")
+ tgen.gears["r1"].run("ip link set vrf10 up")
+ tgen.gears["r1"].run("ip link add vrf20 type vrf table 20")
+ tgen.gears["r1"].run("ip link set vrf20 up")
+ tgen.gears["r1"].run("ip link set eth1 master vrf10")
+ tgen.gears["r1"].run("ip link set eth2 master vrf10")
+ tgen.gears["r1"].run("ip link set eth3 master vrf20")
+ tgen.gears["r1"].run("sysctl net.vrf.strict_mode=1")
+ tgen.gears["r1"].run("sysctl net.ipv4.conf.default.rp_filter=0")
+ tgen.gears["r1"].run("sysctl net.ipv4.conf.all.rp_filter=0")
+ tgen.gears["r1"].run("sysctl net.ipv4.conf.lo.rp_filter=0")
+ tgen.gears["r1"].run("sysctl net.ipv4.conf.eth0.rp_filter=0")
+ tgen.gears["r1"].run("sysctl net.ipv4.conf.eth1.rp_filter=0")
+ tgen.gears["r1"].run("sysctl net.ipv4.conf.vrf10.rp_filter=0")
+
+ tgen.gears["r2"].run("modprobe vrf")
+ tgen.gears["r2"].run("ip link add vrf10 type vrf table 10")
+ tgen.gears["r2"].run("ip link set vrf10 up")
+ tgen.gears["r2"].run("ip link add vrf20 type vrf table 20")
+ tgen.gears["r2"].run("ip link set vrf20 up")
+ tgen.gears["r2"].run("ip link set eth1 master vrf10")
+ tgen.gears["r2"].run("ip link set eth2 master vrf20")
+ tgen.gears["r2"].run("ip link set eth3 master vrf20")
+ tgen.gears["r2"].run("sysctl net.vrf.strict_mode=1")
+ tgen.gears["r2"].run("sysctl net.ipv4.conf.default.rp_filter=0")
+ tgen.gears["r2"].run("sysctl net.ipv4.conf.all.rp_filter=0")
+ tgen.gears["r2"].run("sysctl net.ipv4.conf.lo.rp_filter=0")
+ tgen.gears["r2"].run("sysctl net.ipv4.conf.eth0.rp_filter=0")
+ tgen.gears["r2"].run("sysctl net.ipv4.conf.eth1.rp_filter=0")
+ tgen.gears["r2"].run("sysctl net.ipv4.conf.vrf10.rp_filter=0")
+ tgen.start_router()
+
+ # FOR DEVELOPER:
+ # If you want to stop some specific line and start interactive shell,
+ # please use tgen.mininet_cli() to start it.
+ # Example:
+ # tgen=get_topogen()
+ # tgen.mininet_cli()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+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_ping(name, dest_addr, expect_connected):
+ def _check(name, dest_addr, match):
+ tgen = get_topogen()
+ output = tgen.gears[name].run("ping {} -c 1 -w 1".format(dest_addr))
+ logger.info(output)
+ if match not in output:
+ return True
+
+ match = ", {} packet loss".format("0%" if expect_connected else "100%")
+ logger.info("[+] check {} {} {}".format(name, dest_addr, match))
+ tgen = get_topogen()
+ func = functools.partial(_check, name, dest_addr, match)
+ success, result = topotest.run_and_expect(func, None, count=10, wait=0.5)
+ assert result is None, "Failed"
+
+
+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 = functools.partial(_check, name, cmd, expected_file)
+ success, result = topotest.run_and_expect(func, None, count=10, wait=0.5)
+ assert result is None, "Failed"
+
+
+def test_rib():
+ check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib.json")
+ check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib.json")
+ check_rib("r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_rib.json")
+ check_rib("r1", "show ipv6 route vrf vrf20 json", "r1/vrf20_rib.json")
+ check_rib("r2", "show ipv6 route vrf vrf10 json", "r2/vrf10_rib.json")
+ check_rib("r2", "show ipv6 route vrf vrf20 json", "r2/vrf20_rib.json")
+ check_rib("ce1", "show ipv6 route json", "ce1/ipv6_rib.json")
+ check_rib("ce2", "show ipv6 route json", "ce2/ipv6_rib.json")
+ check_rib("ce3", "show ipv6 route json", "ce3/ipv6_rib.json")
+ check_rib("ce4", "show ipv6 route json", "ce4/ipv6_rib.json")
+ check_rib("ce5", "show ipv6 route json", "ce5/ipv6_rib.json")
+ check_rib("ce6", "show ipv6 route json", "ce6/ipv6_rib.json")
+
+
+def test_ping():
+ check_ping("ce1", "2001:2::2", True)
+ check_ping("ce1", "2001:3::2", True)
+ check_ping("ce1", "2001:4::2", False)
+ check_ping("ce1", "2001:5::2", False)
+ check_ping("ce1", "2001:6::2", False)
+ check_ping("ce4", "2001:1::2", False)
+ check_ping("ce4", "2001:2::2", False)
+ check_ping("ce4", "2001:3::2", False)
+ check_ping("ce4", "2001:5::2", True)
+ check_ping("ce4", "2001:6::2", True)
+
+
+def test_sid_per_afv6_auto():
+ check_rib("r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_afv6_auto_sid_rib.json")
+ check_ping("ce1", "2001:2::2", True)
+ get_topogen().gears["r2"].vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2 vrf vrf10
+ address-family ipv6 unicast
+ no sid vpn export auto
+ """
+ )
+ check_rib(
+ "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_afv6_auto_no_sid_rib.json"
+ )
+ check_ping("ce1", "2001:2::2", False)
+
+ get_topogen().gears["r2"].vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2 vrf vrf10
+ address-family ipv6 unicast
+ sid vpn export auto
+ """
+ )
+ check_rib("r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_afv6_auto_sid_rib.json")
+ check_ping("ce1", "2001:2::2", True)
+
+ get_topogen().gears["r2"].vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2 vrf vrf10
+ address-family ipv6 unicast
+ no sid vpn export auto
+ """
+ )
+ check_rib(
+ "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_afv6_auto_no_sid_rib.json"
+ )
+ check_ping("ce1", "2001:2::2", False)
+
+
+def test_sid_per_afv6_manual():
+ check_rib(
+ "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_afv6_manual_no_sid_rib.json"
+ )
+ check_ping("ce1", "2001:2::2", False)
+
+ get_topogen().gears["r2"].vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2 vrf vrf10
+ address-family ipv6 unicast
+ sid vpn export 8
+ """
+ )
+
+ check_rib(
+ "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_afv6_manual_sid_rib.json"
+ )
+ check_ping("ce1", "2001:2::2", True)
+
+ get_topogen().gears["r2"].vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2 vrf vrf10
+ address-family ipv6 unicast
+ no sid vpn export 8
+ """
+ )
+ check_rib(
+ "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_afv6_manual_no_sid_rib.json"
+ )
+ check_ping("ce1", "2001:2::2", False)
+
+
+def test_sid_per_afv4_auto():
+ check_rib("r1", "show ip route vrf vrf10 json", "r1/vrf10_afv4_auto_sid_rib.json")
+ check_ping("ce1", "192.168.2.2", True)
+ get_topogen().gears["r2"].vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2 vrf vrf10
+ address-family ipv4 unicast
+ no sid vpn export auto
+ """
+ )
+
+ check_rib(
+ "r1", "show ip route vrf vrf10 json", "r1/vrf10_afv4_auto_no_sid_rib.json"
+ )
+ check_ping("ce1", "192.168.2.2", False)
+
+ get_topogen().gears["r2"].vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2 vrf vrf10
+ address-family ipv4 unicast
+ sid vpn export auto
+ """
+ )
+
+ check_rib("r1", "show ip route vrf vrf10 json", "r1/vrf10_afv4_auto_sid_rib.json")
+ check_ping("ce1", "192.168.2.2", True)
+
+ get_topogen().gears["r2"].vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2 vrf vrf10
+ address-family ipv4 unicast
+ no sid vpn export auto
+ """
+ )
+ check_rib(
+ "r1", "show ip route vrf vrf10 json", "r1/vrf10_afv4_auto_no_sid_rib.json"
+ )
+ check_ping("ce1", "192.168.2.2", False)
+
+
+def test_sid_per_afv4_manual():
+ check_rib(
+ "r1", "show ip route vrf vrf10 json", "r1/vrf10_afv4_manual_no_sid_rib.json"
+ )
+ check_ping("ce1", "192.168.2.2", False)
+ get_topogen().gears["r2"].vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2 vrf vrf10
+ address-family ipv4 unicast
+ sid vpn export 8
+ """
+ )
+
+ check_rib("r1", "show ip route vrf vrf10 json", "r1/vrf10_afv4_manual_sid_rib.json")
+ check_ping("ce1", "192.168.2.2", True)
+
+ get_topogen().gears["r2"].vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2 vrf vrf10
+ address-family ipv4 unicast
+ no sid vpn export 8
+ """
+ )
+ check_ping("ce1", "192.168.2.2", False)
+ check_rib(
+ "r1", "show ip route vrf vrf10 json", "r1/vrf10_afv4_manual_no_sid_rib.json"
+ )
+
+
+def test_sid_per_vrf_auto():
+ check_rib(
+ "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_pervrf_auto_no_sid_rib.json"
+ )
+ check_ping("ce1", "2001:2::2", False)
+ get_topogen().gears["r2"].vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2 vrf vrf10
+ sid vpn per-vrf export auto
+ """
+ )
+
+ check_rib(
+ "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_pervrf6_auto_sid_rib.json"
+ )
+ check_ping("ce1", "2001:2::2", True)
+ check_rib(
+ "r1", "show ip route vrf vrf10 json", "r1/vrf10_pervrf4_auto_sid_rib.json"
+ )
+ check_ping("ce1", "192.168.2.2", True)
+
+ get_topogen().gears["r2"].vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2 vrf vrf10
+ no sid vpn per-vrf export auto
+ """
+ )
+
+ check_rib(
+ "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_pervrf_auto_no_sid_rib.json"
+ )
+ check_ping("ce1", "2001:2::2", False)
+
+
+def test_sid_per_vrf_manual():
+ check_rib(
+ "r1", "show ip route vrf vrf10 json", "r1/vrf10_pervrf_manual_no_sid_rib.json"
+ )
+ check_ping("ce1", "192.168.2.2", False)
+ get_topogen().gears["r2"].vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2 vrf vrf10
+ sid vpn per-vrf export 8
+ """
+ )
+
+ check_rib(
+ "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_pervrf6_manual_sid_rib.json"
+ )
+ check_ping("ce1", "2001:2::2", True)
+ check_rib(
+ "r1", "show ip route vrf vrf10 json", "r1/vrf10_pervrf4_manual_sid_rib.json"
+ )
+ check_ping("ce1", "192.168.2.2", True)
+
+ get_topogen().gears["r2"].vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2 vrf vrf10
+ no sid vpn per-vrf export 8
+ """
+ )
+
+ check_rib(
+ "r1", "show ip route vrf vrf10 json", "r1/vrf10_pervrf_manual_no_sid_rib.json"
+ )
+ check_ping("ce1", "192.168.2.2", False)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
log monitor notifications
log commands
!
-debug zebra packet
-debug zebra dplane
-debug zebra kernel
+!debug zebra packet
+!debug zebra dplane
+!debug zebra kernel
!
interface eth0
ipv6 address 2001::1/64
log monitor notifications
log commands
!
-debug zebra packet
-debug zebra dplane
-debug zebra kernel
+!debug zebra packet
+!debug zebra dplane
+!debug zebra kernel
!
interface eth0
ipv6 address 2001::2/64
log monitor notifications
log commands
!
-debug zebra packet
-debug zebra dplane
-debug zebra kernel
+!debug zebra packet
+!debug zebra dplane
+!debug zebra kernel
!
interface eth0
ipv6 address 2001::1/64
log monitor notifications
log commands
!
-debug zebra packet
-debug zebra dplane
-debug zebra kernel
+!debug zebra packet
+!debug zebra dplane
+!debug zebra kernel
!
interface eth0
ipv6 address 2001::2/64
!
ip route 192.168.1.1/32 10.0.0.10
!
-debug bgp bestpath
-debug bgp nht
-debug bgp updates
-debug bgp update-groups
-debug bgp zebra
-debug zebra rib detail
+!debug bgp bestpath
+!debug bgp nht
+!debug bgp updates
+!debug bgp update-groups
+!debug bgp zebra
+!debug zebra rib detail
!
router bgp 2
address-family ipv4 uni
-debug bgp updates
-debug bgp bestpath 40.0.0.0/8
-debug bgp zebra
+!debug bgp updates
+!debug bgp bestpath 40.0.0.0/8
+!debug bgp zebra
!
router bgp 2
no bgp ebgp-requires-policy
neighbor 10.0.0.1 remote-as 1
neighbor 10.0.0.10 remote-as 3
address-family ipv4 uni
- network 60.0.0.0/24
\ No newline at end of file
+ network 60.0.0.0/24
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)
!
-debug bgp neighbor
+!debug bgp neighbor
!
router bgp 65534 vrf public
bgp router-id 10.0.0.1
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
)
# 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
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
"""
Topotest conftest.py file.
"""
# pylint: disable=consider-using-f-string
import glob
+import logging
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
+import pytest
+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.topolog import get_test_logdir, logger
from lib.topotest import json_cmp_result
+from munet import cli
+from munet.base import Commander, proc_error
+from munet.cleanup import cleanup_current, cleanup_previous
+from munet.config import ConfigOptionsProxy
+from munet.testing.util import pause_test
+
+from lib import topolog, topotest
+
+try:
+ # Used by munet native tests
+ from munet.testing.fixtures import event_loop, unet # pylint: disable=all # noqa
+
+ @pytest.fixture(scope="module")
+ def rundir_module(pytestconfig):
+ d = os.path.join(pytestconfig.option.rundir, get_test_logdir())
+ logging.debug("rundir_module: test module rundir %s", d)
+ return d
+
+except (AttributeError, ImportError):
+ pass
def pytest_addoption(parser):
help="Comma-separated list of routers to spawn gdb on, or 'all'",
)
+ parser.addoption(
+ "--logd",
+ action="append",
+ metavar="DAEMON[,ROUTER[,...]",
+ help=(
+ "Tail-F the DAEMON log file on all or a subset of ROUTERs."
+ " Option can be given multiple times."
+ ),
+ )
+
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="Do not pause after (disables default when --shell or -vtysh given)",
)
+ parser.addoption(
+ "--pcap",
+ default="",
+ metavar="NET[,NET...]",
+ help="Comma-separated list of networks to capture packets on, or 'all'",
+ )
+
+ parser.addoption(
+ "--perf",
+ action="append",
+ metavar="DAEMON[,ROUTER[,...]",
+ help=(
+ "Collect performance data from given DAEMON on all or a subset of ROUTERs."
+ " Option can be given multiple times."
+ ),
+ )
+
+ parser.addoption(
+ "--perf-options",
+ metavar="OPTS",
+ default="-g",
+ help="Options to pass to `perf record`.",
+ )
+
rundir_help = "directory for running in and log files"
parser.addini("rundir", rundir_help, default="/tmp/topotests")
parser.addoption("--rundir", metavar="DIR", help=rundir_help)
def check_for_memleaks():
- assert topotest_extra_config["valgrind_memleaks"]
+ assert topotest.g_pytest_config.option.valgrind_memleaks
leaks = []
tgen = get_topogen() # pylint: disable=redefined-outer-name
@pytest.fixture(autouse=True, scope="module")
def module_check_memtest(request):
- del request # disable unused warning
yield
- if topotest_extra_config["valgrind_memleaks"]:
+ if request.config.option.valgrind_memleaks:
if get_topogen() is not None:
check_for_memleaks()
def pytest_runtest_logstart(nodeid, location):
# location is (filename, lineno, testname)
- topolog.logstart(nodeid, location, topotest_extra_config["rundir"])
+ topolog.logstart(nodeid, location, topotest.g_pytest_config.option.rundir)
def pytest_runtest_logfinish(nodeid, location):
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_call(item: pytest.Item) -> None:
"Hook the function that is called to execute the test."
- del item # disable unused warning
# For topology only run the CLI then exit
- if topotest_extra_config["topology_only"]:
+ if item.config.option.topology_only:
get_topogen().cli()
pytest.exit("exiting after --topology-only")
yield
# Check for leaks if requested
- if topotest_extra_config["valgrind_memleaks"]:
+ if item.config.option.valgrind_memleaks:
check_for_memleaks()
"""
Assert that the environment is correctly configured, and get extra config.
"""
+ topotest.g_pytest_config = ConfigOptionsProxy(config)
if config.getoption("--collect-only"):
return
# Set some defaults for the pytest.ini [pytest] section
# ---------------------------------------------------
- rundir = config.getoption("--rundir")
+ rundir = config.option.rundir
if not rundir:
rundir = config.getini("rundir")
if not rundir:
rundir = "/tmp/topotests"
+ config.option.rundir = rundir
+
if not config.getoption("--junitxml"):
config.option.xmlpath = os.path.join(rundir, "topotests.xml")
xmlpath = config.option.xmlpath
mv_path = commander.get_exec_path("mv")
commander.cmd_status([mv_path, xmlpath, xmlpath + suffix])
- topotest_extra_config["rundir"] = rundir
-
# Set the log_file (exec) to inside the rundir if not specified
if not config.getoption("--log-file") and not config.getini("log_file"):
config.option.log_file = os.path.join(rundir, "exec.log")
elif b and not is_xdist and not have_windows:
pytest.exit("{} use requires byobu/TMUX/SCREEN/XTerm".format(feature))
- # ---------------------------------------
- # Record our options in global dictionary
- # ---------------------------------------
-
- topotest_extra_config["rundir"] = rundir
-
- asan_abort = config.getoption("--asan-abort")
- topotest_extra_config["asan_abort"] = asan_abort
-
- gdb_routers = config.getoption("--gdb-routers")
- gdb_routers = gdb_routers.split(",") if gdb_routers else []
- topotest_extra_config["gdb_routers"] = gdb_routers
-
- gdb_daemons = config.getoption("--gdb-daemons")
- gdb_daemons = gdb_daemons.split(",") if gdb_daemons else []
- topotest_extra_config["gdb_daemons"] = gdb_daemons
- assert_feature_windows(gdb_routers or gdb_daemons, "GDB")
-
- gdb_breakpoints = config.getoption("--gdb-breakpoints")
- gdb_breakpoints = gdb_breakpoints.split(",") if gdb_breakpoints else []
- topotest_extra_config["gdb_breakpoints"] = gdb_breakpoints
-
- cli_on_error = config.getoption("--cli-on-error")
- topotest_extra_config["cli_on_error"] = cli_on_error
- assert_feature_windows(cli_on_error, "--cli-on-error")
-
- shell = config.getoption("--shell")
- topotest_extra_config["shell"] = shell.split(",") if shell else []
- assert_feature_windows(shell, "--shell")
-
- strace = config.getoption("--strace-daemons")
- topotest_extra_config["strace_daemons"] = strace.split(",") if strace else []
-
- shell_on_error = config.getoption("--shell-on-error")
- topotest_extra_config["shell_on_error"] = shell_on_error
- assert_feature_windows(shell_on_error, "--shell-on-error")
-
- topotest_extra_config["valgrind_extra"] = config.getoption("--valgrind-extra")
- topotest_extra_config["valgrind_memleaks"] = config.getoption("--valgrind-memleaks")
-
- vtysh = config.getoption("--vtysh")
- topotest_extra_config["vtysh"] = vtysh.split(",") if vtysh else []
- assert_feature_windows(vtysh, "--vtysh")
-
- vtysh_on_error = config.getoption("--vtysh-on-error")
- topotest_extra_config["vtysh_on_error"] = vtysh_on_error
- assert_feature_windows(vtysh_on_error, "--vtysh-on-error")
-
- pause_on_error = vtysh or shell or config.getoption("--pause-on-error")
- if config.getoption("--no-pause-on-error"):
- pause_on_error = False
-
- topotest_extra_config["pause_on_error"] = pause_on_error
- assert_feature_windows(pause_on_error, "--pause-on-error")
-
- pause = config.getoption("--pause")
- topotest_extra_config["pause"] = pause
- assert_feature_windows(pause, "--pause")
-
- topology_only = config.getoption("--topology-only")
- if topology_only and is_xdist:
+ #
+ # Check for window capability if given options that require window
+ #
+ assert_feature_windows(config.option.gdb_routers, "GDB")
+ assert_feature_windows(config.option.gdb_daemons, "GDB")
+ assert_feature_windows(config.option.cli_on_error, "--cli-on-error")
+ assert_feature_windows(config.option.shell, "--shell")
+ assert_feature_windows(config.option.shell_on_error, "--shell-on-error")
+ assert_feature_windows(config.option.vtysh, "--vtysh")
+ assert_feature_windows(config.option.vtysh_on_error, "--vtysh-on-error")
+
+ if config.option.topology_only and is_xdist:
pytest.exit("Cannot use --topology-only with distributed test mode")
- topotest_extra_config["topology_only"] = topology_only
# Check environment now that we have config
if not diagnose_env(rundir):
# We want to pause, if requested, on any error not just test cases
# (e.g., call.when == "setup")
if not pause:
- pause = (
- topotest_extra_config["pause_on_error"]
- or topotest_extra_config["pause"]
- )
+ pause = item.config.option.pause_on_error or item.config.option.pause
# (topogen) Set topology error to avoid advancing in the test.
tgen = get_topogen() # pylint: disable=redefined-outer-name
isatty = sys.stdout.isatty()
error_cmd = None
- if error and topotest_extra_config["vtysh_on_error"]:
+ if error and item.config.option.vtysh_on_error:
error_cmd = commander.get_exec_path(["vtysh"])
- elif error and topotest_extra_config["shell_on_error"]:
+ elif error and item.config.option.shell_on_error:
error_cmd = os.getenv("SHELL", commander.get_exec_path(["bash"]))
if error_cmd:
if p.wait():
logger.warning("xterm proc failed: %s:", proc_error(p, o, e))
- if error and topotest_extra_config["cli_on_error"]:
+ if error and item.config.option.cli_on_error:
# 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")
- while pause and isatty:
- try:
- user = raw_input(
- 'PAUSED, "cli" for CLI, "pdb" to debug, "Enter" to continue: '
- )
- except NameError:
- user = input('PAUSED, "cli" for CLI, "pdb" to debug, "Enter" to continue: ')
- user = user.strip()
-
- if user == "cli":
- cli(Mininet.g_mnet_inst)
- elif user == "pdb":
- pdb.set_trace() # pylint: disable=forgotten-debug-statement
- elif user:
- print('Unrecognized input: "%s"' % user)
- else:
- break
+ if pause and isatty:
+ pause_test()
#
],
"edges":[
{
- "edge-id":65537,
+ "edge-id":"2001:db8:1::1:1",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0001",
}
},
{
- "edge-id":65538,
+ "edge-id":"2001:db8:1::1:2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":196610,
+ "edge-id":"2001:db8:3::3:2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":196611,
+ "edge-id":"2001:db8:3::3:3",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0003",
}
},
{
- "edge-id":196612,
+ "edge-id":"2001:db8:5::3:4",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0004",
]
},
{
- "edge-id":262147,
+ "edge-id":"2001:db8:5::4:3",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0003",
}
},
{
- "edge-id":167772161,
+ "edge-id":"10.0.0.1",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0001",
}
},
{
- "edge-id":167772162,
+ "edge-id":"10.0.0.2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":167772417,
+ "edge-id":"10.0.1.1",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0001",
}
},
{
- "edge-id":167772418,
+ "edge-id":"10.0.1.2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":167772930,
+ "edge-id":"10.0.3.2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":167772931,
+ "edge-id":"10.0.3.3",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0003",
}
},
{
- "edge-id":167773186,
+ "edge-id":"10.0.4.2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":167773188,
+ "edge-id":"10.0.4.4",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0004",
--- /dev/null
+version: 1
+topology:
+ ipv6-enable: true
+ networks-autonumber: true
+ networks:
+ - name: net1
+ - name: net2
+ nodes:
+ - name: r1
+ kind: frr
+ connections: ["net1"]
+ - name: r2
+ kind: frr
+ connections: ["net1", "net2"]
+ - name: r3
+ kind: frr
+ connections: ["net2"]
--- /dev/null
+zebra=1
+staticd=1
+vtysh_enable=1
+watchfrr_enable=1
+zebra_options="-d -F traditional --log=file:/var/log/frr/zebra.log"
+staticd_options="-d -F traditional --log=file:/var/log/frr/staticd.log"
--- /dev/null
+log file /var/log/frr/frr.log
+service integrated-vtysh-config
+
+interface eth0
+ ip address 10.0.1.1/24
+
+ip route 10.0.0.0/8 blackhole
--- /dev/null
+service integrated-vtysh-config
\ No newline at end of file
--- /dev/null
+zebra=1
+staticd=1
+vtysh_enable=1
+watchfrr_enable=1
+zebra_options="-d -F traditional --log=file:/var/log/frr/zebra.log"
+staticd_options="-d -F traditional --log=file:/var/log/frr/staticd.log"
--- /dev/null
+log file /var/log/frr/frr.log
+service integrated-vtysh-config
+
+interface eth0
+ ip address 10.0.1.2/24
+
+interface eth1
+ ip address 10.0.2.2/24
+
+ip route 10.0.0.0/8 blackhole
--- /dev/null
+service integrated-vtysh-config
\ No newline at end of file
--- /dev/null
+zebra=1
+staticd=1
+vtysh_enable=1
+watchfrr_enable=1
+zebra_options="-d -F traditional --log=file:/var/log/frr/zebra.log"
+staticd_options="-d -F traditional --log=file:/var/log/frr/staticd.log"
--- /dev/null
+log file /var/log/frr/frr.log
+service integrated-vtysh-config
+
+interface eth0
+ ip address 10.0.2.3/24
+
+ip route 10.0.0.0/8 blackhole
--- /dev/null
+service integrated-vtysh-config
\ No newline at end of file
--- /dev/null
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# April 23 2023, Christian Hopps <chopps@labn.net>
+#
+# Copyright (c) 2023, LabN Consulting, L.L.C.
+#
+async def test_native_test(unet):
+ o = unet.hosts["r1"].cmd_nostatus("ip addr")
+ print(o)
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
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
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:
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
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
)
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
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.
Topology:
r2
- / \
+ // \\
r1 r4
- \ /
+ \\ //
r3
Devices are configured with preferred route between r1 and r4:
hostname r5
log file ldpd.log
!
-debug mpls ldp zebra
-debug mpls ldp event
-debug mpls ldp errors
-debug mpls ldp sync
+!debug mpls ldp zebra
+!debug mpls ldp event
+!debug mpls ldp errors
+!debug mpls ldp sync
!
mpls ldp
router-id 3.3.3.3
--- /dev/null
+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
+!
--- /dev/null
+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
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+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
+
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+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
+
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+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
+
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+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
+
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+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
+
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+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
+
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+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
+
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+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
+
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+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
+
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+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
+
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+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
+!
--- /dev/null
+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
+!
--- /dev/null
+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
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+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
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+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
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+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
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+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
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+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
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+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
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+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
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+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
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+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
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+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
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+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
+!
--- /dev/null
+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
+!
--- /dev/null
+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
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+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
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+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
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+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
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+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
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+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
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+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
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+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
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+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
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+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
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+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
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+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
+!
--- /dev/null
+#!/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))
--- /dev/null
+
+## 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
+```
--- /dev/null
+#!/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
--- /dev/null
+!
+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
+!
--- /dev/null
+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
+!
--- /dev/null
+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
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+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
+!
--- /dev/null
+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
+!
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+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
+!
--- /dev/null
+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
+!
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+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
+!
--- /dev/null
+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
+!
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+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
+!
--- /dev/null
+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
+!
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+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
+!
--- /dev/null
+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
+!
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+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
+!
--- /dev/null
+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
+!
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+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
+!
--- /dev/null
+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
+!
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+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
+!
--- /dev/null
+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
+!
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+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
+!
--- /dev/null
+!
+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
+!
--- /dev/null
+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
+!
--- /dev/null
+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
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+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
+!
--- /dev/null
+#!/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))
],
"edges":[
{
- "edge-id":65537,
+ "edge-id":"2001:db8:1::1:1",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0001",
}
},
{
- "edge-id":65538,
+ "edge-id":"2001:db8:1::1:2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":196610,
+ "edge-id":"2001:db8:3::3:2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":196611,
+ "edge-id":"2001:db8:3::3:3",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0003",
}
},
{
- "edge-id":196612,
+ "edge-id":"2001:db8:5::3:4",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0004",
]
},
{
- "edge-id":262147,
+ "edge-id":"2001:db8:5::4:3",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0003",
}
},
{
- "edge-id":167772161,
+ "edge-id":"10.0.0.1",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0001",
}
},
{
- "edge-id":167772162,
+ "edge-id":"10.0.0.2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":167772417,
+ "edge-id":"10.0.1.1",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0001",
}
},
{
- "edge-id":167772418,
+ "edge-id":"10.0.1.2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":167772930,
+ "edge-id":"10.0.3.2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":167772931,
+ "edge-id":"10.0.3.3",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0003",
}
},
{
- "edge-id":167773186,
+ "edge-id":"10.0.4.2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":167773188,
+ "edge-id":"10.0.4.4",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0004",
],
"edges":[
{
- "edge-id":1,
+ "edge-id":"2001:db8::1",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0001",
}
},
{
- "edge-id":2,
+ "edge-id":"2001:db8::2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":65537,
+ "edge-id":"2001:db8:1::1:1",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0001",
}
},
{
- "edge-id":65538,
+ "edge-id":"2001:db8:1::1:2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":196610,
+ "edge-id":"2001:db8:3::3:2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":196611,
+ "edge-id":"2001:db8:3::3:3",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0003",
}
},
{
- "edge-id":196612,
+ "edge-id":"2001:db8:5::3:4",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0004",
]
},
{
- "edge-id":262147,
+ "edge-id":"2001:db8:5::4:3",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0003",
}
},
{
- "edge-id":167772161,
+ "edge-id":"10.0.0.1",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0001",
}
},
{
- "edge-id":167772162,
+ "edge-id":"10.0.0.2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":167772417,
+ "edge-id":"10.0.1.1",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0001",
}
},
{
- "edge-id":167772418,
+ "edge-id":"10.0.1.2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":167772930,
+ "edge-id":"10.0.3.2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":167772931,
+ "edge-id":"10.0.3.3",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0003",
}
},
{
- "edge-id":167773186,
+ "edge-id":"10.0.4.2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":167773188,
+ "edge-id":"10.0.4.4",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0004",
],
"edges":[
{
- "edge-id":196610,
+ "edge-id":"2001:db8:3::3:2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":196611,
+ "edge-id":"2001:db8:3::3:3",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0003",
}
},
{
- "edge-id":196612,
+ "edge-id":"2001:db8:5::3:4",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0004",
]
},
{
- "edge-id":262147,
+ "edge-id":"2001:db8:5::4:3",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0003",
}
},
{
- "edge-id":167772161,
+ "edge-id":"10.0.0.1",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0001",
}
},
{
- "edge-id":167772162,
+ "edge-id":"10.0.0.2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":167772930,
+ "edge-id":"10.0.3.2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":167772931,
+ "edge-id":"10.0.3.3",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0003",
}
},
{
- "edge-id":167773186,
+ "edge-id":"10.0.4.2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":167773188,
+ "edge-id":"10.0.4.4",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0004",
],
"edges":[
{
- "edge-id":1,
+ "edge-id":"2001:db8::1",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0001",
}
},
{
- "edge-id":2,
+ "edge-id":"2001:db8::2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":196610,
+ "edge-id":"2001:db8:3::3:2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":196611,
+ "edge-id":"2001:db8:3::3:3",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0003",
}
},
{
- "edge-id":196612,
+ "edge-id":"2001:db8:5::3:4",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0004",
]
},
{
- "edge-id":262147,
+ "edge-id":"2001:db8:5::4:3",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0003",
}
},
{
- "edge-id":167772161,
+ "edge-id":"10.0.0.1",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0001",
}
},
{
- "edge-id":167772162,
+ "edge-id":"10.0.0.2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":167772930,
+ "edge-id":"10.0.3.2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":167772931,
+ "edge-id":"10.0.3.3",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0003",
}
},
{
- "edge-id":167773186,
+ "edge-id":"10.0.4.2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":167773188,
+ "edge-id":"10.0.4.4",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0004",
],
"edges":[
{
- "edge-id":1,
+ "edge-id":"2001:db8::1",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0001",
}
},
{
- "edge-id":2,
+ "edge-id":"2001:db8::2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":196610,
+ "edge-id":"2001:db8:3::3:2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":196611,
+ "edge-id":"2001:db8:3::3:3",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0003",
}
},
{
- "edge-id":196612,
+ "edge-id":"2001:db8:5::3:4",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0004",
]
},
{
- "edge-id":262147,
+ "edge-id":"2001:db8:5::4:3",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0003",
}
},
{
- "edge-id":167772161,
+ "edge-id":"10.0.0.1",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0001",
}
},
{
- "edge-id":167772162,
+ "edge-id":"10.0.0.2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":167772930,
+ "edge-id":"10.0.3.2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":167772931,
+ "edge-id":"10.0.3.3",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0003",
}
},
{
- "edge-id":167773186,
+ "edge-id":"10.0.4.2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":167773188,
+ "edge-id":"10.0.4.4",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0004",
],
"edges":[
{
- "edge-id":1,
+ "edge-id":"2001:db8::1",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0001",
}
},
{
- "edge-id":2,
+ "edge-id":"2001:db8::2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":65537,
+ "edge-id":"2001:db8:1::1:1",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0001",
}
},
{
- "edge-id":65538,
+ "edge-id":"2001:db8:1::1:2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":196610,
+ "edge-id":"2001:db8:3::3:2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":196611,
+ "edge-id":"2001:db8:3::3:3",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0003",
}
},
{
- "edge-id":196612,
+ "edge-id":"2001:db8:5::3:4",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0004",
]
},
{
- "edge-id":262147,
+ "edge-id":"2001:db8:5::4:3",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0003",
}
},
{
- "edge-id":167772161,
+ "edge-id":"10.0.0.1",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0001",
}
},
{
- "edge-id":167772162,
+ "edge-id":"10.0.0.2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":167772417,
+ "edge-id":"10.0.1.1",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0001",
}
},
{
- "edge-id":167772418,
+ "edge-id":"10.0.1.2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":167772930,
+ "edge-id":"10.0.3.2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":167772931,
+ "edge-id":"10.0.3.3",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0003",
}
},
{
- "edge-id":167773186,
+ "edge-id":"10.0.4.2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":167773188,
+ "edge-id":"10.0.4.4",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0004",
],
"edges":[
{
- "edge-id":1,
+ "edge-id":"2001:db8::1",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0001",
}
},
{
- "edge-id":2,
+ "edge-id":"2001:db8::2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":65537,
+ "edge-id":"2001:db8:1::1:1",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0001",
}
},
{
- "edge-id":65538,
+ "edge-id":"2001:db8:1::1:2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":196610,
+ "edge-id":"2001:db8:3::3:2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":196611,
+ "edge-id":"2001:db8:3::3:3",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0003",
}
},
{
- "edge-id":196612,
+ "edge-id":"2001:db8:5::3:4",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0004",
]
},
{
- "edge-id":262147,
+ "edge-id":"2001:db8:5::4:3",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0003",
}
},
{
- "edge-id":167772161,
+ "edge-id":"10.0.0.1",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0001",
}
},
{
- "edge-id":167772162,
+ "edge-id":"10.0.0.2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":167772417,
+ "edge-id":"10.0.1.1",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0001",
}
},
{
- "edge-id":167772418,
+ "edge-id":"10.0.1.2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":167772930,
+ "edge-id":"10.0.3.2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":167772931,
+ "edge-id":"10.0.3.3",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0003",
}
},
{
- "edge-id":167773186,
+ "edge-id":"10.0.4.2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":167773188,
+ "edge-id":"10.0.4.4",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0004",
],
"edges":[
{
- "edge-id":1,
+ "edge-id":"2001:db8::1",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0001",
}
},
{
- "edge-id":2,
+ "edge-id":"2001:db8::2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":65537,
+ "edge-id":"2001:db8:1::1:1",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0001",
}
},
{
- "edge-id":65538,
+ "edge-id":"2001:db8:1::1:2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":196610,
+ "edge-id":"2001:db8:3::3:2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":196611,
+ "edge-id":"2001:db8:3::3:3",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0003",
}
},
{
- "edge-id":196612,
+ "edge-id":"2001:db8:5::3:4",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0004",
]
},
{
- "edge-id":262147,
+ "edge-id":"2001:db8:5::4:3",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0003",
}
},
{
- "edge-id":167772161,
+ "edge-id":"10.0.0.1",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0001",
}
},
{
- "edge-id":167772162,
+ "edge-id":"10.0.0.2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":167772417,
+ "edge-id":"10.0.1.1",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0001",
}
},
{
- "edge-id":167772418,
+ "edge-id":"10.0.1.2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":167772930,
+ "edge-id":"10.0.3.2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":167772931,
+ "edge-id":"10.0.3.3",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0003",
}
},
{
- "edge-id":167773186,
+ "edge-id":"10.0.4.2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":167773188,
+ "edge-id":"10.0.4.4",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0004",
],
"edges":[
{
- "edge-id":1,
+ "edge-id":"2001:db8::1",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0001",
}
},
{
- "edge-id":2,
+ "edge-id":"2001:db8::2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":65537,
+ "edge-id":"2001:db8:1::1:1",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0001",
}
},
{
- "edge-id":65538,
+ "edge-id":"2001:db8:1::1:2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":196610,
+ "edge-id":"2001:db8:3::3:2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":196611,
+ "edge-id":"2001:db8:3::3:3",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0003",
}
},
{
- "edge-id":196612,
+ "edge-id":"2001:db8:5::3:4",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0004",
]
},
{
- "edge-id":262147,
+ "edge-id":"2001:db8:5::4:3",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0003",
}
},
{
- "edge-id":167772161,
+ "edge-id":"10.0.0.1",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0001",
}
},
{
- "edge-id":167772162,
+ "edge-id":"10.0.0.2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":167772417,
+ "edge-id":"10.0.1.1",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0001",
}
},
{
- "edge-id":167772418,
+ "edge-id":"10.0.1.2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":167772930,
+ "edge-id":"10.0.3.2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":167772931,
+ "edge-id":"10.0.3.3",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0003",
}
},
{
- "edge-id":167773186,
+ "edge-id":"10.0.4.2",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0002",
}
},
{
- "edge-id":167773188,
+ "edge-id":"10.0.4.4",
"status":"Sync",
"origin":"ISIS_L2",
"advertised-router":"0000.0000.0004",
--- /dev/null
+version: 1
+kinds:
+ - name: frr
+ cmd: |
+ chown frr:frr -R /var/run/frr
+ chown frr:frr -R /var/log/frr
+ /usr/lib/frr/frrinit.sh start
+ tail -F /var/log/frr/frr.log
+ cleanup-cmd: |
+ /usr/lib/frr/frrinit.sh stop
+ volumes:
+ - "./%NAME%:/etc/frr"
+ - "%RUNDIR%/var.log.frr:/var/log/frr"
+ - "%RUNDIR%/var.run.frr:/var/run/frr"
+ cap-add:
+ - SYS_ADMIN
+ - AUDIT_WRITE
+ merge: ["volumes"]
+cli:
+ commands:
+ - name: ""
+ exec: "vtysh -c '{}'"
+ format: "[ROUTER ...] COMMAND"
+ help: "execute vtysh COMMAND on the router[s]"
+ kinds: ["frr"]
+ - name: "vtysh"
+ exec: "/usr/bin/vtysh"
+ format: "vtysh ROUTER [ROUTER ...]"
+ new-window: true
+ kinds: ["frr"]
"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"
}
]
"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"
}
]
"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"
}
]
"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"
}
]
"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"
}
]
"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"
}
]
"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"
}
]
"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"
}
]
"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
}
]
}
"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
}
]
}
"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
}
]
}
"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
}
]
}
"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
}
]
}
"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
}
]
}
"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
}
]
}
"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
}
]
}
"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
}
]
}
"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
}
]
}
# 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,
#
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,
#
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(
#
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)],
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
)
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,
output,
)
else:
- logger.info(
+ logger.debug(
"Configuration on router %s after reset:\n%s", rname, output
)
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,
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
)
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
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:
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(
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(
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:
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)
interface_name, _vrf
)
- logger.info(
+ logger.debug(
"[DUT: %s]: Running" " kernel cmd [%s]",
c_router,
cmd,
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:
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)
)
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
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,
# 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
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(
)
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
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))
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(
#
# 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,
+)
+++ /dev/null
-# -*- 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)
#
# 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"]
+ if "logger" in kwargs:
+ nkwargs["logger"] = kwargs["logger"]
- 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
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, pytestconfig=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
# 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, pytestconfig=pytestconfig
+ )
+
+ # 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.cfgopt.get_option_list("--shell")
+ if "all" in shellopt or "." in shellopt:
+ self.run_in_window("bash")
+
+ # This is expected by newer munet CLI code
+ self.config_dirname = ""
+ self.config = {}
self.logger.debug("%s: Creating", self)
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)
def start(self):
"""Start the micronet topology."""
+ pcapopt = self.cfgopt.get_option_list("--pcap")
+ if "all" in pcapopt:
+ pcapopt = self.switches.keys()
+ for pcap in pcapopt:
+ if ":" in pcap:
+ host, intf = pcap.split(":")
+ pcap = f"{host}-{intf}"
+ host = self.hosts[host]
+ else:
+ host = self
+ intf = pcap
+ pcapfile = f"{self.rundir}/capture-{pcap}.pcap"
+ host.run_in_window(
+ f"tshark -s 9200 -i {intf} -P -w {pcapfile}",
+ background=True,
+ title=f"cap:{pcap}",
+ )
+
self.logger.debug("%s: Starting (no-op).", self)
def stop(self):
Mininet.g_mnet_inst = None
def cli(self):
- cli(self)
+ cli.cli(self)
"ospf": {
"neighbors": {
"r1": {
- "state": "Full",
+ "nbrState": "Full",
"role": "DR"
},
"r2": {
- "state": "Full",
+ "nbrState": "Full",
"role": "DROther"
},
"r3": {
- "state": "Full",
+ "nbrState": "Full",
"role": "DROther"
}
}
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:
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
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)
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()
* After running stop Mininet with: tgen.stop_topology()
"""
+import configparser
import grp
import inspect
import json
import platform
import pwd
import re
+import shlex
import subprocess
import sys
from collections import OrderedDict
-if sys.version_info[0] > 2:
- import configparser
-else:
- import ConfigParser as configparser
-
import lib.topolog as topolog
from lib.micronet import Commander
from lib.micronet_compat import Mininet
from lib.topolog import logger
-from lib.topotest import g_extra_config
+from munet.testing.util import pause_test
from lib import topotest
self._load_config()
# Create new log directory
- self.logdir = topotest.get_logs_path(g_extra_config["rundir"])
+ self.logdir = topotest.get_logs_path(topotest.g_pytest_config.option.rundir)
subprocess.check_call(
"mkdir -p {0} && chmod 1777 {0}".format(self.logdir), shell=True
)
# Mininet(Micronet) to build the actual topology.
assert not inspect.isclass(topodef)
- self.net = Mininet(controller=None)
+ self.net = Mininet(rundir=self.logdir, pytestconfig=topotest.g_pytest_config)
+
+ # 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
self.add_topology_from_dict(topodef)
def add_topology_from_dict(self, topodef):
-
keylist = (
topodef.keys()
if isinstance(topodef, OrderedDict)
first is a simple kill with no sleep, the second will sleep if not
killed and try with a different signal.
"""
+ pause = bool(self.net.cfgopt.get_option("--pause-at-end"))
+ pause = pause or bool(self.net.cfgopt.get_option("--pause"))
+ if pause:
+ try:
+ pause_test("Before MUNET delete")
+ except KeyboardInterrupt:
+ print("^C...continuing")
+ except Exception as error:
+ self.logger.error("\n...continuing after error: %s", error)
+
logger.info("stopping topology: {}".format(self.modname))
+
errors = ""
for gear in self.gears.values():
errors += gear.stop()
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:
"""
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()
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:
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):
"conf t",
"log file {}.log debug".format(daemon),
"log commands",
- "log timestamp precision 3",
+ "log timestamp precision 6",
]
),
daemon=daemon,
"conf t",
"log file {}.log debug".format(daemon),
"log commands",
- "log timestamp precision 3",
+ "log timestamp precision 6",
]
),
daemon=daemon,
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
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)
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):
* `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)
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
(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):
# Diagnostic function
#
+
# Disable linter branch warning. It is expected to have these here.
# pylint: disable=R0912
def diagnose_env_linux(rundir):
# Network Device Education Foundation, Inc. ("NetDEF")
#
+import configparser
import difflib
import errno
import functools
import glob
import json
import os
-import pdb
import platform
import re
import resource
import sys
import tempfile
import time
+from collections.abc import Mapping
from copy import deepcopy
import lib.topolog as topolog
+from lib.micronet_compat import Node
from lib.topolog import logger
-
-if sys.version_info[0] > 2:
- import configparser
- from collections.abc import Mapping
-else:
- import ConfigParser as configparser
- from collections import Mapping
+from munet.base import Timeout
from lib import micronet
-from lib.micronet_compat import Node
-g_extra_config = {}
+g_pytest_config = None
def get_logs_path(rundir):
count, wait
)
- logger.info(
+ logger.debug(
"'{}' polling started (interval {} secs, maximum {} tries)".format(
func_name, wait, count
)
continue
end_time = time.time()
- logger.info(
+ logger.debug(
"'{}' succeeded after {:.2f} seconds".format(
func_name, end_time - start_time
)
count, wait
)
- logger.info(
+ logger.debug(
"'{}' polling started (interval {} secs, maximum wait {} secs)".format(
func_name, wait, int(wait * count)
)
continue
end_time = time.time()
- logger.info(
+ logger.debug(
"'{}' succeeded after {:.2f} seconds".format(
func_name, end_time - start_time
)
)
-def pid_exists(pid):
- "Check whether pid exists in the current process table."
-
- if pid <= 0:
- return False
- try:
- os.waitpid(pid, os.WNOHANG)
- except:
- pass
- try:
- os.kill(pid, 0)
- except OSError as err:
- if err.errno == errno.ESRCH:
- # ESRCH == No such process
- return False
- elif err.errno == errno.EPERM:
- # EPERM clearly means there's a process to deny access to
- return True
- else:
- # According to "man 2 kill" possible error values are
- # (EINVAL, EPERM, ESRCH)
- raise
- else:
- return True
-
-
def get_textdiff(text1, text2, title1="", title2="", **opts):
"Returns empty string if same or formatted diff"
# No Address Sanitizer Error in Output. Now check for AddressSanitizer daemon file
if logdir:
- filepattern = logdir + "/" + router + "/" + component + ".asan.*"
+ filepattern = logdir + "/" + router + ".asan." + component + ".*"
logger.debug(
"Log check for %s on %s, pattern %s\n" % (component, router, filepattern)
)
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))
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))
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(
def fix_netns_limits(ns):
-
# Maximum read and write socket buffer sizes
- sysctl_atleast(ns, "net.ipv4.tcp_rmem", [10 * 1024, 87380, 16 * 2 ** 20])
- sysctl_atleast(ns, "net.ipv4.tcp_wmem", [10 * 1024, 87380, 16 * 2 ** 20])
+ sysctl_atleast(ns, "net.ipv4.tcp_rmem", [10 * 1024, 87380, 16 * 2**20])
+ sysctl_atleast(ns, "net.ipv4.tcp_wmem", [10 * 1024, 87380, 16 * 2**20])
sysctl_assure(ns, "net.ipv4.conf.all.rp_filter", 0)
sysctl_assure(ns, "net.ipv4.conf.default.rp_filter", 0)
sysctl_atleast(None, "net.core.netdev_max_backlog", 4 * 1024)
# Maximum read and write socket buffer sizes
- sysctl_atleast(None, "net.core.rmem_max", 16 * 2 ** 20)
- sysctl_atleast(None, "net.core.wmem_max", 16 * 2 ** 20)
+ sysctl_atleast(None, "net.core.rmem_max", 16 * 2**20)
+ sysctl_atleast(None, "net.core.wmem_max", 16 * 2**20)
# Garbage Collection Settings for ARP and Neighbors
sysctl_atleast(None, "net.ipv4.neigh.default.gc_thresh2", 4 * 1024)
def setup_node_tmpdir(logdir, name):
# Cleanup old log, valgrind, and core files.
subprocess.check_call(
- "rm -rf {0}/{1}.valgrind.* {1}.*.asan {0}/{1}/".format(logdir, name), shell=True
+ "rm -rf {0}/{1}.valgrind.* {0}/{1}.asan.* {0}/{1}/".format(logdir, name),
+ shell=True,
)
# Setup the per node directory.
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.
self.config_defaults = configparser.ConfigParser(
os.path.join(os.path.dirname(os.path.realpath(__file__)), "../pytest.ini")
)
+ self.perf_daemons = {}
+
# If this topology is using old API and doesn't have logdir
# specified, then attempt to generate an unique logdir.
self.logdir = params.get("logdir")
if self.logdir is None:
- self.logdir = get_logs_path(g_extra_config["rundir"])
+ self.logdir = get_logs_path(g_pytest_config.getoption("--rundir"))
if not params.get("logger"):
# If logger is present topogen has already set this up
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
# 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")
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)
)
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()
+ # breakpoint()
# assert False, "can't remove IPs %s" % str(ex)
def checkCapability(self, daemon, param):
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)
)
if (daemon == "zebra") and (self.daemons["mgmtd"] == 0):
# Add mgmtd with zebra - if it exists
- try:
- mgmtd_path = os.path.join(self.daemondir, "mgmtd")
- except:
- pdb.set_trace()
+ mgmtd_path = os.path.join(self.daemondir, "mgmtd")
if os.path.isfile(mgmtd_path):
self.daemons["mgmtd"] = 1
self.daemons_options["mgmtd"] = ""
if (daemon == "zebra") and (self.daemons["staticd"] == 0):
# Add staticd with zebra - if it exists
- try:
- staticd_path = os.path.join(self.daemondir, "staticd")
- except:
- pdb.set_trace()
-
+ staticd_path = os.path.join(self.daemondir, "staticd")
if os.path.isfile(staticd_path):
self.daemons["staticd"] = 1
self.daemons_options["staticd"] = ""
# 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):
# used
self.cmd("echo 100000 > /proc/sys/net/mpls/platform_labels")
- shell_routers = g_extra_config["shell"]
- if "all" in shell_routers or self.name in shell_routers:
+ if g_pytest_config.name_in_option_list(self.name, "--shell"):
self.run_in_window(os.getenv("SHELL", "bash"), title="sh-%s" % self.name)
if self.daemons["eigrpd"] == 1:
status = self.startRouterDaemons(tgen=tgen)
- vtysh_routers = g_extra_config["vtysh"]
- if "all" in vtysh_routers or self.name in vtysh_routers:
+ if g_pytest_config.name_in_option_list(self.name, "--vtysh"):
self.run_in_window("vtysh", title="vt-%s" % self.name)
if self.unified_config:
def startRouterDaemons(self, daemons=None, tgen=None):
"Starts FRR daemons for this router."
- asan_abort = g_extra_config["asan_abort"]
- gdb_breakpoints = g_extra_config["gdb_breakpoints"]
- gdb_daemons = g_extra_config["gdb_daemons"]
- gdb_routers = g_extra_config["gdb_routers"]
- valgrind_extra = g_extra_config["valgrind_extra"]
- valgrind_memleaks = g_extra_config["valgrind_memleaks"]
- strace_daemons = g_extra_config["strace_daemons"]
+ asan_abort = bool(g_pytest_config.option.asan_abort)
+ gdb_breakpoints = g_pytest_config.get_option_list("--gdb-breakpoints")
+ gdb_daemons = g_pytest_config.get_option_list("--gdb-daemons")
+ gdb_routers = g_pytest_config.get_option_list("--gdb-routers")
+ valgrind_extra = bool(g_pytest_config.option.valgrind_extra)
+ valgrind_memleaks = bool(g_pytest_config.option.valgrind_memleaks)
+ strace_daemons = g_pytest_config.get_option_list("--strace-daemons")
# Get global bundle data
if not self.path_exists("/etc/frr/support_bundle_commands.conf"):
self.reportCores = True
# XXX: glue code forward ported from removed function.
- if self.version == None:
+ if self.version is None:
self.version = self.cmd(
os.path.join(self.daemondir, "bgpd") + " -v"
).split()[2]
logger.info("{}: running version: {}".format(self.name, self.version))
+
+ perfds = {}
+ perf_options = g_pytest_config.get_option("--perf-options", "-g")
+ for perf in g_pytest_config.get_option("--perf", []):
+ if "," in perf:
+ daemon, routers = perf.split(",", 1)
+ perfds[daemon] = routers.split(",")
+ else:
+ daemon = perf
+ perfds[daemon] = ["all"]
+
+ logd_options = {}
+ for logd in g_pytest_config.get_option("--logd", []):
+ if "," in logd:
+ daemon, routers = logd.split(",", 1)
+ logd_options[daemon] = routers.split(",")
+ else:
+ daemon = logd
+ logd_options[daemon] = ["all"]
+
# If `daemons` was specified then some upper API called us with
# specific daemons, otherwise just use our own configuration.
daemons_list = []
if self.daemons[daemon] == 1:
daemons_list.append(daemon)
+ tail_log_files = []
+ check_daemon_files = []
+
def start_daemon(daemon, extra_opts=None):
daemon_opts = self.daemons_options.get(daemon, "")
+
+ # get pid and vty filenames and remove the files
+ m = re.match(r"(.* |^)-n (\d+)( ?.*|$)", daemon_opts)
+ dfname = daemon if not m else "{}-{}".format(daemon, m.group(2))
+ runbase = "/var/run/{}/{}".format(self.routertype, dfname)
+ # If this is a new system bring-up remove the pid/vty files, otherwise
+ # do not since apparently presence of the pidfile impacts BGP GR
+ self.cmd_status("rm -f {0}.pid {0}.vty".format(runbase))
+
rediropt = " > {0}.out 2> {0}.err".format(daemon)
if daemon == "snmpd":
binary = "/usr/sbin/snmpd"
cmdenv = ""
cmdopt = "{} -C -c /etc/frr/snmpd.conf -p ".format(
daemon_opts
- ) + "/var/run/{}/snmpd.pid -x /etc/frr/agentx".format(self.routertype)
+ ) + "{}.pid -x /etc/frr/agentx".format(runbase)
+ # check_daemon_files.append(runbase + ".pid")
else:
binary = os.path.join(self.daemondir, daemon)
+ check_daemon_files.extend([runbase + ".pid", runbase + ".vty"])
cmdenv = "ASAN_OPTIONS="
if asan_abort:
- cmdenv = "abort_on_error=1:"
- cmdenv += "log_path={0}/{1}.{2}.asan ".format(
+ cmdenv += "abort_on_error=1:"
+ cmdenv += "log_path={0}/{1}.asan.{2} ".format(
self.logdir, self.name, daemon
)
daemon, self.logdir, self.name
)
- cmdopt = "{} --command-log-always --log file:{}.log --log-level debug".format(
- daemon_opts, daemon
- )
+ cmdopt = "{} --command-log-always ".format(daemon_opts)
+ cmdopt += "--log file:{}.log --log-level debug".format(daemon)
+
+ if daemon in logd_options:
+ logdopt = logd_options[daemon]
+ if "all" in logdopt or self.name in logdopt:
+ tail_log_files.append(
+ "{}/{}/{}.log".format(self.logdir, self.name, daemon)
+ )
if extra_opts:
cmdopt += " " + extra_opts
logger.info(
"%s: %s %s launched in gdb window", self, self.routertype, daemon
)
+ elif daemon in perfds and (self.name in perfds[daemon] or "all" in perfds[daemon]):
+ cmdopt += rediropt
+ cmd = " ".join(["perf record {} --".format(perf_options), binary, cmdopt])
+ p = self.popen(cmd)
+ self.perf_daemons[daemon] = p
+ if p.poll() and p.returncode:
+ self.logger.error(
+ '%s: Failed to launch "%s" (%s) with perf using: %s',
+ self,
+ daemon,
+ p.returncode,
+ cmd,
+ )
+ else:
+ logger.debug(
+ "%s: %s %s started with perf", self, self.routertype, daemon
+ )
else:
if daemon != "snmpd":
cmdopt += " -d "
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:
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:
start_daemon(daemon)
# Check if daemons are running.
- rundaemons = self.cmd("ls -1 /var/run/%s/*.pid" % self.routertype)
- if re.search(r"No such file or directory", rundaemons):
- return "Daemons are not running"
+ wait_time = 30 if (gdb_routers or gdb_daemons) else 10
+ timeout = Timeout(wait_time)
+ for remaining in timeout:
+ if not check_daemon_files:
+ break
+ check = check_daemon_files[0]
+ if self.path_exists(check):
+ check_daemon_files.pop(0)
+ continue
+ self.logger.debug("Waiting {}s for {} to appear".format(remaining, check))
+ time.sleep(0.5)
+
+ if check_daemon_files:
+ assert False, "Timeout({}) waiting for {} to appear on {}".format(
+ wait_time, check_daemon_files[0], self.name
+ )
# Update the permissions on the log files
self.cmd("chown frr:frr -R {}/{}".format(self.logdir, self.name))
self.cmd("chmod ug+rwX,o+r -R {}/{}".format(self.logdir, self.name))
+ if "frr" in logd_options:
+ logdopt = logd_options["frr"]
+ if "all" in logdopt or self.name in logdopt:
+ tail_log_files.append("{}/{}/frr.log".format(self.logdir, self.name))
+
+ for tailf in tail_log_files:
+ self.run_in_window("tail -f " + tailf, title=tailf, background=True)
+
return ""
+ def pid_exists(self, pid):
+ if pid <= 0:
+ return False
+ try:
+ # If we are not using PID namespaces then we will be a parent of the pid,
+ # otherwise the init process of the PID namespace will have reaped the proc.
+ os.waitpid(pid, os.WNOHANG)
+ except Exception:
+ pass
+
+ rc, o, e = self.cmd_status("kill -0 " + str(pid), warn=False)
+ return rc == 0 or "No such process" not in e
+
def killRouterDaemons(
self, daemons, wait=True, assertOnError=True, minErrorVersion="5.1"
):
if re.search(r"%s" % daemon, d):
daemonpidfile = d.rstrip()
daemonpid = self.cmd("cat %s" % daemonpidfile).rstrip()
- if daemonpid.isdigit() and pid_exists(int(daemonpid)):
- logger.info(
+ if daemonpid.isdigit() and self.pid_exists(int(daemonpid)):
+ logger.debug(
"{}: killing {}".format(
self.name,
os.path.basename(daemonpidfile.rsplit(".", 1)[0]),
)
)
- os.kill(int(daemonpid), signal.SIGKILL)
- if pid_exists(int(daemonpid)):
+ self.cmd_status("kill -KILL {}".format(daemonpid))
+ if self.pid_exists(int(daemonpid)):
numRunning += 1
while wait and numRunning > 0:
sleep(
for d in dmns[:-1]:
if re.search(r"%s" % daemon, d):
daemonpid = self.cmd("cat %s" % d.rstrip()).rstrip()
- if daemonpid.isdigit() and pid_exists(
+ if daemonpid.isdigit() and self.pid_exists(
int(daemonpid)
):
logger.info(
),
)
)
- os.kill(int(daemonpid), signal.SIGKILL)
- if daemonpid.isdigit() and not pid_exists(
+ self.cmd_status(
+ "kill -KILL {}".format(daemonpid)
+ )
+ if daemonpid.isdigit() and not self.pid_exists(
int(daemonpid)
):
numRunning -= 1
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:
]
}
}
- 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)
]
}
}
- 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)
assert (
result is True
), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
-
+
step("Mgmt delete config")
raw_config = {
"r1": {
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)
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)
--- /dev/null
+{
+ "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
--- /dev/null
+#!/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))
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 (
# 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")
tgen = get_topogen()
- app_helper.cleanup()
+ # Clean up socat
+ kill_socat(tgen)
# Stop toplogy and Remove tmp files
tgen.stop_topology()
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 (
# 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")
tgen = get_topogen()
- app_helper.cleanup()
+ # Clean up socat
+ kill_socat(tgen)
# Stop toplogy and Remove tmp files
tgen.stop_topology()
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()
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()
--- /dev/null
+{"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":"*"}]}]}}
--- /dev/null
+{
+ "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":"*"
+ }
+ ]
+ }
+}
+
--- /dev/null
+{"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":"*"}]}]}}
--- /dev/null
+{
+ "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":"*"
+ }
+ ]
+ }
+}
--- /dev/null
+{"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":"*"}]}]}}
--- /dev/null
+{
+ "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":"*"
+ }
+ ]
+ }
+ }
+}
+
--- /dev/null
+{
+ "l1-i1-eth1":{
+ "name":"l1-i1-eth1",
+ "225.1.1.4":{
+ "group":"225.1.1.4",
+ "sources":[
+ {
+ "source":"*",
+ "timer":"*",
+ "forwarded":true,
+ "uptime":"*"
+ }
+ ]
+ }
+ }
+}
import datetime
import pytest
from time import sleep
+import json
+import functools
pytestmark = pytest.mark.pimd
# 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,
)
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"
--- /dev/null
+# -*- 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",
+]
--- /dev/null
+# -*- 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)
--- /dev/null
+# -*- 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 config as munet_config
+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 = unet.rootcmd.get_exec_path("pgrep")
+ # a sing fork was done
+ child_pid = unet.rootcmd.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_nostatus_nsonly("ls -l /proc/1/ns")
+ else:
+ o = self.cmd_nostatus_nsonly("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,
+ pytestconfig=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
+ elif not pid:
+ nsflags = (
+ 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)
+ else:
+ # XXX user
+ nsflags = (
+ # XXX Backing up PID namespace just doesn't work.
+ # 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
+
+ self.cfgopt = munet_config.ConfigOptionsProxy(pytestconfig)
+
+ 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
--- /dev/null
+# -*- 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)
--- /dev/null
+# -*- 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
+
+
+try:
+ from . import linux
+ from .config import list_to_dict_with_key
+except ImportError:
+ # 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
+
+ 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."""
+ assert unet is not None
+ 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
+
+ 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()
+
+ class EncodingFile:
+ """Wrap a writer to encode in utf-8."""
+
+ def __init__(self, writer):
+ self.writer = writer
+
+ def write(self, x):
+ self.writer.write(x.encode("utf-8"))
+
+ def flush(self):
+ self.writer.flush()
+
+ if not await doline(unet, line, EncodingFile(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,
+ )
+ )
--- /dev/null
+# -*- 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."""
+
+ class Namespace:
+ """A namespace defined by a dictionary of values."""
+
+ def __init__(self, args):
+ self.args = args
+
+ def __getattr__(self, attr):
+ return self.args[attr] if attr in self.args else None
+
+ def __init__(self, args):
+ self.args = vars(args)
+ self.option = PytestConfig.Namespace(self.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
--- /dev/null
+# -*- 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
+
+
+def cli_opt_list(option_list):
+ if not option_list:
+ return []
+ if isinstance(option_list, str):
+ return [x for x in option_list.split(",") if x]
+ return [x for x in option_list if x]
+
+
+def name_in_cli_opt_str(name, option_list):
+ ol = cli_opt_list(option_list)
+ return name in ol or "all" in ol
+
+
+class ConfigOptionsProxy:
+ """Proxy options object to fill in for any missing pytest config."""
+
+ class DefNoneObject:
+ """An object that returns None for any attribute access."""
+
+ def __getattr__(self, attr):
+ return None
+
+ def __init__(self, pytestconfig=None):
+ if isinstance(pytestconfig, ConfigOptionsProxy):
+ self.config = pytestconfig.config
+ self.option = self.config.option
+ else:
+ self.config = pytestconfig
+ if self.config:
+ self.option = self.config.option
+ else:
+ self.option = ConfigOptionsProxy.DefNoneObject()
+
+ def getoption(self, opt, default=None):
+ if not self.config:
+ return default
+
+ try:
+ value = self.config.getoption(opt)
+ return value if value is not None else default
+ except ValueError:
+ return default
+
+ def get_option(self, opt, default=None):
+ return self.getoption(opt, default)
+
+ def get_option_list(self, opt):
+ value = self.get_option(opt, "")
+ return cli_opt_list(value)
+
+ def name_in_option_list(self, name, opt):
+ optlist = self.get_option_list(opt)
+ return "all" in optlist or name in optlist
--- /dev/null
+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?
--- /dev/null
+# -*- 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)
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+# -*- 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)
--- /dev/null
+# -*- 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)
--- /dev/null
+{
+ "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
--- /dev/null
+# -*- 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()
--- /dev/null
+# -*- 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
--- /dev/null
+# -*- 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()
--- /dev/null
+#!/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()
+
+ # Reap children as init process
+ vdebug("installing local handler for SIGCHLD")
+ signal.signal(signal.SIGCHLD, sig_sigchld)
+
+ while True:
+ logging.info("init: 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()
--- /dev/null
+# -*- 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:
+ return
+
+ outopt = self.unet.cfgopt.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.cfgopt.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:
+ return
+
+ gdbcmd = self.config.get("gdb-cmd")
+ shellopt = self.unet.cfgopt.getoption("--gdb", "")
+ should_gdb = gdbcmd and (shellopt == "all" or self.name in shellopt.split(","))
+ use_emacs = self.unet.cfgopt.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.cfgopt.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.cfgopt.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.cfgopt.getoption("--shell")
+ shellopt = shellopt if shellopt 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.cfgopt.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,
+ 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 = ""
+
+ # 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.cfgopt.getoption("--shell")
+ shellopt = shellopt if shellopt 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()
+ ]
+
+ pcapopt = self.cfgopt.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.cfgopt.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)
+
+ pause = bool(self.cfgopt.getoption("--pause-at-end"))
+ pause = pause or bool(self.cfgopt.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}
--- /dev/null
+# -*- 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.insert(0, 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))
--- /dev/null
+"""Sub-package supporting munet use in pytest."""
--- /dev/null
+# -*- 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
--- /dev/null
+# -*- 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}'")
--- /dev/null
+# -*- 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
-debug ospf6 lsa all
-debug ospf6 message all
-debug ospf6 route all
-debug ospf6 spf time
-debug ospf6 spf database
-debug ospf6 zebra send
-debug ospf6 zebra recv
-
-debug ospf6 lsa router
-debug ospf6 lsa router originate
-debug ospf6 lsa router examine
-debug ospf6 lsa router flooding
-debug ospf6 lsa as-external
-debug ospf6 lsa as-external originate
-debug ospf6 lsa as-external examine
-debug ospf6 lsa as-external flooding
-debug ospf6 lsa intra-prefix
-debug ospf6 lsa intra-prefix originate
-debug ospf6 lsa intra-prefix examine
-debug ospf6 lsa intra-prefix flooding
-debug ospf6 border-routers
-debug ospf6 zebra
-debug ospf6 interface
-debug ospf6 neighbor
-debug ospf6 flooding
-debug ospf6 gr helper
-debug ospf6 spf process
-debug ospf6 route intra-area
-debug ospf6 route inter-area
-debug ospf6 abr
-debug ospf6 asbr
-debug ospf6 nssa
+!debug ospf6 lsa all
+!debug ospf6 message all
+!debug ospf6 route all
+!debug ospf6 spf time
+!debug ospf6 spf database
+!debug ospf6 zebra send
+!debug ospf6 zebra recv
+!
+!debug ospf6 lsa router
+!debug ospf6 lsa router originate
+!debug ospf6 lsa router examine
+!debug ospf6 lsa router flooding
+!debug ospf6 lsa as-external
+!debug ospf6 lsa as-external originate
+!debug ospf6 lsa as-external examine
+!debug ospf6 lsa as-external flooding
+!debug ospf6 lsa intra-prefix
+!debug ospf6 lsa intra-prefix originate
+!debug ospf6 lsa intra-prefix examine
+!debug ospf6 lsa intra-prefix flooding
+!debug ospf6 border-routers
+!debug ospf6 zebra
+!debug ospf6 interface
+!debug ospf6 neighbor
+!debug ospf6 flooding
+!debug ospf6 gr helper
+!debug ospf6 spf process
+!debug ospf6 route intra-area
+!debug ospf6 route inter-area
+!debug ospf6 abr
+!debug ospf6 asbr
+!debug ospf6 nssa
!
interface r1-eth0
ipv6 ospf6 area 0
"ospf": {
"area": "0.0.0.3",
"hello_interval": 1,
- "dead_interval": 4,
+ "dead_interval": 10,
"priority": 98
}
},
"ospf": {
"area": "0.0.0.3",
"hello_interval": 1,
- "dead_interval": 4,
+ "dead_interval": 10,
"priority": 99
}
},
"ospf": {
"area": "0.0.0.3",
"hello_interval": 1,
- "dead_interval": 4,
+ "dead_interval": 10,
"priority": 0
}
},
"ospf": {
"area": "0.0.0.3",
"hello_interval": 1,
- "dead_interval": 4,
+ "dead_interval": 10,
"priority": 0
}
}
}
}
}
-}
\ No newline at end of file
+}
pytest.skip(tgen.errors)
# Api call verify whether OSPF is converged
ospf_covergence = verify_ospf_neighbor(tgen, topo)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "setup_module :Failed \n Error {}".format(
ospf_covergence
)
result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name)
- step(
- "Configure External Route summary in R0 to summarise 5" " routes to one route."
- )
+ step("Configure External Route summary in R0 to summarise 5 routes to one route.")
ospf_summ_r1 = {
"r0": {
"ospf": {
result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name)
step("Verify that show ip ospf summary should show the summaries.")
input_dict = {
result = verify_ospf_summary(tgen, topo, dut, input_dict)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name)
step("Change the summary address mask to lower match (ex - 16 to 8)")
ospf_summ_r1 = {
result = verify_ospf_summary(tgen, topo, dut, input_dict)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name)
step(
"Verify that external routes(static / connected) are summarised"
result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name)
step("Change the summary address mask to higher match (ex - 8 to 24)")
ospf_summ_r1 = {
result = verify_ospf_summary(tgen, topo, dut, input_dict)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name)
step(
"Verify that external routes(static / connected) are summarised"
result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name)
step(" Un configure one of the summary address.")
ospf_summ_r1 = {
result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name)
ospf_summ_r1 = {
"r0": {
result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name)
write_test_footer(tc_name)
result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name)
- step(
- "Configure External Route summary in R0 to summarise 5" " routes to one route."
- )
+ step("Configure External Route summary in R0 to summarise 5 routes to one route.")
ospf_summ_r1 = {
"r0": {
result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name)
step("Verify that show ip ospf summary should show the summaries.")
input_dict = {
result = verify_ospf_summary(tgen, topo, dut, input_dict)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name)
- step("Verify that originally advertised routes are withdraw from there" " peer.")
+ step("Verify that originally advertised routes are withdraw from there peer.")
input_dict = {
"r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]}
}
dut = "r1"
result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
- assert (
- result is not True
- ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
- tc_name, result
+ assert result is not True, (
+ "Testcase {} : Failed\n Expected: Routes should not be present in OSPF RIB \n Error: "
+ "Routes still present in OSPF RIB {}".format(tc_name, result)
)
result = verify_rib(
tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False
)
- assert (
- result is not True
- ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+ assert result is not True, (
+ "Testcase {} : Failed \n Expected: Routes should not be present in RIB\n"
+ "Error: Routes still present in RIB".format(tc_name)
+ )
step(
"Configure route map and & rule to permit configured summary address,"
result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name)
input_dict = {
SUMMARY["ipv4"][0]: {
result = verify_ospf_summary(tgen, topo, dut, input_dict)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name)
write_test_footer(tc_name)
result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name)
step(
"Configure External Route summary in R0 to summarise 5"
result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name)
step("Verify that show ip ospf summary should show the summaries.")
input_dict = {
result = verify_ospf_summary(tgen, topo, dut, input_dict)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name)
- step("Verify that originally advertised routes are withdraw from there" " peer.")
+ step("Verify that originally advertised routes are withdraw from there peer.")
input_dict = {
"r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]}
}
dut = "r1"
result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
- assert (
- result is not True
- ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
- tc_name, result
+ assert result is not True, (
+ "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB \n Error: "
+ "Routes still present in OSPF RIB {}".format(tc_name, result)
)
result = verify_rib(
tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False
)
- assert (
- result is not True
- ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+ assert result is not True, (
+ "Testcase {} : Failed \n Expected: Routes should not be present in RIB"
+ "Error: Routes still present in RIB".format(tc_name)
+ )
step("Delete the configured summary")
ospf_summ_r1 = {
step("Verify that summary lsa is withdrawn from R1 and deleted from R0.")
dut = "r1"
result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
- assert (
- result is not True
- ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
- tc_name, result
+ assert result is not True, (
+ "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB \n Error: "
+ "Routes still present in OSPF RIB {}".format(tc_name, result)
)
result = verify_rib(
tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False
)
- assert (
- result is not True
- ), "Testcase {} : Failed" "Error: Summary Route still present in RIB".format(
- tc_name
+ assert result is not True, (
+ "Testcase {} : Failed \n Expected: Summary Route should not present in RIB"
+ "Error: Summary Route still present in RIB".format(tc_name)
)
step("show ip ospf summary should not have any summary address.")
}
dut = "r0"
result = verify_ospf_summary(tgen, topo, dut, input_dict, expected=False)
- assert (
- result is not True
- ), "Testcase {} : Failed" "Error: Summary still present in DB".format(tc_name)
+ assert result is not True, (
+ "Testcase {} : Failed \n Expected: Summary Route should not present in OSPF DB"
+ "Error: Summary still present in DB".format(tc_name)
+ )
dut = "r1"
step("All 5 routes are advertised after deletion of configured summary.")
result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name)
step("configure the summary again and delete static routes .")
ospf_summ_r1 = {
result = verify_ospf_summary(tgen, topo, dut, input_dict)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name)
input_dict = {
"r0": {
dut = "r1"
result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False)
- assert (
- result is not True
- ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
- tc_name, result
+ assert result is not True, (
+ "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB \n Error: "
+ "Routes still present in OSPF RIB {}".format(tc_name, result)
)
result = verify_rib(
tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False
)
- assert (
- result is not True
- ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+ assert result is not True, (
+ "Testcase {} : Failed \n Expected: Routes should not be present in RIB\n"
+ "Error: Routes still present in RIB".format(tc_name)
+ )
step("Add back static routes.")
input_dict_static_rtes = {
dut = "r1"
result = verify_ospf_rib(tgen, dut, input_dict_static_rtes, expected=False)
- assert (
- result is not True
- ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
- tc_name, result
+ assert result is not True, (
+ "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB \n Error: "
+ "Routes still present in OSPF RIB {}".format(tc_name, result)
)
result = verify_rib(
tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol, expected=False
)
- assert (
- result is not True
- ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+ assert result is not True, (
+ "Testcase {} : Failed \n Expected: Routes should not be present in RIB"
+ "Error: Routes still present in RIB".format(tc_name)
+ )
input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}}
dut = "r1"
result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name)
step("Verify that show ip ospf summary should show configure summaries.")
result = verify_ospf_summary(tgen, topo, dut, input_dict)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name)
step("Configure new static route which is matching configured summary.")
input_dict_static_rtes = {
result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name)
step("Shut one of the interface")
intf = topo["routers"]["r0"]["links"]["r3-link0"]["interface"]
result = verify_ospf_summary(tgen, topo, dut, input_dict)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name)
input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}}
result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name)
ospf_summ_r1 = {
"r0": {
dut = "r1"
result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False)
- assert (
- result is not True
- ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
- tc_name, result
+ assert result is not True, (
+ "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB. \n Error: "
+ "Routes still present in OSPF RIB {}".format(tc_name, result)
)
result = verify_rib(
tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False
)
- assert (
- result is not True
- ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+ assert result is not True, (
+ "Testcase {} : Failed \n Expected: Routes should not be present in RIB"
+ "Error: Routes still present in RIB".format(tc_name)
+ )
ospf_summ_r1 = {
"r0": {
result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name)
- step(
- "Configure External Route summary in R0 to summarise 5" " routes to one route."
- )
+ step("Configure External Route summary in R0 to summarise 5 routes to one route.")
ospf_summ_r1 = {
"r0": {
"ospf": {
result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name)
step("Verify that show ip ospf summary should show the summaries with tag.")
input_dict = {
result = verify_ospf_summary(tgen, topo, dut, input_dict)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name)
step("Delete the configured summary")
ospf_summ_r1 = {
step("Verify that summary lsa is withdrawn from R1 and deleted from R0.")
dut = "r1"
result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False)
- assert (
- result is not True
- ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
- tc_name, result
+ assert result is not True, (
+ "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB \n Error: "
+ "Routes still present in OSPF RIB {}".format(tc_name, result)
)
result = verify_rib(
tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False
)
- assert (
- result is not True
- ), "Testcase {} : Failed" "Error: Summary Route still present in RIB".format(
- tc_name
+ assert result is not True, (
+ "Testcase {} : Failed \n Expected: Summary Routes should not be present in RIB. \n"
+ "Error: Summary Route still present in RIB".format(tc_name)
)
step("show ip ospf summary should not have any summary address.")
result = verify_ospf_summary(tgen, topo, dut, input_dict, expected=False)
assert (
result is not True
- ), "Testcase {} : Failed" "Error: Summary still present in DB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Summary still present in DB".format(tc_name)
step("Configure Min tag value")
ospf_summ_r1 = {
result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name)
step("Verify that show ip ospf summary should show the summaries with tag.")
input_dict = {
result = verify_ospf_summary(tgen, topo, dut, input_dict)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name)
step("Configure Max Tag Value")
ospf_summ_r1 = {
result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name)
step(
"Verify that boundary values tags are used for summary route"
result = verify_ospf_summary(tgen, topo, dut, input_dict)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name)
step("configure new static route with different tag.")
input_dict_static_rtes_11 = {
dut = "r1"
result = verify_ospf_rib(tgen, dut, input_dict_summary, tag="88888", expected=False)
- assert (
- result is not True
- ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
- tc_name, result
+ assert result is not True, (
+ "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB \n Error: "
+ "Routes still present in OSPF RIB {}".format(tc_name, result)
)
result = verify_rib(
tag="88888",
expected=False,
)
- assert (
- result is not True
- ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+ assert result is not True, (
+ "Testcase {} : Failed \n Expected: Routes should not be present in RIB\n"
+ "Error: Routes still present in RIB".format(tc_name)
+ )
step(
"Verify that boundary values tags are used for summary route"
result = verify_ospf_summary(tgen, topo, dut, input_dict, expected=False)
assert (
result is not True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name)
step("Delete the configured summary address")
ospf_summ_r1 = {
result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name)
step("Verify that summary address is flushed from neighbor.")
dut = "r1"
result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False)
- assert (
- result is not True
- ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
- tc_name, result
+ assert result is not True, (
+ "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB \n Error: "
+ "Routes still present in OSPF RIB {}".format(tc_name, result)
)
result = verify_rib(
tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False
)
- assert (
- result is not True
- ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+ assert result is not True, (
+ "Testcase {} : Failed \n Expected: Routes should not be present in RIB\n"
+ "Error: Routes still present in RIB".format(tc_name)
+ )
step("Configure summary first & then configure matching static route.")
assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
ospf_covergence = verify_ospf_neighbor(tgen, topo)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "setup_module :Failed \n Error {}".format(
ospf_covergence
)
result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name)
- step(
- "Configure External Route summary in R0 to summarise 5" " routes to one route."
- )
+ step("Configure External Route summary in R0 to summarise 5 routes to one route.")
ospf_summ_r1 = {
"r0": {
"ospf": {
result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name)
step("Verify that show ip ospf summary should show the summaries with tag.")
input_dict = {
step("Verify that summary lsa is withdrawn from R1 and deleted from R0.")
dut = "r1"
result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
- assert (
- result is not True
- ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
- tc_name, result
+ assert result is not True, (
+ "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB \n Error: "
+ "Routes still present in OSPF RIB {}".format(tc_name, result)
)
result = verify_rib(
tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False
)
- assert (
- result is not True
- ), "Testcase {} : Failed" "Error: Summary Route still present in RIB".format(
- tc_name
+ assert result is not True, (
+ "Testcase {} : Failed \n Expected: Routes should not be present in RIB "
+ "Error: Summary Route still present in RIB".format(tc_name)
)
step("show ip ospf summary should not have any summary address.")
result = verify_ospf_summary(tgen, topo, dut, input_dict, expected=False)
assert (
result is not True
- ), "Testcase {} : Failed" "Error: Summary still present in DB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Summary still present in DB".format(tc_name)
step("Configure Min tag value")
ospf_summ_r1 = {
result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name)
step("Verify that show ip ospf summary should show the summaries with tag.")
input_dict = {
result = verify_ospf_summary(tgen, topo, dut, input_dict)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name)
step("Configure Max Tag Value")
ospf_summ_r1 = {
result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name)
step(
"Verify that boundary values tags are used for summary route"
result = verify_ospf_summary(tgen, topo, dut, input_dict)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name)
step("configure new static route with different tag.")
input_dict_static_rtes_11 = {
dut = "r1"
result = verify_ospf_rib(tgen, dut, input_dict_summary, tag="88888", expected=False)
- assert (
- result is not True
- ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
- tc_name, result
+ assert result is not True, (
+ "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB \n Error: "
+ "Routes still present in OSPF RIB {}".format(tc_name, result)
)
result = verify_rib(
tag="88888",
expected=False,
)
- assert (
- result is not True
- ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+ assert result is not True, (
+ "Testcase {} : Failed\n Expected: Routes should not be present in RIB.\n"
+ "Error: Routes still present in RIB".format(tc_name)
+ )
step(
"Verify that boundary values tags are used for summary route"
result = verify_ospf_summary(tgen, topo, dut, input_dict, expected=False)
assert (
result is not True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name)
step("Delete the configured summary address")
ospf_summ_r1 = {
result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name)
step("Verify that summary address is flushed from neighbor.")
dut = "r1"
result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False)
- assert (
- result is not True
- ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
- tc_name, result
+ assert result is not True, (
+ "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB \n Error: "
+ "Routes still present in OSPF RIB {}".format(tc_name, result)
)
result = verify_rib(
tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False
)
- assert (
- result is not True
- ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+ assert result is not True, (
+ "Testcase {} : Failed \n Expected: Routes should not be present in RIB \n"
+ "Error: Routes still present in RIB".format(tc_name)
+ )
step("Configure summary first & then configure matching static route.")
result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name)
step(
"Configure External Route summary in R0 to summarise 5"
dut = "r1"
result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False)
- assert (
- result is not True
- ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
- tc_name, result
+ assert result is not True, (
+ "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB.\n Error: "
+ "Routes still present in OSPF RIB {}".format(tc_name, result)
)
result = verify_rib(
tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False
)
- assert (
- result is not True
- ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+ assert result is not True, (
+ "Testcase {} : Failed\n Expected: Routes should not be present in RIB."
+ "Error: Routes still present in RIB".format(tc_name)
+ )
- step("Verify that show ip ospf summary should show the " "configured summaries.")
+ step("Verify that show ip ospf summary should show the configured summaries.")
input_dict = {
SUMMARY["ipv4"][0]: {
"summaryAddress": SUMMARY["ipv4"][0],
result = verify_ospf_summary(tgen, topo, dut, input_dict)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name)
step("Delete the configured summary")
ospf_summ_r1 = {
step("Verify that summary lsa is withdrawn from R1 and deleted from R0.")
dut = "r1"
result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
- assert (
- result is not True
- ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
- tc_name, result
+ assert result is not True, (
+ "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB. \n Error: "
+ "Routes still present in OSPF RIB {}".format(tc_name, result)
)
result = verify_rib(
)
assert (
result is not True
- ), "Testcase {} : Failed" "Error: Summary Route still present in RIB".format(
- tc_name
- )
+ ), "Testcase {} : Failed. Error: Summary Route still present in RIB".format(tc_name)
step("show ip ospf summary should not have any summary address.")
input_dict = {
result = verify_ospf_summary(tgen, topo, dut, input_dict, expected=False)
assert (
result is not True
- ), "Testcase {} : Failed" "Error: Summary still present in DB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Summary still present in DB".format(tc_name)
step("Reconfigure summary with no advertise.")
ospf_summ_r1 = {
dut = "r1"
result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False)
- assert (
- result is not True
- ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
- tc_name, result
+ assert result is not True, (
+ "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB. \n Error: "
+ "Routes still present in OSPF RIB {}".format(tc_name, result)
)
result = verify_rib(
tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False
)
- assert (
- result is not True
- ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+ assert result is not True, (
+ "Testcase {} : Failed \n Expected: Routes should not be present in RIB \n"
+ "Error: Routes still present in RIB".format(tc_name)
+ )
- step("Verify that show ip ospf summary should show the " "configured summaries.")
+ step("Verify that show ip ospf summary should show the configured summaries.")
input_dict = {
SUMMARY["ipv4"][0]: {
"summaryAddress": SUMMARY["ipv4"][0],
result = verify_ospf_summary(tgen, topo, dut, input_dict)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name)
step(
"Change summary address from no advertise to advertise "
result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name)
step("Verify that show ip ospf summary should show the summaries.")
input_dict = {
result = verify_ospf_summary(tgen, topo, dut, input_dict)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name)
- step("Verify that originally advertised routes are withdraw from there" " peer.")
+ step("Verify that originally advertised routes are withdraw from there peer.")
input_dict = {
"r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]}
}
dut = "r1"
result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
- assert (
- result is not True
- ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
- tc_name, result
+ assert result is not True, (
+ "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB \n Error: "
+ "Routes still present in OSPF RIB {}".format(tc_name, result)
)
result = verify_rib(
tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False
)
- assert (
- result is not True
- ), "Testcase {} : Failed" "Error: Routes is present in RIB".format(tc_name)
+ assert result is not True, (
+ "Testcase {} : Failed\n Expected: Routes should not be present in RIB"
+ "Error: Routes is present in RIB".format(tc_name)
+ )
write_test_footer(tc_name)
result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name)
- step(
- "Configure External Route summary in R0 to summarise 5" " routes to one route."
- )
+ step("Configure External Route summary in R0 to summarise 5 routes to one route.")
ospf_summ_r1 = {
"r0": {
result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name)
step("Verify that show ip ospf summary should show the summaries.")
input_dict = {
result = verify_ospf_summary(tgen, topo, dut, input_dict)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name)
- step("Verify that originally advertised routes are withdraw from there" " peer.")
+ step("Verify that originally advertised routes are withdraw from there peer.")
input_dict = {
"r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]}
}
dut = "r1"
result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
- assert (
- result is not True
- ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
- tc_name, result
+ assert result is not True, (
+ "Testcase {} : Failed \n \n Expected: Routes should not be present in OSPF RIB.\n Error: "
+ "Routes still present in OSPF RIB {}".format(tc_name, result)
)
result = verify_rib(
tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False
)
- assert (
- result is not True
- ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+ assert result is not True, (
+ "Testcase {} : Failed\n Expected: Routes should not be present in RIB.\n"
+ "Error: Routes still present in RIB".format(tc_name)
+ )
step(
"configure route map and add rule to permit configured static "
result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name)
input_dict = {
SUMMARY["ipv4"][0]: {
step("Verify that advertised summary route is flushed from neighbor.")
dut = "r1"
result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False)
- assert (
- result is not True
- ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
- tc_name, result
+ assert result is not True, (
+ "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB\n Error: "
+ "Routes still present in OSPF RIB {}".format(tc_name, result)
)
result = verify_rib(
tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False
)
- assert (
- result is not True
- ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+ assert result is not True, (
+ "Testcase {} : Failed \n Expected: Routes should not be present in RIB.\n"
+ "Error: Routes still present in RIB".format(tc_name)
+ )
step("Delete the configured route map.")
result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name)
input_dict = {
SUMMARY["ipv4"][0]: {
result = verify_ospf_summary(tgen, topo, dut, input_dict)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name)
step("Reconfigure the route map with denying configure summary address.")
result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name)
step("Redistribute static/connected routes without route map.")
result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name)
input_dict = {
SUMMARY["ipv4"][0]: {
result = verify_ospf_summary(tgen, topo, dut, input_dict)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name)
step(
"Configure rule to deny all the routes in route map and configure"
step("Verify that no summary route is originated.")
dut = "r1"
result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False)
- assert (
- result is not True
- ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
- tc_name, result
+ assert result is not True, (
+ "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB.\n Error: "
+ "Routes still present in OSPF RIB {}".format(tc_name, result)
)
result = verify_rib(
tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False
)
- assert (
- result is not True
- ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+ assert result is not True, (
+ "Testcase {} : Failed\n Expected: Routes should not be present in RIB"
+ "Error: Routes still present in RIB".format(tc_name)
+ )
routemaps = {
"r0": {
result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name)
step("Verify that show ip ospf summary should show the summaries.")
input_dict = {
result = verify_ospf_summary(tgen, topo, dut, input_dict)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name)
step("Change route map rule for 1 of the routes to deny.")
# Create ip prefix list
result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name)
step("add rule in route map to deny configured summary address.")
# Create ip prefix list
result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name)
write_test_footer(tc_name)
result = verify_ospf_summary(tgen, topo, dut, input_dict)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name)
write_test_footer(tc_name)
result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name)
- step(
- "Configure External Route summary in R0 to summarise 5" " routes to one route."
- )
+ step("Configure External Route summary in R0 to summarise 5 routes to one route.")
ospf_summ_r1 = {
"r0": {
result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name)
step("Verify that show ip ospf summary should show the summaries.")
input_dict = {
result = verify_ospf_summary(tgen, topo, dut, input_dict)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name)
- step("Verify that originally advertised routes are withdraw from there" " peer.")
+ step("Verify that originally advertised routes are withdraw from there peer.")
input_dict = {
"r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]}
}
dut = "r1"
result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
- assert (
- result is not True
- ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
- tc_name, result
+ assert result is not True, (
+ "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB.\n Error: "
+ "Routes still present in OSPF RIB {}".format(tc_name, result)
)
result = verify_rib(
tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False
)
- assert (
- result is not True
- ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+ assert result is not True, (
+ "Testcase {} : Failed\n Expected: Routes should not be present in RIB.\n"
+ "Error: Routes still present in RIB".format(tc_name)
+ )
step("Reload the FRR router")
# stop/start -> restart FRR router and verify
result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name)
step("Verify that show ip ospf summary should show the summaries.")
input_dict = {
result = verify_ospf_summary(tgen, topo, dut, input_dict)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name)
- step("Verify that originally advertised routes are withdraw from there" " peer.")
+ step("Verify that originally advertised routes are withdraw from there peer.")
input_dict = {
"r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]}
}
dut = "r1"
result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
- assert (
- result is not True
- ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
- tc_name, result
+ assert result is not True, (
+ "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB. \n Error: "
+ "Routes still present in OSPF RIB {}".format(tc_name, result)
)
result = verify_rib(
tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False
)
- assert (
- result is not True
- ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+ assert result is not True, (
+ "Testcase {} : Failed\n Expected: Routes should not be present in RIB\n"
+ "Error: Routes still present in RIB".format(tc_name)
+ )
step("Kill OSPFd daemon on R0.")
kill_router_daemons(tgen, "r0", ["ospfd"])
step("Verify OSPF neighbors are up after bringing back ospfd in R0")
# Api call verify whether OSPF is converged
ospf_covergence = verify_ospf_neighbor(tgen, topo)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "setup_module :Failed \n Error {}".format(
ospf_covergence
)
result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name)
step("Verify that show ip ospf summary should show the summaries.")
input_dict = {
result = verify_ospf_summary(tgen, topo, dut, input_dict)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name)
- step("Verify that originally advertised routes are withdraw from there" " peer.")
+ step("Verify that originally advertised routes are withdraw from there peer.")
input_dict = {
"r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]}
}
dut = "r1"
result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
- assert (
- result is not True
- ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
- tc_name, result
+ assert result is not True, (
+ "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB. \n Error: "
+ "Routes still present in OSPF RIB {}".format(tc_name, result)
)
result = verify_rib(
tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False
)
- assert (
- result is not True
- ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+ assert result is not True, (
+ "Testcase {} : Failed\n Expected: Routes should not be present in RIB\n"
+ "Error: Routes still present in RIB".format(tc_name)
+ )
step("restart zebrad")
kill_router_daemons(tgen, "r0", ["zebra"])
result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name)
step("Verify that show ip ospf summary should show the summaries.")
input_dict = {
result = verify_ospf_summary(tgen, topo, dut, input_dict)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name)
- step("Verify that originally advertised routes are withdraw from there" " peer.")
+ step("Verify that originally advertised routes are withdraw from there peer.")
input_dict = {
"r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]}
}
dut = "r1"
result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
- assert (
- result is not True
- ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
- tc_name, result
+ assert result is not True, (
+ "Testcase {} : Failed\n Expected: Routes should not be present in OSPF RIB. \n Error: "
+ "Routes still present in OSPF RIB {}".format(tc_name, result)
)
result = verify_rib(
tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False
)
- assert (
- result is not True
- ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+ assert result is not True, (
+ "Testcase {} : Failed\n Expected: Routes should not be present in RIB.\n"
+ "Error: Routes still present in RIB".format(tc_name)
+ )
write_test_footer(tc_name)
pytest.skip(tgen.errors)
# Api call verify whether OSPF is converged
ospf_covergence = verify_ospf_neighbor(tgen, topo)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "setup_module :Failed \n Error {}".format(
ospf_covergence
)
result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name)
- step(
- "Configure External Route summary in R0 to summarise 5" " routes to one route."
- )
+ step("Configure External Route summary in R0 to summarise 5 routes to one route.")
ospf_summ_r0 = {
"r0": {
"route is sent to R1."
)
- step(
- "Configure summary & redistribute static/connected route with " "metric type 2"
- )
+ step("Configure summary & redistribute static/connected route with metric type 2")
input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][3]}]}}
dut = "r1"
result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name)
step("Verify that show ip ospf summary should show the summaries.")
input_dict = {
result = verify_ospf_summary(tgen, topo, dut, input_dict)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name)
step("Learn type 7 lsa from neighbours")
result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name)
ospf_summ_r0 = {
"r0": {
result = verify_ospf_summary(tgen, topo, dut, input_dict)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name)
step("Verify that already originated summary is intact.")
input_dict = {
result = verify_ospf_summary(tgen, topo, dut, input_dict)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name)
dut = "r1"
aggr_timer = {"r1": {"ospf": {"aggr_timer": 6}}}
pytest.skip(tgen.errors)
ospf_covergence = verify_ospf_neighbor(tgen, topo)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "setup_module :Failed \n Error {}".format(
ospf_covergence
)
step("Verify that the neighbour is not FULL between R1 and R2.")
dut = "r1"
ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, expected=False)
- assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is not True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
dut = "r2"
ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
ospf_covergence = verify_ospf_neighbor(
tgen, topo, dut=dut, expected=False, retry_timeout=10
)
- assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is not True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
dut = "r2"
ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
"show ip ospf neighbor cmd."
)
ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, expected=False)
- assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is not True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
dut = "r2"
ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
dut = "r1"
ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
ospf_covergence = verify_ospf_neighbor(
tgen, topo, dut=dut, expected=False, retry_timeout=6
)
- assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is not True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
dut = "r2"
ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
ospf_covergence = verify_ospf_neighbor(
tgen, topo, dut=dut, expected=False, retry_timeout=10
)
- assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is not True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
dut = "r2"
ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
"show ip ospf neighbor cmd."
)
ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, expected=False)
- assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is not True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
dut = "r2"
ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
dut = "r1"
ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
ospf_covergence = verify_ospf_neighbor(
tgen, topo, dut=dut, expected=False, retry_timeout=10
)
- assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is not True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
dut = "r2"
ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
dut = "r2"
ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
dut = "r2"
ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
dut = "r2"
ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
dut = "r2"
ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
dut = "r2"
ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
pytest.skip(tgen.errors)
ospf_covergence = verify_ospf_neighbor(tgen, topo)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "setup_module :Failed \n Error {}".format(
ospf_covergence
)
step("Verify OSPF neighbors after base config is done.")
# Api call verify whether OSPF is converged
ospf_covergence = verify_ospf_neighbor(tgen, topo)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
dut = "r0"
# Api call verify whether OSPF is converged
ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, expected=False)
- assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is not True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
assert (
result is not True
- ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format(
+ ), "Testcase {} : Failed \n r1: OSPF routes are present \n Error: {}".format(
tc_name, result
)
)
assert (
result is not True
- ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format(
+ ), "Testcase {} : Failed \n r1: routes are still present \n Error: {}".format(
tc_name, result
)
step("Verify OSPF neighbors are up after bringing back ospfd in R0")
# Api call verify whether OSPF is converged
ospf_covergence = verify_ospf_neighbor(tgen, topo)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
dut = "r1"
# Api call verify whether OSPF is converged
ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, expected=False)
- assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is not True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
step("Verify OSPF neighbors are up after bringing back ospfd in R1")
# Api call verify whether OSPF is converged
ospf_covergence = verify_ospf_neighbor(tgen, topo)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
step("Verify OSPF neighbors after base config is done.")
# Api call verify whether OSPF is converged
ospf_covergence = verify_ospf_neighbor(tgen, topo)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
step("Verify OSPF neighbors are up after restarting R0")
# Api call verify whether OSPF is converged
ospf_covergence = verify_ospf_neighbor(tgen, topo)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
step("Verify OSPF neighbors are up after restarting R1")
# Api call verify whether OSPF is converged
ospf_covergence = verify_ospf_neighbor(tgen, topo)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
step("Verify OSPF neighbors after base config is done.")
# Api call verify whether OSPF is converged
ospf_covergence = verify_ospf_neighbor(tgen, topo)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
assert (
result is not True
- ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format(
+ ), "Testcase {} : Failed \n r1: OSPF routes are present \n Error: {}".format(
tc_name, result
)
)
assert (
result is not True
- ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format(
+ ), "Testcase {} : Failed \n r1: routes are still present \n Error: {}".format(
tc_name, result
)
step("Verify OSPF neighbors are up after bringing back ospfd in R0")
# Api call verify whether OSPF is converged
ospf_covergence = verify_ospf_neighbor(tgen, topo)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
step("Verify OSPF neighbors are up after bringing back ospfd in R1")
# Api call verify whether OSPF is converged
ospf_covergence = verify_ospf_neighbor(tgen, topo)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
pytest.skip(tgen.errors)
# Api call verify whether OSPF is converged
ospf_covergence = verify_ospf_neighbor(tgen, topo)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "setup_module :Failed \n Error {}".format(
ospf_covergence
)
step("Verify that OSPF is up with 8 neighborship sessions.")
dut = "r1"
ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh, expected=False)
assert (
result is not True
- ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format(
+ ), "Testcase {} : Failed \n r1: OSPF routes are present \n Error: {}".format(
tc_name, result
)
)
assert (
result is not True
- ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format(
+ ), "Testcase {} : Failed \n r1: routes are still present \n Error: {}".format(
tc_name, result
)
step("Verify that OSPF is up with 8 neighborship sessions.")
dut = "r1"
ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
)
assert (
result is not True
- ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format(
+ ), "Testcase {} : Failed \n r1: OSPF routes are present \n Error: {}".format(
tc_name, result
)
)
assert (
result is not True
- ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format(
+ ), "Testcase {} : Failed \n r1: routes are still present \n Error: {}".format(
tc_name, result
)
step("Verify that OSPF is up with 2 neighborship sessions.")
dut = "r1"
ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
)
assert (
result is not True
- ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format(
+ ), "Testcase {} : Failed \n r1: OSPF routes are present \n Error: {}".format(
tc_name, result
)
)
assert (
result is not True
- ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format(
+ ), "Testcase {} : Failed \n r1: routes are still present \n Error: {}".format(
tc_name, result
)
pytest.skip(tgen.errors)
# Api call verify whether OSPF is converged
ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "setup_module :Failed \n Error {}".format(
ospf_covergence
)
step("Verify that OSPF is up with 8 neighborship sessions.")
ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
dut = "r0"
ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, lan=True)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
dut = "r2"
ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, lan=True)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
)
assert (
result is not True
- ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format(
+ ), "Testcase {} : Failed \n r1: OSPF routes are present \n Error: {}".format(
tc_name, result
)
)
assert (
result is not True
- ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format(
+ ), "Testcase {} : Failed \n r1: routes are still present \n Error: {}".format(
tc_name, result
)
pytest.skip(tgen.errors)
# Api call verify whether OSPF is converged
ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "setup_module :Failed \n Error {}".format(
ospf_covergence
)
"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"},
}
}
}
"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"},
}
}
}
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."
+ "Configure DR priority 100 on R0 and clear ospf neighbors "
+ "on all the routers."
)
input_dict = {
"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"},
}
}
}
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."
+ "Configure DR priority 150 on R0 and clear ospf neighbors "
+ "on all the routers."
)
input_dict = {
"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"},
}
}
}
"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"},
}
}
}
"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"},
}
}
}
result = verify_ospf_neighbor(tgen, topo, dut, lan=True, expected=False)
assert (
result is not True
- ), "Testcase {} : Failed \n " "r0: OSPF neighbors-hip is up \n Error: {}".format(
+ ), "Testcase {} : Failed \n r0: OSPF neighbors-hip is up \n Error: {}".format(
tc_name, result
)
"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"},
}
}
}
"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"},
}
}
}
"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"},
}
}
}
"s1": {
"ospf": {
"priority": 98,
- "timerDeadSecs": 4,
+ "timerDeadSecs": 10,
"area": "0.0.0.3",
"mcastMemberOspfDesignatedRouters": True,
"mcastMemberOspfAllRouters": True,
pytest.skip(tgen.errors)
# Api call verify whether OSPF is converged
ospf_covergence = verify_ospf_neighbor(tgen, topo)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "setup_module :Failed \n Error {}".format(
ospf_covergence
)
assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
ospf_covergence = verify_ospf_neighbor(tgen, topo)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
- step("Change area 1 as non nssa area (on the fly changing area" " type on DUT).")
+ step("Change area 1 as non nssa area (on the fly changing area type on DUT).")
for rtr in ["r1", "r2", "r3"]:
input_dict = {
"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",
}
],
}
"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",
}
],
}
"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",
}
],
}
"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",
}
],
}
pytest.skip(tgen.errors)
# Api call verify whether OSPF is converged
ospf_covergence = verify_ospf_neighbor(tgen, topo)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "setup_module :Failed \n Error {}".format(
ospf_covergence
)
redistribute_ospf(tgen, topo, "r0", "static", delete=True)
- step(
- "Create prefix-list in R0 to permit 10.0.20.1/32 prefix &" " deny 10.0.20.2/32"
- )
+ step("Create prefix-list in R0 to permit 10.0.20.1/32 prefix & deny 10.0.20.2/32")
# Create ip prefix list
pfx_list = {
result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
assert (
result is not True
- ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format(
+ ), "Testcase {} : Failed \n r1: OSPF routes are present \n Error: {}".format(
tc_name, result
)
)
assert (
result is not True
- ), "Testcase {} : Failed \n " "r1: routes are present in fib \n Error: {}".format(
+ ), "Testcase {} : Failed \n r1: routes are present in fib \n Error: {}".format(
tc_name, result
)
result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
assert (
result is not True
- ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format(
+ ), "Testcase {} : Failed \n r1: OSPF routes are present \n Error: {}".format(
tc_name, result
)
)
assert (
result is not True
- ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format(
+ ), "Testcase {} : Failed \n r1: OSPF routes are present \n Error: {}".format(
tc_name, result
)
result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
assert (
result is not True
- ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format(
+ ), "Testcase {} : Failed \n r1: OSPF routes are present \n Error: {}".format(
tc_name, result
)
)
assert (
result is not True
- ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format(
+ ), "Testcase {} : Failed \n r1: routes are still present \n Error: {}".format(
tc_name, result
)
result = verify_ospf_rib(tgen, dut, input_dict, retry_timeout=4, expected=False)
assert (
result is not True
- ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format(
+ ), "Testcase {} : Failed \n r1: OSPF routes are present \n Error: {}".format(
tc_name, result
)
)
assert (
result is not True
- ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format(
+ ), "Testcase {} : Failed \n r1: routes are still present \n Error: {}".format(
tc_name, result
)
result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
assert (
result is not True
- ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format(
+ ), "Testcase {} : Failed \n r1: OSPF routes are present \n Error: {}".format(
tc_name, result
)
)
assert (
result is not True
- ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format(
+ ), "Testcase {} : Failed \n r1: routes are still present \n Error: {}".format(
tc_name, result
)
result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
assert (
result is not True
- ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format(
+ ), "Testcase {} : Failed \n r1: OSPF routes are present \n Error: {}".format(
tc_name, result
)
)
assert (
result is not True
- ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format(
+ ), "Testcase {} : Failed \n r1: routes are still present \n Error: {}".format(
tc_name, result
)
result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
assert (
result is not True
- ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format(
+ ), "Testcase {} : Failed \n r1: OSPF routes are present \n Error: {}".format(
tc_name, result
)
)
assert (
result is not True
- ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format(
+ ), "Testcase {} : Failed \n r1: routes are still present \n Error: {}".format(
tc_name, result
)
result = verify_prefix_lists(tgen, pfx_list)
assert (
result is not True
- ), "Testcase {} : Failed \n Prefix list not " "present. Error: {}".format(
+ ), "Testcase {} : Failed \n Prefix list not present. Error: {}".format(
tc_name, result
)
result = verify_prefix_lists(tgen, pfx_list)
assert (
result is not True
- ), "Testcase {} : Failed \n Prefix list not " "present. Error: {}".format(
+ ), "Testcase {} : Failed \n Prefix list not present. Error: {}".format(
tc_name, result
)
# Api call verify whether OSPF is converged
ospf_covergence = verify_ospf_neighbor(tgen, topo)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
pytest.skip(tgen.errors)
# Api call verify whether OSPF is converged
ospf_covergence = verify_ospf_neighbor(tgen, topo)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "setup_module :Failed \n Error {}".format(
ospf_covergence
)
step("Verify that OSPF neighbors are FULL.")
ospf_covergence = verify_ospf_neighbor(tgen, topo)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
step("Verify that OSPF neighbors are FULL.")
ospf_covergence = verify_ospf_neighbor(tgen, topo)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
step("Verify that OSPF neighbours are reset and forms new adjacencies.")
# Api call verify whether OSPF is converged
ospf_covergence = verify_ospf_neighbor(tgen, topo)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
step("Verify that OSPF neighbours are Full.")
# Api call verify whether OSPF is converged
ospf_covergence = verify_ospf_neighbor(tgen, topo)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
pytest.skip(tgen.errors)
ospf_covergence = verify_ospf_neighbor(tgen, topo)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "setup_module :Failed \n Error {}".format(
ospf_covergence
)
# Api call verify whether BGP is converged
ospf_covergence = verify_ospf_neighbor(tgen, topo)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
step("verify that ospf neighbours are full")
ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
step("verify that ospf neighbours are full")
ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
step("verify that ospf neighbours are full")
ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
step("verify that ospf neighbours are full")
ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
reset_config_on_routers(tgen)
ospf_covergence = verify_ospf_neighbor(tgen, topo)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
dut = "r1"
result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict)
assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
- step("modify dead interval from default value to r1" "dead interval timer on r2")
+ step("modify dead interval from default value to r1 dead interval timer on r2")
topo1 = {
"r0": {
step("verify that ospf neighbours are full")
ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
- step("reconfigure the default dead interval timer value to " "default on r1 and r2")
+ step("reconfigure the default dead interval timer value to default on r1 and r2")
topo1 = {
"r0": {
"links": {
step("verify that ospf neighbours are full")
ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
step("verify that ospf neighbours are full")
ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
ospf_covergence
)
result = create_interfaces_cfg(tgen, topo1)
assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
- step(
- "Verify that timer value is deleted from intf & " "set to default value 40 sec."
- )
+ step("Verify that timer value is deleted from intf & set to default value 40 sec.")
input_dict = {"r1": {"links": {"r0": {"ospf": {"timerDeadSecs": 40}}}}}
dut = "r1"
result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict)
clear_ospf(tgen, "r0")
- step(
- "Verify that OSPF neighborship between R0 and R1 is stuck in Exstart" " State."
- )
+ step("Verify that OSPF neighborship between R0 and R1 is stuck in Exstart State.")
result = verify_ospf_neighbor(tgen, topo, expected=False)
assert result is not True, (
"Testcase {} : Failed \n OSPF nbrs are Full "
"instead of Exstart. Error: {}".format(tc_name, result)
)
- step(
- "Verify that configured MTU value is updated in the show ip " "ospf interface."
- )
+ step("Verify that configured MTU value is updated in the show ip ospf interface.")
dut = "r0"
input_dict = {"r0": {"links": {"r1": {"ospf": {"mtuBytes": 1200}}}}}
clear_ospf(tgen, "r0")
- step(
- "Verify that OSPF neighborship between R0 and R1 is stuck in Exstart" " State."
- )
+ step("Verify that OSPF neighborship between R0 and R1 is stuck in Exstart State.")
result = verify_ospf_neighbor(tgen, topo, expected=False)
assert result is not True, (
"Testcase {} : Failed \n OSPF nbrs are Full "
result = verify_ospf_neighbor(tgen, topo)
assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
- step(
- "Configure ospf interface with jumbo MTU (9216)." "Reset ospf neighbors on R0."
- )
+ step("Configure ospf interface with jumbo MTU (9216). Reset ospf neighbors on R0.")
rtr0.run("ip link set {} mtu 9216".format(r0_r1_intf))
rtr1.run("ip link set {} mtu 9216".format(r1_r0_intf))
pytest.skip(tgen.errors)
ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "setup_module :Failed \n Error: {}".format(
ospf_covergence
)
ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True)
assert (
ospf_covergence is True
- ), "OSPF is not after reset config \n Error:" " {}".format(ospf_covergence)
+ ), "OSPF is not after reset config \n Error: {}".format(ospf_covergence)
- step("Verify that GR helper route is disabled by default to the in" "the DUT.")
+ step("Verify that GR helper route is disabled by default to the in the DUT.")
input_dict = {
"helperSupport": "Disabled",
"strictLsaCheck": "Enabled",
- "restartSupoort": "Planned and Unplanned Restarts",
+ "restartSupport": "Planned and Unplanned Restarts",
"supportedGracePeriod": 1800,
}
dut = "r0"
result = verify_ospf_gr_helper(tgen, topo, dut, input_dict)
assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
- step("Verify that DUT does not enter helper mode upon receiving the " "grace lsa.")
+ step("Verify that DUT does not enter helper mode upon receiving the grace lsa.")
# send grace lsa
scapy_send_raw_packet(tgen, topo, "r1", intf1, pkt)
result = verify_ospf_gr_helper(tgen, topo, dut, input_dict, expected=False)
assert (
result is not True
- ), "Testcase {} : Failed. DUT entered helper role " " \n Error: {}".format(
+ ), "Testcase {} : Failed. DUT entered helper role \n Error: {}".format(
tc_name, result
)
input_dict = {
"helperSupport": "Enabled",
"strictLsaCheck": "Enabled",
- "restartSupoort": "Planned and Unplanned Restarts",
+ "restartSupport": "Planned and Unplanned Restarts",
"supportedGracePeriod": 1800,
}
dut = "r0"
assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
step("Perform GR in RR.")
- step("Verify that DUT does enter helper mode upon receiving" " the grace lsa.")
+ step("Verify that DUT does enter helper mode upon receiving the grace lsa.")
input_dict = {"activeRestarterCnt": 1}
gracelsa_sent = False
repeat = 0
result = create_router_ospf(tgen, topo, ospf_gr_r0)
assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
- step("Verify that DUT does enter helper mode upon receiving" " the grace lsa.")
+ step("Verify that DUT does enter helper mode upon receiving the grace lsa.")
input_dict = {"activeRestarterCnt": 1}
gracelsa_sent = False
repeat = 0
result = create_router_ospf(tgen, topo, ospf_gr_r0)
assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
- step("Verify that GR helper router is disabled in the DUT for" " router id x.x.x.x")
+ step("Verify that GR helper router is disabled in the DUT for router id x.x.x.x")
input_dict = {"enabledRouterIds": [{"routerId": "1.1.1.1"}]}
dut = "r0"
result = verify_ospf_gr_helper(tgen, topo, dut, input_dict, expected=False)
ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True)
assert (
ospf_covergence is True
- ), "OSPF is not after reset config \n Error:" " {}".format(ospf_covergence)
+ ), "OSPF is not after reset config \n Error: {}".format(ospf_covergence)
ospf_gr_r0 = {
"r0": {"ospf": {"graceful-restart": {"helper enable": [], "opaque": True}}}
}
pytest.skip(tgen.errors)
ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "setup_module :Failed \n Error: {}".format(
ospf_covergence
)
ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True)
assert (
ospf_covergence is True
- ), "OSPF is not after reset config \n Error:" " {}".format(ospf_covergence)
- step(
- "Configure DR pririty 100 on R0 and clear ospf neighbors " "on all the routers."
- )
+ ), "OSPF is not after reset config \n Error: {}".format(ospf_covergence)
+
+ step("Configure DR priority 100 on R0 and clear ospf neighbors "
+ "on all the routers.")
input_dict = {
"r0": {
"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"},
}
}
}
ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True)
assert (
ospf_covergence is True
- ), "OSPF is not after reset config \n Error:" " {}".format(ospf_covergence)
- step(
- "Configure DR pririty 100 on R0 and clear ospf neighbors " "on all the routers."
- )
+ ), "OSPF is not after reset config \n Error: {}".format(ospf_covergence)
+ step("Configure DR priority 0 on R0 and clear ospf neighbors on all the routers.")
input_dict = {
"r0": {
"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"},
}
}
}
pytest.skip(tgen.errors)
ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "setup_module :Failed \n Error: {}".format(
ospf_covergence
)
ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True)
assert (
ospf_covergence is True
- ), "OSPF is not after reset config \n Error:" " {}".format(ospf_covergence)
+ ), "OSPF is not after reset config \n Error: {}".format(ospf_covergence)
ospf_gr_r0 = {
"r0": {"ospf": {"graceful-restart": {"helper enable": [], "opaque": True}}}
}
result = verify_ospf_gr_helper(tgen, topo, dut, input_dict, expected=False)
assert (
result is not True
- ), "Testcase {} : Failed. DUT entered helper role " " \n Error: {}".format(
+ ), "Testcase {} : Failed. DUT entered helper role \n Error: {}".format(
tc_name, result
)
ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True)
assert (
ospf_covergence is True
- ), "OSPF is not after reset config \n Error:" " {}".format(ospf_covergence)
+ ), "OSPF is not after reset config \n Error: {}".format(ospf_covergence)
ospf_gr_r0 = {
"r0": {"ospf": {"graceful-restart": {"helper enable": [], "opaque": True}}}
}
"2.2.2.2":[
{
"converged":"Full",
- "address":"10.0.1.2",
+ "ifaceAddress":"10.0.1.2",
"ifaceName":"eth-rt2:10.0.1.1"
}
]
"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"
}
]
"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"
}
]
"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"
}
]
"4.4.4.4":[
{
"converged":"Full",
- "address":"10.0.5.4",
+ "ifaceAddress":"10.0.5.4",
"ifaceName":"eth-rt4:10.0.5.5"
}
]
"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"
}
]
"6.6.6.6":[
{
"converged":"Full",
- "address":"10.0.6.6",
+ "ifaceAddress":"10.0.6.6",
"ifaceName":"eth-rt6:10.0.6.7"
}
]
--- /dev/null
+!
+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
--- /dev/null
+!
+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
--- /dev/null
+!
+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 30
+!
+interface r1-eth1 vrf blue
+ ip address 10.0.10.1/24
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+!
+interface r1-eth2 vrf green
+ ip address 10.0.91.1/24
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+!
+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
--- /dev/null
+{
+ "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
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+{
+ "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
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+{
+ "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
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+{
+ "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
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+{
+ "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
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+{
+ "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
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+!
+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 30
+!
+interface r2-eth1 vrf blue
+ ip address 10.0.20.2/24
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+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 30
+!
+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
--- /dev/null
+!
+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 30
+!
+interface r3-eth1 vrf blue
+ ip address 10.0.30.3/24
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+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 30
+!
+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
--- /dev/null
+!
+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 30
+!
+interface r4-eth1 vrf blue
+ ip address 10.0.40.4/24
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+interface r4-eth2 vrf green
+ ip address 10.0.94.4/24
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+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
--- /dev/null
+!
+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 30
+!
+interface ra-eth1
+ ip address 10.0.10.5/24
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+interface ra-eth2
+ ip address 10.0.20.5/24
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+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
+!
--- /dev/null
+!
+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 30
+!
+interface rb-eth1
+ ip address 10.0.30.6/24
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+interface rb-eth2
+ ip address 10.0.40.6/24
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+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
+!
--- /dev/null
+!
+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 30
+!
+interface rc-eth1
+ ip address 10.0.80.7/24
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+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
+!
--- /dev/null
+#!/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=60, 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=60, 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")
+ tgen.net["r2"].cmd("ip link set dev r2-eth2 down")
+ # ospf dead-interval is set to 30 seconds, wait 35 seconds to clear the neighbor
+ sleep(35)
+ tgen.net["r2"].cmd("ip link set dev r2-eth2 up")
+ 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=60, 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 30 seconds, wait 35 seconds to clear the neighbor
+ sleep(35)
+ 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=60, 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=60, 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=60, 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=60, 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=60, 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=60, 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))
--- /dev/null
+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
+!
--- /dev/null
+log file staticd.log
+!
+hostname rt1
+!
+line vty
+!
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+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
+!
--- /dev/null
+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
+!
--- /dev/null
+log file staticd.log
+!
+hostname rt2
+!
+line vty
+!
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+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
+!
--- /dev/null
+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
+!
--- /dev/null
+log file staticd.log
+!
+hostname rt3
+!
+ip route 0.0.0.0/0 Null0
+!
+line vty
+!
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+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
+!
--- /dev/null
+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
+!
--- /dev/null
+log file staticd.log
+!
+hostname rt4
+!
+ip route 172.16.1.1/32 Null0
+ip route 172.16.1.2/32 Null0
+!
+line vty
+!
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+}
--- /dev/null
+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
+!
--- /dev/null
+#!/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))
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):
],
"edges":[
{
- "edge-id":167772161,
+ "edge-id":"10.0.0.1",
"status":"Sync",
"origin":"OSPFv2",
"advertised-router":"10.0.255.1",
}
},
{
- "edge-id":167772162,
+ "edge-id":"10.0.0.2",
"status":"Sync",
"origin":"OSPFv2",
"advertised-router":"10.0.255.2",
}
},
{
- "edge-id":167772417,
+ "edge-id":"10.0.1.1",
"status":"Sync",
"origin":"OSPFv2",
"advertised-router":"10.0.255.1",
}
},
{
- "edge-id":167772418,
+ "edge-id":"10.0.1.2",
"status":"Sync",
"origin":"OSPFv2",
"advertised-router":"10.0.255.2",
}
},
{
- "edge-id":167772929,
+ "edge-id":"10.0.3.1",
"status":"Sync",
"origin":"OSPFv2",
"advertised-router":"10.0.255.3",
}
},
{
- "edge-id":167772930,
+ "edge-id":"10.0.3.2",
"status":"Sync",
"origin":"OSPFv2",
"advertised-router":"10.0.255.2",
}
},
{
- "edge-id":167773185,
+ "edge-id":"10.0.4.1",
"status":"Sync",
"origin":"OSPFv2",
"advertised-router":"10.0.255.4",
]
},
{
- "edge-id":167773186,
+ "edge-id":"10.0.4.2",
"status":"Sync",
"origin":"OSPFv2",
"advertised-router":"10.0.255.2",
}
},
{
- "edge-id":167773441,
+ "edge-id":"10.0.5.1",
"status":"Sync",
"origin":"OSPFv2",
"advertised-router":"10.0.255.3",
],
"edges":[
{
- "edge-id":167772161,
+ "edge-id":"10.0.0.1",
"status":"Sync",
"origin":"OSPFv2",
"advertised-router":"10.0.255.1",
}
},
{
- "edge-id":167772162,
+ "edge-id":"10.0.0.2",
"status":"Sync",
"origin":"OSPFv2",
"advertised-router":"10.0.255.2",
}
},
{
- "edge-id":167772929,
+ "edge-id":"10.0.3.1",
"status":"Sync",
"origin":"OSPFv2",
"advertised-router":"10.0.255.3",
}
},
{
- "edge-id":167772930,
+ "edge-id":"10.0.3.2",
"status":"Sync",
"origin":"OSPFv2",
"advertised-router":"10.0.255.2",
}
},
{
- "edge-id":167773185,
+ "edge-id":"10.0.4.1",
"status":"Sync",
"origin":"OSPFv2",
"advertised-router":"10.0.255.4",
]
},
{
- "edge-id":167773186,
+ "edge-id":"10.0.4.2",
"status":"Sync",
"origin":"OSPFv2",
"advertised-router":"10.0.255.2",
}
},
{
- "edge-id":167773441,
+ "edge-id":"10.0.5.1",
"status":"Sync",
"origin":"OSPFv2",
"advertised-router":"10.0.255.3",
],
"edges":[
{
- "edge-id":167772161,
+ "edge-id":"10.0.0.1",
"status":"Sync",
"origin":"OSPFv2",
"advertised-router":"10.0.255.1",
}
},
{
- "edge-id":167772162,
+ "edge-id":"10.0.0.2",
"status":"Sync",
"origin":"OSPFv2",
"advertised-router":"10.0.255.2",
}
},
{
- "edge-id":167772929,
+ "edge-id":"10.0.3.1",
"status":"Sync",
"origin":"OSPFv2",
"advertised-router":"10.0.255.3",
}
},
{
- "edge-id":167772930,
+ "edge-id":"10.0.3.2",
"status":"Sync",
"origin":"OSPFv2",
"advertised-router":"10.0.255.2",
}
},
{
- "edge-id":167773185,
+ "edge-id":"10.0.4.1",
"status":"Sync",
"origin":"OSPFv2",
"advertised-router":"10.0.255.4",
]
},
{
- "edge-id":167773186,
+ "edge-id":"10.0.4.2",
"status":"Sync",
"origin":"OSPFv2",
"advertised-router":"10.0.255.2",
],
"edges":[
{
- "edge-id":167772161,
+ "edge-id":"10.0.0.1",
"status":"Sync",
"origin":"OSPFv2",
"advertised-router":"10.0.255.1",
]
},
{
- "edge-id":167772162,
+ "edge-id":"10.0.0.2",
"status":"Sync",
"origin":"OSPFv2",
"advertised-router":"10.0.255.2",
]
},
{
- "edge-id":167772929,
+ "edge-id":"10.0.3.1",
"status":"Sync",
"origin":"OSPFv2",
"advertised-router":"10.0.255.3",
}
},
{
- "edge-id":167772930,
+ "edge-id":"10.0.3.2",
"status":"Sync",
"origin":"OSPFv2",
"advertised-router":"10.0.255.2",
]
},
{
- "edge-id":167773185,
+ "edge-id":"10.0.4.1",
"status":"Sync",
"origin":"OSPFv2",
"advertised-router":"10.0.255.4",
]
},
{
- "edge-id":167773186,
+ "edge-id":"10.0.4.2",
"status":"Sync",
"origin":"OSPFv2",
"advertised-router":"10.0.255.2",
],
"edges":[
{
- "edge-id":167772161,
+ "edge-id":"10.0.0.1",
"status":"Sync",
"origin":"OSPFv2",
"advertised-router":"10.0.255.1",
]
},
{
- "edge-id":167772162,
+ "edge-id":"10.0.0.2",
"status":"Sync",
"origin":"OSPFv2",
"advertised-router":"10.0.255.2",
]
},
{
- "edge-id":167772417,
+ "edge-id":"10.0.1.1",
"status":"Sync",
"origin":"OSPFv2",
"advertised-router":"10.0.255.1",
]
},
{
- "edge-id":167772418,
+ "edge-id":"10.0.1.2",
"status":"Sync",
"origin":"OSPFv2",
"advertised-router":"10.0.255.2",
]
},
{
- "edge-id":167772929,
+ "edge-id":"10.0.3.1",
"status":"Sync",
"origin":"OSPFv2",
"advertised-router":"10.0.255.3",
}
},
{
- "edge-id":167772930,
+ "edge-id":"10.0.3.2",
"status":"Sync",
"origin":"OSPFv2",
"advertised-router":"10.0.255.2",
]
},
{
- "edge-id":167773185,
+ "edge-id":"10.0.4.1",
"status":"Sync",
"origin":"OSPFv2",
"advertised-router":"10.0.255.4",
]
},
{
- "edge-id":167773186,
+ "edge-id":"10.0.4.2",
"status":"Sync",
"origin":"OSPFv2",
"advertised-router":"10.0.255.2",
],
"edges":[
{
- "edge-id":167772161,
+ "edge-id":"10.0.0.1",
"status":"Sync",
"origin":"OSPFv2",
"advertised-router":"10.0.255.1",
]
},
{
- "edge-id":167772162,
+ "edge-id":"10.0.0.2",
"status":"Sync",
"origin":"OSPFv2",
"advertised-router":"10.0.255.2",
]
},
{
- "edge-id":167772417,
+ "edge-id":"10.0.1.1",
"status":"Sync",
"origin":"OSPFv2",
"advertised-router":"10.0.255.1",
]
},
{
- "edge-id":167772418,
+ "edge-id":"10.0.1.2",
"status":"Sync",
"origin":"OSPFv2",
"advertised-router":"10.0.255.2",
]
},
{
- "edge-id":167772929,
+ "edge-id":"10.0.3.1",
"status":"Sync",
"origin":"OSPFv2",
"advertised-router":"10.0.255.3",
}
},
{
- "edge-id":167772930,
+ "edge-id":"10.0.3.2",
"status":"Sync",
"origin":"OSPFv2",
"advertised-router":"10.0.255.2",
]
},
{
- "edge-id":167773185,
+ "edge-id":"10.0.4.1",
"status":"Sync",
"origin":"OSPFv2",
"advertised-router":"10.0.255.4",
]
},
{
- "edge-id":167773186,
+ "edge-id":"10.0.4.2",
"status":"Sync",
"origin":"OSPFv2",
"advertised-router":"10.0.255.2",
],
"edges":[
{
- "edge-id":167772161,
+ "edge-id":"10.0.0.1",
"status":"Sync",
"origin":"OSPFv2",
"advertised-router":"10.0.255.1",
]
},
{
- "edge-id":167772162,
+ "edge-id":"10.0.0.2",
"status":"Sync",
"origin":"OSPFv2",
"advertised-router":"10.0.255.2",
]
},
{
- "edge-id":167772417,
+ "edge-id":"10.0.1.1",
"status":"Sync",
"origin":"OSPFv2",
"advertised-router":"10.0.255.1",
]
},
{
- "edge-id":167772418,
+ "edge-id":"10.0.1.2",
"status":"Sync",
"origin":"OSPFv2",
"advertised-router":"10.0.255.2",
]
},
{
- "edge-id":167772929,
+ "edge-id":"10.0.3.1",
"status":"Sync",
"origin":"OSPFv2",
"advertised-router":"10.0.255.3",
}
},
{
- "edge-id":167772930,
+ "edge-id":"10.0.3.2",
"status":"Sync",
"origin":"OSPFv2",
"advertised-router":"10.0.255.2",
pytest.skip(tgen.errors)
# Api call verify whether OSPF is converged
ospf_covergence = verify_ospf6_neighbor(tgen, topo)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "setup_module :Failed \n Error: {}".format(
ospf_covergence
)
result = verify_rib(tgen, "ipv6", dut, input_dict_static_rtes, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
step(
"Configure External Route summary in R0 to summarise 5"
result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
step("Verify that show ip ospf summary should show the summaries.")
input_dict = {
result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
assert (
result is True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name)
- step("Verify that originally advertised routes are withdraw from there" " peer.")
+ step("Verify that originally advertised routes are withdraw from there peer.")
input_dict = {
"r0": {"static_routes": [{"network": NETWORK["ipv6"], "next_hop": "blackhole"}]}
}
result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
assert (
result is not True
- ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format(
tc_name, result
)
)
assert (
result is not True
- ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+ ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name)
step("Delete the configured summary")
ospf_summ_r1 = {
result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
assert (
result is not True
- ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format(
tc_name, result
)
)
assert (
result is not True
- ), "Testcase {} : Failed" "Error: Summary Route still present in RIB".format(
- tc_name
- )
+ ), "Testcase {} : Failed Error: Summary Route still present in RIB".format(tc_name)
step("show ip ospf summary should not have any summary address.")
input_dict = {
)
assert (
result is not True
- ), "Testcase {} : Failed" "Error: Summary still present in DB".format(tc_name)
+ ), "Testcase {} : Failed Error: Summary still present in DB".format(tc_name)
dut = "r1"
step("All 5 routes are advertised after deletion of configured summary.")
result = verify_rib(tgen, "ipv6", dut, input_dict_static_rtes, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
step("configure the summary again and delete static routes .")
ospf_summ_r1 = {
result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
assert (
result is True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name)
input_dict = {
"r0": {
result = verify_ospf6_rib(tgen, dut, input_dict_summary, expected=False)
assert (
result is not True
- ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format(
tc_name, result
)
)
assert (
result is not True
- ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+ ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name)
step("Add back static routes.")
input_dict_static_rtes = {
result = verify_ospf6_rib(tgen, dut, input_dict_static_rtes, expected=False)
assert (
result is not True
- ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format(
tc_name, result
)
)
assert (
result is not True
- ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+ ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name)
input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv6"][0]}]}}
dut = "r1"
result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
step("Verify that show ip ospf summary should show configure summaries.")
result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
assert (
result is True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name)
step("Configure new static route which is matching configured summary.")
input_dict_static_rtes = {
result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
step("Shut one of the interface")
intf = topo["routers"]["r0"]["links"]["r3-link0"]["interface"]
result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
assert (
result is True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name)
input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv6"][0]}]}}
result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
ospf_summ_r1 = {
"r0": {
result = verify_ospf6_rib(tgen, dut, input_dict_summary, expected=False)
assert (
result is not True
- ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format(
tc_name, result
)
)
assert (
result is not True
- ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+ ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name)
ospf_summ_r1 = {
"r0": {
result = verify_rib(tgen, "ipv6", dut, input_dict_static_rtes, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
- step(
- "Configure External Route summary in R0 to summarise 5" " routes to one route."
- )
+ step("Configure External Route summary in R0 to summarise 5 routes to one route.")
ospf_summ_r1 = {
"r0": {
"ospf6": {
result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
step("Verify that show ip ospf summary should show the summaries.")
input_dict = {
result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
assert (
result is True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name)
step("Change the summary address mask to lower match (ex - 16 to 8)")
ospf_summ_r1 = {
result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
assert (
result is True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name)
step(
"Verify that external routes(static / connected) are summarised"
result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
step("Change the summary address mask to higher match (ex - 8 to 24)")
ospf_summ_r1 = {
result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
assert (
result is True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name)
step(
"Verify that external routes(static / connected) are summarised"
result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
step(" Un configure one of the summary address.")
ospf_summ_r1 = {
result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
ospf_summ_r1 = {
"r0": {
result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
write_test_footer(tc_name)
result = verify_rib(tgen, "ipv6", dut, input_dict_static_rtes, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
- step(
- "Configure External Route summary in R0 to summarise 5" " routes to one route."
- )
+ step("Configure External Route summary in R0 to summarise 5 routes to one route.")
ospf_summ_r1 = {
"r0": {
"ospf6": {
result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
step("Verify that show ip ospf summary should show the summaries with tag.")
input_dict = {
result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
assert (
result is True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name)
step("Delete the configured summary")
ospf_summ_r1 = {
result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
assert (
result is not True
- ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format(
tc_name, result
)
)
assert (
result is not True
- ), "Testcase {} : Failed" "Error: Summary Route still present in RIB".format(
- tc_name
- )
+ ), "Testcase {} : Failed Error: Summary Route still present in RIB".format(tc_name)
step("show ip ospf summary should not have any summary address.")
input_dict = {
)
assert (
result is not True
- ), "Testcase {} : Failed" "Error: Summary still present in DB".format(tc_name)
+ ), "Testcase {} : Failed Error: Summary still present in DB".format(tc_name)
step("Configure Min tag value")
ospf_summ_r1 = {
result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
step("Verify that show ip ospf summary should show the summaries with tag.")
input_dict = {
result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
assert (
result is True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name)
step("Configure Max Tag Value")
ospf_summ_r1 = {
result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
step(
"Verify that boundary values tags are used for summary route"
result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
assert (
result is True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name)
step("configure new static route with different tag.")
input_dict_static_rtes_11 = {
)
assert (
result is not True
- ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format(
tc_name, result
)
)
assert (
result is not True
- ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+ ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name)
step(
"Verify that boundary values tags are used for summary route"
)
assert (
result is not True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name)
step("Delete the configured summary address")
ospf_summ_r1 = {
result = verify_rib(tgen, "ipv6", dut, input_dict_static_rtes, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
step("Verify that summary address is flushed from neighbor.")
result = verify_ospf6_rib(tgen, dut, input_dict_summary, expected=False)
assert (
result is not True
- ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format(
tc_name, result
)
)
assert (
result is not True
- ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+ ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name)
step("Configure summary first & then configure matching static route.")
result = verify_rib(tgen, "ipv6", dut, input_dict_static_rtes, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
- step(
- "Configure External Route summary in R0 to summarise 5" " routes to one route."
- )
+ step("Configure External Route summary in R0 to summarise 5 routes to one route.")
ospf_summ_r1 = {
"r0": {
"ospf6": {
result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
step("Verify that show ip ospf summary should show the summaries with tag.")
input_dict = {
result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
assert (
result is not True
- ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format(
tc_name, result
)
)
assert (
result is not True
- ), "Testcase {} : Failed" "Error: Summary Route still present in RIB".format(
- tc_name
- )
+ ), "Testcase {} : Failed Error: Summary Route still present in RIB".format(tc_name)
step("show ip ospf summary should not have any summary address.")
input_dict = {
)
assert (
result is not True
- ), "Testcase {} : Failed" "Error: Summary still present in DB".format(tc_name)
+ ), "Testcase {} : Failed Error: Summary still present in DB".format(tc_name)
step("Configure Min tag value")
ospf_summ_r1 = {
result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
step("Verify that show ip ospf summary should show the summaries with tag.")
input_dict = {
result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
assert (
result is True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name)
step("Configure Max Tag Value")
ospf_summ_r1 = {
result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
step(
"Verify that boundary values tags are used for summary route"
result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
assert (
result is True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name)
step("configure new static route with different tag.")
input_dict_static_rtes_11 = {
)
assert (
result is not True
- ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format(
tc_name, result
)
)
assert (
result is not True
- ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+ ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name)
step(
"Verify that boundary values tags are used for summary route"
)
assert (
result is not True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name)
step("Delete the configured summary address")
ospf_summ_r1 = {
result = verify_rib(tgen, "ipv6", dut, input_dict_static_rtes, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
step("Verify that summary address is flushed from neighbor.")
result = verify_ospf6_rib(tgen, dut, input_dict_summary, expected=False)
assert (
result is not True
- ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format(
tc_name, result
)
)
assert (
result is not True
- ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+ ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name)
step("Configure summary first & then configure matching static route.")
result = verify_rib(tgen, "ipv6", dut, input_dict_static_rtes, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
step(
"Configure External Route summary in R0 to summarise 5"
result = verify_ospf6_rib(tgen, dut, input_dict_summary, expected=False)
assert (
result is not True
- ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format(
tc_name, result
)
)
assert (
result is not True
- ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+ ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name)
- step("Verify that show ip ospf summary should show the " "configured summaries.")
+ step("Verify that show ip ospf summary should show the configured summaries.")
input_dict = {
SUMMARY["ipv6"][0]: {
"summaryAddress": SUMMARY["ipv6"][0],
result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
assert (
result is True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name)
step("Delete the configured summary")
ospf_summ_r1 = {
result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
assert (
result is not True
- ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format(
tc_name, result
)
)
assert (
result is not True
- ), "Testcase {} : Failed" "Error: Summary Route still present in RIB".format(
- tc_name
- )
+ ), "Testcase {} : Failed Error: Summary Route still present in RIB".format(tc_name)
step("show ip ospf summary should not have any summary address.")
input_dict = {
)
assert (
result is not True
- ), "Testcase {} : Failed" "Error: Summary still present in DB".format(tc_name)
+ ), "Testcase {} : Failed Error: Summary still present in DB".format(tc_name)
step("Reconfigure summary with no advertise.")
ospf_summ_r1 = {
result = verify_ospf6_rib(tgen, dut, input_dict_summary, expected=False)
assert (
result is not True
- ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format(
tc_name, result
)
)
assert (
result is not True
- ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+ ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name)
- step("Verify that show ip ospf summary should show the " "configured summaries.")
+ step("Verify that show ip ospf summary should show the configured summaries.")
input_dict = {
SUMMARY["ipv6"][0]: {
"summaryAddress": SUMMARY["ipv6"][0],
result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
assert (
result is True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name)
step(
"Change summary address from no advertise to advertise "
result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
step("Verify that show ip ospf summary should show the summaries.")
input_dict = {
result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
assert (
result is True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name)
- step("Verify that originally advertised routes are withdraw from there" " peer.")
+ step("Verify that originally advertised routes are withdraw from there peer.")
output = tgen.gears["r0"].vtysh_cmd(
"show ipv6 ospf6 database as-external json", isjson=True
)
result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
assert (
result is not True
- ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format(
tc_name, result
)
)
assert (
result is not True
- ), "Testcase {} : Failed" "Error: Routes is present in RIB".format(tc_name)
+ ), "Testcase {} : Failed Error: Routes is present in RIB".format(tc_name)
write_test_footer(tc_name)
result = verify_rib(tgen, "ipv6", dut, input_dict_static_rtes, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
- step(
- "Configure External Route summary in R0 to summarise 5" " routes to one route."
- )
+ step("Configure External Route summary in R0 to summarise 5 routes to one route.")
ospf_summ_r1 = {
"r0": {
result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
step("Verify that show ip ospf summary should show the summaries.")
input_dict = {
result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
assert (
result is True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name)
- step("Verify that originally advertised routes are withdraw from there" " peer.")
+ step("Verify that originally advertised routes are withdraw from there peer.")
input_dict = {
"r0": {"static_routes": [{"network": NETWORK["ipv6"], "next_hop": "blackhole"}]}
}
result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
assert (
result is not True
- ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format(
tc_name, result
)
)
assert (
result is not True
- ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+ ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name)
step(
"Configure route map and & rule to permit configured summary address,"
result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
input_dict = {
SUMMARY["ipv6"][0]: {
result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
assert (
result is True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name)
step("Configure metric type as 1 in route map.")
result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
assert (
result is True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name)
step("Un configure metric type from route map.")
result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
assert (
result is True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name)
step("Change rule from permit to deny in prefix list.")
pfx_list = {
result = verify_ospf6_rib(tgen, dut, input_dict_summary, expected=False)
assert (
result is not True
- ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format(
tc_name, result
)
)
assert (
result is not True
- ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+ ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name)
write_test_footer(tc_name)
result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
assert (
result is True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name)
write_test_footer(tc_name)
result = verify_rib(tgen, "ipv6", dut, input_dict_static_rtes, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
- step(
- "Configure External Route summary in R0 to summarise 5" " routes to one route."
- )
+ step("Configure External Route summary in R0 to summarise 5 routes to one route.")
ospf_summ_r1 = {
"r0": {
result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
step("Verify that show ip ospf summary should show the summaries.")
input_dict = {
result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
assert (
result is True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name)
- step("Verify that originally advertised routes are withdraw from there" " peer.")
+ step("Verify that originally advertised routes are withdraw from there peer.")
input_dict = {
"r0": {"static_routes": [{"network": NETWORK["ipv6"], "next_hop": "blackhole"}]}
}
result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
assert (
result is not True
- ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format(
tc_name, result
)
)
assert (
result is not True
- ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+ ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name)
step("Reload the FRR router")
# stop/start -> restart FRR router and verify
result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
assert (
result is True
- ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+ ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
step("Verify that show ip ospf summary should show the summaries.")
input_dict = {
result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
assert (
result is True
- ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+ ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name)
- step("Verify that originally advertised routes are withdraw from there" " peer.")
+ step("Verify that originally advertised routes are withdraw from there peer.")
input_dict = {
"r0": {"static_routes": [{"network": NETWORK["ipv6"], "next_hop": "blackhole"}]}
}
result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
assert (
result is not True
- ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format(
tc_name, result
)
)
assert (
result is not True
- ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+ ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name)
write_test_footer(tc_name)
pytest.skip(tgen.errors)
ospf6_covergence = verify_ospf6_neighbor(tgen, topo)
- assert ospf6_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf6_covergence is True, "setup_module :Failed \n Error: {}".format(
ospf6_covergence
)
ospf6_covergence = verify_ospf6_neighbor(
tgen, topo, dut=dut, expected=False, retry_timeout=3
)
- assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+ assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format(
tc_name, ospf6_covergence
)
dut = "r2"
ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
- assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format(
+ assert ospf6_covergence is True, "Testcase {} :Failed \n Error: {}".format(
tc_name, ospf6_covergence
)
ospf6_covergence = verify_ospf6_neighbor(
tgen, topo, dut=dut, expected=False, retry_timeout=5
)
- assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+ assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format(
tc_name, ospf6_covergence
)
dut = "r2"
ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
- assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format(
+ assert ospf6_covergence is True, "Testcase {} :Failed \n Error: {}".format(
tc_name, ospf6_covergence
)
"show ip ospf6 neighbor cmd."
)
ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut, expected=False)
- assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+ assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format(
tc_name, ospf6_covergence
)
dut = "r2"
ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
- assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format(
+ assert ospf6_covergence is True, "Testcase {} :Failed \n Error: {}".format(
tc_name, ospf6_covergence
)
ospf6_covergence = verify_ospf6_neighbor(
tgen, topo, dut=dut, expected=False, retry_timeout=3
)
- assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+ assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format(
tc_name, ospf6_covergence
)
dut = "r2"
ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
- assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format(
+ assert ospf6_covergence is True, "Testcase {} :Failed \n Error: {}".format(
tc_name, ospf6_covergence
)
ospf6_covergence = verify_ospf6_neighbor(
tgen, topo, dut=dut, expected=False, retry_timeout=5
)
- assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+ assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format(
tc_name, ospf6_covergence
)
dut = "r2"
ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
- assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format(
+ assert ospf6_covergence is True, "Testcase {} :Failed \n Error: {}".format(
tc_name, ospf6_covergence
)
"show ip ospf6 neighbor cmd."
)
ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut, expected=False)
- assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+ assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format(
tc_name, ospf6_covergence
)
dut = "r2"
ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
- assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format(
+ assert ospf6_covergence is True, "Testcase {} :Failed \n Error: {}".format(
tc_name, ospf6_covergence
)
ospf6_covergence = verify_ospf6_neighbor(
tgen, topo, dut=dut, expected=False, retry_timeout=3
)
- assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+ assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format(
tc_name, ospf6_covergence
)
dut = "r2"
ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
- assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format(
+ assert ospf6_covergence is True, "Testcase {} :Failed \n Error: {}".format(
tc_name, ospf6_covergence
)
ospf6_covergence = verify_ospf6_neighbor(
tgen, topo, dut=dut, expected=False, retry_timeout=5
)
- assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+ assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format(
tc_name, ospf6_covergence
)
dut = "r2"
ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
- assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format(
+ assert ospf6_covergence is True, "Testcase {} :Failed \n Error: {}".format(
tc_name, ospf6_covergence
)
"show ip ospf6 neighbor cmd."
)
ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut, expected=False)
- assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+ assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format(
tc_name, ospf6_covergence
)
dut = "r2"
ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
- assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format(
+ assert ospf6_covergence is True, "Testcase {} :Failed \n Error: {}".format(
tc_name, ospf6_covergence
)
ospf6_covergence = verify_ospf6_neighbor(
tgen, topo, dut=dut, expected=False, retry_timeout=3
)
- assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+ assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format(
tc_name, ospf6_covergence
)
dut = "r2"
ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
- assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format(
+ assert ospf6_covergence is True, "Testcase {} :Failed \n Error: {}".format(
tc_name, ospf6_covergence
)
ospf6_covergence = verify_ospf6_neighbor(
tgen, topo, dut=dut, expected=False, retry_timeout=5
)
- assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+ assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format(
tc_name, ospf6_covergence
)
dut = "r2"
ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
- assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format(
+ assert ospf6_covergence is True, "Testcase {} :Failed \n Error: {}".format(
tc_name, ospf6_covergence
)
"show ip ospf6 neighbor cmd."
)
ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut, expected=False)
- assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+ assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format(
tc_name, ospf6_covergence
)
dut = "r2"
ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
- assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format(
+ assert ospf6_covergence is True, "Testcase {} :Failed \n Error: {}".format(
tc_name, ospf6_covergence
)
ospf6_covergence = verify_ospf6_neighbor(
tgen, topo, dut=dut, expected=False, retry_timeout=3
)
- assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+ assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format(
tc_name, ospf6_covergence
)
ospf6_covergence = verify_ospf6_neighbor(
tgen, topo, dut=dut, expected=False, retry_timeout=3
)
- assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+ assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format(
tc_name, ospf6_covergence
)
dut = "r2"
ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
- assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format(
+ assert ospf6_covergence is True, "Testcase {} :Failed \n Error: {}".format(
tc_name, ospf6_covergence
)
ospf6_covergence = verify_ospf6_neighbor(
tgen, topo, dut=dut, expected=False, retry_timeout=3
)
- assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+ assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format(
tc_name, ospf6_covergence
)
ospf6_covergence = verify_ospf6_neighbor(
tgen, topo, dut=dut, expected=False, retry_timeout=3
)
- assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+ assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format(
tc_name, ospf6_covergence
)
dut = "r2"
ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
- assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format(
+ assert ospf6_covergence is True, "Testcase {} :Failed \n Error: {}".format(
tc_name, ospf6_covergence
)
ospf6_covergence = verify_ospf6_neighbor(
tgen, topo, dut=dut, expected=False, retry_timeout=3
)
- assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+ assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format(
tc_name, ospf6_covergence
)
ospf6_covergence = verify_ospf6_neighbor(
tgen, topo, dut=dut, expected=False, retry_timeout=3
)
- assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+ assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format(
tc_name, ospf6_covergence
)
dut = "r2"
ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
- assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format(
+ assert ospf6_covergence is True, "Testcase {} :Failed \n Error: {}".format(
tc_name, ospf6_covergence
)
ospf6_covergence = verify_ospf6_neighbor(
tgen, topo, dut=dut, expected=False, retry_timeout=3
)
- assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+ assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format(
tc_name, ospf6_covergence
)
ospf6_covergence = verify_ospf6_neighbor(
tgen, topo, dut=dut, expected=False, retry_timeout=3
)
- assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+ assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format(
tc_name, ospf6_covergence
)
dut = "r2"
ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
- assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format(
+ assert ospf6_covergence is True, "Testcase {} :Failed \n Error: {}".format(
tc_name, ospf6_covergence
)
ospf6_covergence = verify_ospf6_neighbor(
tgen, topo, dut=dut, expected=False, retry_timeout=3
)
- assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+ assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format(
tc_name, ospf6_covergence
)
ospf6_covergence = verify_ospf6_neighbor(
tgen, topo, dut=dut, expected=False, retry_timeout=3
)
- assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+ assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format(
tc_name, ospf6_covergence
)
dut = "r2"
ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
- assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format(
+ assert ospf6_covergence is True, "Testcase {} :Failed \n Error: {}".format(
tc_name, ospf6_covergence
)
pytest.skip(tgen.errors)
ospf_covergence = verify_ospf6_neighbor(tgen, topo)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "setup_module :Failed \n Error: {}".format(
ospf_covergence
)
step("Verify that OSPF is up with 8 neighborship sessions.")
dut = "r1"
ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error: {}".format(
ospf_covergence
)
step("Verify that OSPF is up with 8 neighborship sessions.")
dut = "r1"
ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error: {}".format(
ospf_covergence
)
step("Verify that OSPF is up with 2 neighborship sessions.")
dut = "r1"
ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error: {}".format(
ospf_covergence
)
pytest.skip(tgen.errors)
# Api call verify whether OSPF is converged
ospf_covergence = verify_ospf6_neighbor(tgen, topo, lan=True)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "setup_module :Failed \n Error: {}".format(
ospf_covergence
)
step("Verify that OSPF is up with 8 neighborship sessions.")
ospf_covergence = verify_ospf6_neighbor(tgen, topo, lan=True)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error: {}".format(
ospf_covergence
)
dut = "r0"
ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut, lan=True)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error: {}".format(
ospf_covergence
)
dut = "r2"
ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut, lan=True)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error: {}".format(
ospf_covergence
)
pytest.skip(tgen.errors)
result = verify_ospf6_neighbor(tgen, topo)
- assert result is True, "setup_module: Failed \n Error:" " {}".format(result)
+ assert result is True, "setup_module: Failed \n Error: {}".format(result)
logger.info("Running setup_module() done")
# Api call verify whether OSPF is converged
ospf_covergence = verify_ospf6_neighbor(tgen, topo)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "setup_module :Failed \n Error: {}".format(
ospf_covergence
)
result = verify_ospf6_neighbor(tgen, topo, dut="r2", expected=False)
assert (
result is not True
- ), "Testcase {} : Failed \n Nbrs are not down" "Error: {}".format(tc_name, result)
+ ), "Testcase {} : Failed \n Nbrs are not down Error: {}".format(tc_name, result)
step("Now configure area 0 on interface of r1 connecting to r2.")
result = verify_ospf6_neighbor(tgen, topo, dut="r2", expected=False)
assert (
result is not True
- ), "Testcase {} : Failed \n Nbrs are not down" "Error: {}".format(tc_name, result)
+ ), "Testcase {} : Failed \n Nbrs are not down Error: {}".format(tc_name, result)
step("Now configure area 2 on interface of r1 connecting to r2.")
result = verify_ospf6_neighbor(tgen, topo)
assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
- step("Change area 1 as non nssa area (on the fly changing area" " type on DUT).")
+ step("Change area 1 as non nssa area (on the fly changing area type on DUT).")
for rtr in ["r1", "r2", "r3"]:
input_dict = {
pytest.skip(tgen.errors)
ospf_covergence = verify_ospf6_neighbor(tgen, topo)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "setup_module :Failed \n Error: {}".format(
ospf_covergence
)
result = create_router_ospf(tgen, topo, ospf_red_r1)
assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
- step(
- "Create prefix-list in R0 to permit 10.0.20.1/32 prefix &" " deny 10.0.20.2/32"
- )
+ step("Create prefix-list in R0 to permit 10.0.20.1/32 prefix & deny 10.0.20.2/32")
# Create ip prefix list
pfx_list = {
# Api call verify whether OSPF is converged
ospf_covergence = verify_ospf6_neighbor(tgen, topo)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error: {}".format(
ospf_covergence
)
result = verify_prefix_lists(tgen, pfx_list)
assert (
result is not True
- ), "Testcase {} : Failed \n Prefix list not " "present. Error: {}".format(
+ ), "Testcase {} : Failed \n Prefix list not present. Error: {}".format(
tc_name, result
)
result = verify_prefix_lists(tgen, pfx_list)
assert (
result is not True
- ), "Testcase {} : Failed \n Prefix list not " "present. Error: {}".format(
+ ), "Testcase {} : Failed \n Prefix list not present. Error: {}".format(
tc_name, result
)
pytest.skip(tgen.errors)
ospf_covergence = verify_ospf6_neighbor(tgen, topo)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "setup_module :Failed \n Error: {}".format(
ospf_covergence
)
step("Verify that OSPF neighbors are FULL.")
ospf_covergence = verify_ospf6_neighbor(tgen, topo)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error: {}".format(
ospf_covergence
)
step("Verify that OSPF neighbours are reset and forms new adjacencies.")
# Api call verify whether OSPF is converged
ospf_covergence = verify_ospf6_neighbor(tgen, topo)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error: {}".format(
ospf_covergence
)
pytest.skip(tgen.errors)
ospf_covergence = verify_ospf6_neighbor(tgen, topo)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "setup_module :Failed \n Error: {}".format(
ospf_covergence
)
step("verify that ospf neighbours are full")
ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error: {}".format(
ospf_covergence
)
step("verify that ospf neighbours are full")
ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error: {}".format(
ospf_covergence
)
step("verify that ospf neighbours are full")
ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error: {}".format(
ospf_covergence
)
step("verify that ospf neighbours are full")
ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "Testcase Failed \n Error: {}".format(
ospf_covergence
)
result = create_interfaces_cfg(tgen, topo1)
assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
- step(
- "Verify that timer value is deleted from intf & " "set to default value 40 sec."
- )
+ step("Verify that timer value is deleted from intf & set to default value 40 sec.")
input_dict = {"r1": {"links": {"r0": {"ospf6": {"timerIntervalsConfigHello": 10}}}}}
dut = "r1"
result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict)
result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict)
assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
- step("modify dead interval from default value to r1" "dead interval timer on r2")
+ step("modify dead interval from default value to r1 dead interval timer on r2")
topo1 = {
"r0": {
# reconfiguring deleted ospf process by resetting the configs.
reset_config_on_routers(tgen)
- step("reconfigure the default dead interval timer value to " "default on r1 and r2")
+ step("reconfigure the default dead interval timer value to default on r1 and r2")
topo1 = {
"r0": {
"links": {
result = create_interfaces_cfg(tgen, topo1)
assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
- step(
- "Verify that timer value is deleted from intf & " "set to default value 40 sec."
- )
+ step("Verify that timer value is deleted from intf & set to default value 40 sec.")
input_dict = {"r1": {"links": {"r0": {"ospf6": {"timerIntervalsConfigDead": 40}}}}}
dut = "r1"
result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict)
clear_ospf(tgen, "r0", ospf="ospf6")
clear_ospf(tgen, "r1", ospf="ospf6")
- step(
- "Verify that OSPF neighborship between R0 and R1 is stuck in Exstart" " State."
- )
+ step("Verify that OSPF neighborship between R0 and R1 is stuck in Exstart State.")
result = verify_ospf6_neighbor(tgen, topo, expected=False)
assert result is not True, (
"Testcase {} : Failed \n OSPF nbrs are Full "
"instead of Exstart. Error: {}".format(tc_name, result)
)
- step(
- "Verify that configured MTU value is updated in the show ip " "ospf interface."
- )
+ step("Verify that configured MTU value is updated in the show ip ospf interface.")
dut = "r0"
input_dict = {"r0": {"links": {"r1": {"ospf6": {"interfaceMtu": 1400}}}}}
clear_ospf(tgen, "r0", ospf="ospf6")
- step(
- "Verify that OSPF neighborship between R0 and R1 is stuck in Exstart" " State."
- )
+ step("Verify that OSPF neighborship between R0 and R1 is stuck in Exstart State.")
result = verify_ospf6_neighbor(tgen, topo, expected=False)
assert result is not True, (
"Testcase {} : Failed \n OSPF nbrs are Full "
result = verify_ospf6_neighbor(tgen, topo)
assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
- step(
- "Configure ospf interface with jumbo MTU (9216)." "Reset ospf neighbors on R0."
- )
+ step("Configure ospf interface with jumbo MTU (9216). Reset ospf neighbors on R0.")
rtr0.run("ifconfig {} mtu 9216".format(r0_r1_intf))
rtr1.run("ifconfig {} mtu 9216".format(r1_r0_intf))
result = create_debug_log_config(tgen, input_dict)
- # Code coverage steps #Do Not upstream
- input_dict_config = {
- "r1": {
- "raw_config": [
- "end",
- "debug ospf6 event",
- "debug ospf6 gr helper",
- "debug ospf6 ism events",
- "debug ospf6 ism status",
- "debug ospf6 ism timers",
- "debug ospf6 nsm events",
- "debug ospf6 nsm status",
- "debug ospf6 nsm timers ",
- "debug ospf6 nssa",
- "debug ospf6 lsa aggregate",
- "debug ospf6 lsa flooding ",
- "debug ospf6 lsa generate",
- "debug ospf6 lsa install ",
- "debug ospf6 lsa refresh",
- "debug ospf6 packet all detail",
- "debug ospf6 packet all recv",
- "debug ospf6 packet all send",
- "debug ospf6 packet dd detail",
- "debug ospf6 packet dd recv",
- "debug ospf6 packet dd send ",
- "debug ospf6 packet hello detail",
- "debug ospf6 packet hello recv",
- "debug ospf6 packet hello send",
- "debug ospf6 packet ls-ack detail",
- "debug ospf6 packet ls-ack recv",
- "debug ospf6 packet ls-ack send",
- "debug ospf6 packet ls-request detail",
- "debug ospf6 packet ls-request recv",
- "debug ospf6 packet ls-request send",
- "debug ospf6 packet ls-update detail",
- "debug ospf6 packet ls-update recv",
- "debug ospf6 packet ls-update send",
- "debug ospf6 sr",
- "debug ospf6 te ",
- "debug ospf6 zebra interface",
- "debug ospf6 zebra redistribute",
- ]
- }
- }
-
- apply_raw_config(tgen, input_dict_config)
-
for rtr in topo["routers"]:
clear_ospf(tgen, rtr, ospf="ospf6")
clear_ospf(tgen, rtr, ospf="ospf6")
ospf_covergence = verify_ospf6_neighbor(tgen, topo1)
- assert ospf_covergence is True, "OSPF NBRs not up.Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "OSPF NBRs not up.Failed \n Error: {}".format(
ospf_covergence
)
assert result is True, "Testcase : Failed \n Error: {}".format(result)
ospf_covergence = verify_ospf6_neighbor(tgen, topo, expected=False)
- assert (
- ospf_covergence is not True
- ), "OSPF NBRs are up.Failed \n Error:" " {}".format(ospf_covergence)
+ assert ospf_covergence is not True, "OSPF NBRs are up.Failed \n Error: {}".format(
+ ospf_covergence
+ )
topo1 = {}
topo1 = deepcopy(topo)
topo1["routers"]["r3"]["ospf6"]["router_id"] = "1.1.1.4"
ospf_covergence = verify_ospf6_neighbor(tgen, topo1)
- assert ospf_covergence is True, "OSPF NBRs not up.Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "OSPF NBRs not up.Failed \n Error: {}".format(
ospf_covergence
)
reset_config_on_routers(tgen)
ospf_covergence = verify_ospf6_neighbor(tgen, topo)
- assert ospf_covergence is True, "OSPF NBRs not up.Failed \n Error:" " {}".format(
+ assert ospf_covergence is True, "OSPF NBRs not up.Failed \n Error: {}".format(
ospf_covergence
)
"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
}
]
}
"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"
}
]
"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"
}
]
# Skip pytests example directory
[pytest]
+asyncio_mode = auto
+
# We always turn this on inside conftest.py, default shown
# addopts = --junitxml=<rundir>/topotests.xml
junit_logging = all
junit_log_passing_tests = true
-norecursedirs = .git example_test example_topojson_test lib docker
+norecursedirs = .git example_munet example_test example_topojson_test lib munet docker
# Directory to store test results and run logs in, default shown
# rundir = /tmp/topotests
--- /dev/null
+!
+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
--- /dev/null
+!
+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
+
--- /dev/null
+!
+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
+
--- /dev/null
+#!/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))
--- /dev/null
+bfd
+ profile slow
+ receive-interval 1000
+ transmit-interval 1000
+ exit
+exit
--- /dev/null
+interface r1-eth0
+ ip rip bfd
+ ip rip bfd profile slow
+exit
+!
+interface r1-eth1
+ ip rip bfd
+ ip rip bfd profile slow
+exit
+!
+router rip
+ allow-ecmp
+ network 192.168.0.1/24
+ network 192.168.1.1/24
+ redistribute connected
+ timers basic 10 40 30
+exit
--- /dev/null
+interface r1-eth0
+ ip address 192.168.0.1/24
+exit
+!
+interface r1-eth1
+ ip address 192.168.1.1/24
+exit
+!
+interface lo
+ ip address 10.254.254.1/32
+exit
--- /dev/null
+bfd
+ profile slow
+ receive-interval 1000
+ transmit-interval 1000
+ exit
+exit
--- /dev/null
+interface r2-eth0
+ ip rip bfd
+exit
+!
+router rip
+ bfd default-profile slow
+ network 192.168.0.2/24
+ redistribute connected
+ redistribute static
+ timers basic 10 40 30
+exit
--- /dev/null
+ip route 10.254.254.100/32 lo
--- /dev/null
+interface r2-eth0
+ ip address 192.168.0.2/24
+exit
+!
+interface lo
+ ip address 10.254.254.2/32
+exit
+
--- /dev/null
+bfd
+ profile slow
+ receive-interval 1000
+ transmit-interval 1000
+ exit
+exit
--- /dev/null
+interface r3-eth0
+ ip rip bfd
+ ip rip bfd profile slow
+exit
+!
+router rip
+ network 192.168.1.2/24
+ redistribute connected
+ redistribute static
+ timers basic 10 40 30
+exit
--- /dev/null
+ip route 10.254.254.100/32 lo
--- /dev/null
+interface r3-eth0
+ ip address 192.168.1.2/24
+exit
+!
+interface lo
+ ip address 10.254.254.3/32
+exit
--- /dev/null
+## Color coding:
+#########################
+## Main FRR: #f08080 red
+## Switches: #d0e0d0 gray
+## RIP: #19e3d9 Cyan
+## RIPng: #fcb314 dark yellow
+## OSPFv2: #32b835 Green
+## OSPFv3: #19e3d9 Cyan
+## ISIS IPv4 #fcb314 dark yellow
+## ISIS IPv6 #9a81ec purple
+## BGP IPv4 #eee3d3 beige
+## BGP IPv6 #fdff00 yellow
+##### Colors (see http://www.color-hex.com/)
+
+graph template {
+ label="rip_bfd_topo1";
+
+ # Routers
+ r1 [
+ shape=doubleoctagon,
+ label="r1",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ r2 [
+ shape=doubleoctagon
+ label="r2",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ r3 [
+ shape=doubleoctagon
+ label="r3",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+
+ # Switches
+ s1 [
+ shape=oval,
+ label="s1\n192.168.0.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s2 [
+ shape=oval,
+ label="s1\n192.168.1.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+
+ # Connections
+ r1 -- s1 [label="r1-eth0\n.1"];
+ r2 -- s1 [label="r2-eth0\n.2"];
+
+ r1 -- s2 [label="r1-eth1\n.1"];
+ r3 -- s2 [label="r1-eth0\n.2"];
+}
--- /dev/null
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_rip_bfd_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2023 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+"""
+test_rip_bfd_topo1.py: Test RIP BFD integration.
+"""
+
+import sys
+import re
+import pytest
+
+from functools import partial
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter
+from lib.topolog import logger
+
+pytestmark = [
+ pytest.mark.bfdd,
+ pytest.mark.ripd,
+]
+
+
+@pytest.fixture(scope="module")
+def tgen(request):
+ "Setup/Teardown the environment and provide tgen argument to tests"
+
+ topodef = {
+ "s1": ("r1", "r2"),
+ "s2": ("r1", "r3")
+ }
+ tgen = Topogen(topodef, request.module.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for router_name, router in router_list.items():
+ router.load_config(TopoRouter.RD_BFD, "bfdd.conf")
+ router.load_config(TopoRouter.RD_RIP, "ripd.conf")
+ router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf")
+ if router_name in ["r2", "r3"]:
+ router.load_config(TopoRouter.RD_STATIC, "staticd.conf")
+
+ tgen.start_router()
+ yield tgen
+ tgen.stop_topology()
+
+
+@pytest.fixture(autouse=True)
+def skip_on_failure(tgen):
+ "Test if routers is still running otherwise skip tests"
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of previous test failure")
+
+
+def show_rip_json(router):
+ "Get router 'show ip rip' JSON output"
+ output = router.vtysh_cmd("show ip rip")
+ routes = output.splitlines()[6:]
+ json = {}
+
+ for route in routes:
+ match = re.match(
+ r"(.)\((.)\)\s+([^\s]+)\s+([^\s]+)\s+([^\s]+)\s+([^\s]+)", route)
+ if match is None:
+ continue
+
+ route_entry = {
+ "code": match[1],
+ "subCode": match[2],
+ "nextHop": match[4],
+ "metric": int(match[5]),
+ "from": match[6],
+ }
+
+ if json.get(match[3]) is None:
+ json[match[3]] = []
+
+ json[match[3]].append(route_entry)
+
+ return json
+
+
+def expect_routes(router, routes, time_amount):
+ "Expect 'routes' in 'router'."
+
+ def test_function():
+ "Internal test function."
+ return topotest.json_cmp(show_rip_json(router), routes)
+
+ _, result = topotest.run_and_expect(test_function,
+ None,
+ count=time_amount,
+ wait=1)
+ assert result is None, "Unexpected routing table in {}".format(
+ router.name)
+
+
+def expect_bfd_peers(router, peers):
+ "Expect 'peers' in 'router' BFD status."
+ test_func = partial(
+ topotest.router_json_cmp,
+ router,
+ "show bfd peers json",
+ peers,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=20, wait=1)
+ assert result is None, "{} BFD peer status mismatch".format(router)
+
+
+def test_rip_convergence(tgen):
+ "Test that RIP learns the neighbor routes."
+
+ expect_routes(
+ tgen.gears["r1"], {
+ "10.254.254.2/32": [{
+ "code": "R",
+ "subCode": "n",
+ "from": "192.168.0.2"
+ }],
+ "10.254.254.3/32": [{
+ "code": "R",
+ "subCode": "n",
+ "from": "192.168.1.2"
+ }],
+ "10.254.254.100/32": [{
+ "code": "R",
+ "subCode": "n",
+ "from": "192.168.0.2",
+ }, {
+ "code": "R",
+ "subCode": "n",
+ "from": "192.168.1.2",
+ }]
+ }, 40)
+
+ expect_bfd_peers(tgen.gears["r1"], [{
+ "peer": "192.168.0.2",
+ "status": "up",
+ "receive-interval": 1000,
+ "transmit-interval": 1000,
+ }, {
+ "peer": "192.168.1.2",
+ "status": "up",
+ "receive-interval": 1000,
+ "transmit-interval": 1000,
+ }])
+
+ expect_routes(
+ tgen.gears["r2"], {
+ "10.254.254.1/32": [{
+ "code": "R",
+ "subCode": "n",
+ "from": "192.168.0.1"
+ }],
+ "10.254.254.3/32": [{
+ "code": "R",
+ "subCode": "n",
+ "from": "192.168.0.1"
+ }],
+ "10.254.254.100/32": [{
+ "code": "S",
+ "subCode": "r",
+ "from": "self"
+ }]
+ }, 40)
+
+ expect_bfd_peers(tgen.gears["r2"], [{
+ "peer": "192.168.0.1",
+ "status": "up",
+ "receive-interval": 1000,
+ "transmit-interval": 1000,
+ }])
+
+ expect_routes(
+ tgen.gears["r3"], {
+ "10.254.254.1/32": [{
+ "code": "R",
+ "subCode": "n",
+ "from": "192.168.1.1"
+ }],
+ "10.254.254.2/32": [{
+ "code": "R",
+ "subCode": "n",
+ "from": "192.168.1.1"
+ }],
+ "10.254.254.100/32": [{
+ "code": "S",
+ "subCode": "r",
+ "from": "self"
+ }]
+ }, 40)
+
+ expect_bfd_peers(tgen.gears["r3"], [{
+ "peer": "192.168.1.1",
+ "status": "up",
+ "receive-interval": 1000,
+ "transmit-interval": 1000,
+ }])
+
+
+def test_rip_bfd_convergence(tgen):
+ "Test that RIP drop the gone neighbor routes."
+
+ tgen.gears["r3"].link_enable("r3-eth0", False)
+
+ expect_routes(
+ tgen.gears["r1"], {
+ "10.254.254.2/32": [{
+ "code": "R",
+ "subCode": "n",
+ "from": "192.168.0.2"
+ }],
+ "10.254.254.3/32": None,
+ "10.254.254.100/32": [{
+ "code": "R",
+ "subCode": "n",
+ "from": "192.168.0.2",
+ }]
+ }, 6)
+
+ expect_routes(
+ tgen.gears["r3"], {
+ "10.254.254.1/32": None,
+ "10.254.254.2/32": None,
+ "10.254.254.100/32": [{
+ "code": "S",
+ "subCode": "r",
+ "from": "self"
+ }]
+ }, 6)
+
+
+def test_memory_leak(tgen):
+ "Run the memory leak test and report results."
+
+ 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))
--- /dev/null
+!
+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
--- /dev/null
+!
+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
+
--- /dev/null
+!
+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
+
--- /dev/null
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright (c) 2023 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+"""
+Test if RIP `passive-interface default` and `no passive-interface IFNAME`
+combination works as expected.
+"""
+
+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.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_passive_interface():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r1 = tgen.gears["r1"]
+ r2 = tgen.gears["r2"]
+
+ def _show_routes(nh_num):
+ output = json.loads(r1.vtysh_cmd("show ip route 10.10.10.1/32 json"))
+ expected = {
+ "10.10.10.1/32": [
+ {
+ "internalNextHopNum": nh_num,
+ "internalNextHopActiveNum": nh_num,
+ }
+ ]
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_show_routes, 2)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assert result is None, "Got 10.10.10.1/32, but the next-hop count is not 2"
+
+ step("Configure `passive-interface default` on r2")
+ r2.vtysh_cmd(
+ """
+ configure terminal
+ router rip
+ passive-interface default
+ """
+ )
+
+ test_func = functools.partial(_show_routes, 1)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assert result is None, "Got 10.10.10.1/32, but the next-hop count is not 1"
+
+ step("Configure `no passive-interface r2-eth0` on r2 towards r1")
+ r2.vtysh_cmd(
+ """
+ configure terminal
+ router rip
+ no passive-interface r2-eth0
+ """
+ )
+
+ test_func = functools.partial(_show_routes, 2)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assert result is None, "Got 10.10.10.1/32, but the next-hop count is not 2"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
r1-eth3
Routing Information Sources:
Gateway BadPackets BadRoutes Distance Last Update
- 193.1.1.2 0 0 120 XX:XX:XX
+ 193.1.1.2 0 0 120 XX:XX:XX
Distance: (default is 120)
193.1.2.0/24
Routing Information Sources:
Gateway BadPackets BadRoutes Distance Last Update
- 193.1.1.1 0 0 120 XX:XX:XX
- 193.1.2.2 0 0 120 XX:XX:XX
+ 193.1.1.1 0 0 120 XX:XX:XX
+ 193.1.2.2 0 0 120 XX:XX:XX
Distance: (default is 120)
193.1.2.0/24
Routing Information Sources:
Gateway BadPackets BadRoutes Distance Last Update
- 193.1.2.1 0 0 120 XX:XX:XX
+ 193.1.2.1 0 0 120 XX:XX:XX
Distance: (default is 120)
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()
--- /dev/null
+;;; Directory Local Variables
+;;; For more information see (info "(emacs) Directory Variables")
+;;; Match project coding conventions
+
+((c-mode . ((indent-tabs-mode . t)
+ (show-trailing-whitespace . t)
+ (c-basic-offset . 8)))
+ (json-mode . ((js-indent-level 4))))
# 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:
}
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:
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:
*/
#include <zebra.h>
+#include <getopt.h>
+
#include <lib/version.h>
#include "lib/command.h"
#include "lib/filter.h"
-#include "lib/getopt.h"
#include "lib/if.h"
#include "lib/libfrr.h"
#include "lib/log.h"
/* VTY should add timestamp */
bool vtysh_add_timestamp;
-/* VTY shell client structure */
-struct vtysh_client {
- int fd;
- const char *name;
- int flag;
- char path[MAXPATHLEN];
- struct vtysh_client *next;
-
- struct event *log_reader;
- int log_fd;
- uint32_t lost_msgs;
-};
-
static bool stderr_tty;
static bool stderr_stdout_same;
/* --- */
+/*
+ * When updating this array, remember to change the array size here and in
+ * vtysh.h
+ */
struct vtysh_client vtysh_client[] = {
{.name = "mgmtd", .flag = VTYSH_MGMTD},
{.name = "zebra", .flag = VTYSH_ZEBRA},
.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
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
{
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
int ret;
const char *fname = argv[1]->arg;
- ret = vtysh_read_config(fname, true);
+ ret = vtysh_apply_config(fname, true, false);
/* Return to enable mode - the 'read_config' api leaves us up a level */
vtysh_execute_no_pager("enable");
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 */
int vtysh_mark_file(const char *filename);
-int vtysh_read_config(const char *filename, bool dry_run);
+int vtysh_apply_config(const char *config_file_path, bool dry_run, bool fork);
int vtysh_write_config_integrated(void);
void vtysh_config_parse_line(void *, const char *);
extern bool vtysh_add_timestamp;
+struct vtysh_client {
+ int fd;
+ const char *name;
+ int flag;
+ char path[MAXPATHLEN];
+ struct vtysh_client *next;
+
+ struct event *log_reader;
+ int log_fd;
+ uint32_t lost_msgs;
+};
+
+extern struct vtysh_client vtysh_client[22];
+
#endif /* VTYSH_H */
*/
#include <zebra.h>
+#include <sys/wait.h>
#include "command.h"
#include "linklist.h"
return (ret);
}
-/* Read up configuration file from config_default_dir. */
-int vtysh_read_config(const char *config_default_dir, bool dry_run)
+/*
+ * Read configuration file and send it to all connected daemons
+ */
+static int vtysh_read_config(const char *config_file_path, bool dry_run)
{
FILE *confp = NULL;
bool save;
int ret;
- confp = fopen(config_default_dir, "r");
+ confp = fopen(config_file_path, "r");
if (confp == NULL) {
fprintf(stderr,
"%% Can't open configuration file %s due to '%s'.\n",
- config_default_dir, safe_strerror(errno));
+ config_file_path, safe_strerror(errno));
return CMD_ERR_NO_FILE;
}
vtysh_add_timestamp = save;
- return (ret);
+ return ret;
+}
+
+int vtysh_apply_config(const char *config_file_path, bool dry_run, bool do_fork)
+{
+ /*
+ * We need to apply the whole config file to all daemons. Instead of
+ * having one client talk to N daemons, we fork N times and let each
+ * child handle one daemon.
+ */
+ pid_t fork_pid = getpid();
+ int status = 0;
+ int ret;
+ int my_client_type;
+ char my_client[64];
+
+ if (do_fork) {
+ for (unsigned int i = 0; i < array_size(vtysh_client); i++) {
+ /* Store name of client this fork will handle */
+ strlcpy(my_client, vtysh_client[i].name,
+ sizeof(my_client));
+ my_client_type = vtysh_client[i].flag;
+ fork_pid = fork();
+
+ /* If child, break */
+ if (fork_pid == 0)
+ break;
+ }
+
+ /* parent, wait for children */
+ if (fork_pid != 0) {
+ int keep_status = 0;
+
+ fprintf(stdout,
+ "Waiting for children to finish applying config...\n");
+ while (wait(&status) > 0) {
+ if (!keep_status && WEXITSTATUS(status))
+ keep_status = WEXITSTATUS(status);
+ }
+
+ /*
+ * This will return the first status received
+ * that failed( if that happens ). This is
+ * good enough for the moment
+ */
+ return keep_status;
+ }
+
+ /*
+ * children, grow up to be cowboys
+ */
+ for (unsigned int i = 0; i < array_size(vtysh_client); i++) {
+ if (my_client_type != vtysh_client[i].flag) {
+ struct vtysh_client *cl;
+
+ /*
+ * If this is a client we aren't responsible
+ * for, disconnect
+ */
+ for (cl = &vtysh_client[i]; cl; cl = cl->next) {
+ if (cl->fd >= 0)
+ close(cl->fd);
+ cl->fd = -1;
+ }
+ } else if (vtysh_client[i].fd == -1 &&
+ vtysh_client[i].next == NULL) {
+ /*
+ * If this is the client we are responsible
+ * for, but we aren't already connected to that
+ * client, that means the client isn't up in
+ * the first place and we can exit early
+ */
+ exit(0);
+ }
+ }
+
+ fprintf(stdout, "[%d|%s] sending configuration\n", getpid(),
+ my_client);
+ }
+
+ ret = vtysh_read_config(config_file_path, dry_run);
+
+ if (ret) {
+ if (do_fork)
+ fprintf(stderr,
+ "[%d|%s] Configuration file[%s] processing failure: %d\n",
+ getpid(), my_client, frr_config, ret);
+ else
+ fprintf(stderr,
+ "Configuration file[%s] processing failure: %d\n",
+ frr_config, ret);
+ } else if (do_fork) {
+ fprintf(stderr, "[%d|%s] done\n", getpid(), my_client);
+ exit(0);
+ }
+
+ return ret;
}
/* We don't write vtysh specific into file from vtysh. vtysh.conf should
#include <sys/un.h>
#include <setjmp.h>
-#include <sys/wait.h>
#include <pwd.h>
#include <sys/file.h>
#include <unistd.h>
"-u --user Run as an unprivileged user\n"
"-w, --writeconfig Write integrated config (frr.conf) and exit\n"
"-H, --histfile Override history file\n"
+ "-t, --timestamp Print a timestamp before going to shell or reading the configuration\n"
+ " --no-fork Don't fork clients to handle daemons (slower for large configs)\n"
"-h, --help Display this help and exit\n\n"
"Note that multiple commands may be executed from the command\n"
"line by passing multiple -c args, or by embedding linefeed\n"
/* VTY shell options, we use GNU getopt library. */
#define OPTION_VTYSOCK 1000
#define OPTION_CONFDIR 1001
+#define OPTION_NOFORK 1002
struct option longopts[] = {
{"boot", no_argument, NULL, 'b'},
/* For compatibility with older zebra/quagga versions */
{"pathspace", required_argument, NULL, 'N'},
{"user", no_argument, NULL, 'u'},
{"timestamp", no_argument, NULL, 't'},
+ {"no-fork", no_argument, NULL, OPTION_NOFORK},
{0}};
bool vtysh_loop_exited;
int dryrun = 0;
int boot_flag = 0;
bool ts_flag = false;
+ bool no_fork = false;
const char *daemon_name = NULL;
const char *inputfile = NULL;
struct cmd_rec {
ditch_suid = 1; /* option disables SUID */
snprintf(sysconfdir, sizeof(sysconfdir), "%s/", optarg);
break;
+ case OPTION_NOFORK:
+ no_fork = true;
+ break;
case 'N':
if (strchr(optarg, '/') || strchr(optarg, '.')) {
fprintf(stderr,
}
}
+ /* No need for forks if we're talking to 1 daemon */
+ if (daemon_name)
+ no_fork = true;
+
if (ditch_suid) {
elevuid = realuid;
elevgid = realgid;
/* Read vtysh configuration file before connecting to daemons.
* (file may not be readable to calling user in SUID mode) */
suid_on();
- vtysh_read_config(vtysh_config, dryrun);
+ vtysh_apply_config(vtysh_config, dryrun, false);
suid_off();
}
/* Error code library system */
/* Start execution only if not in dry-run mode */
if (dryrun && !cmd) {
if (inputfile) {
- ret = vtysh_read_config(inputfile, dryrun);
+ ret = vtysh_apply_config(inputfile, dryrun, false);
} else {
- ret = vtysh_read_config(frr_config, dryrun);
+ ret = vtysh_apply_config(frr_config, dryrun, false);
}
exit(ret);
return vtysh_write_config_integrated();
}
- if (inputfile) {
+ if (boot_flag)
+ inputfile = frr_config;
+
+ if (inputfile || boot_flag) {
vtysh_flock_config(inputfile);
- ret = vtysh_read_config(inputfile, dryrun);
+ ret = vtysh_apply_config(inputfile, dryrun, !no_fork);
vtysh_unflock_config();
+
+ if (no_error)
+ ret = 0;
+
exit(ret);
}
exit(0);
}
- /* Boot startup configuration file. */
- if (boot_flag) {
- vtysh_flock_config(frr_config);
- ret = vtysh_read_config(frr_config, dryrun);
- vtysh_unflock_config();
- if (ret) {
- fprintf(stderr,
- "Configuration file[%s] processing failure: %d\n",
- frr_config, ret);
- if (no_error)
- exit(0);
- else
- exit(ret);
- } else
- exit(0);
- }
-
vtysh_readline_init();
vty_hello(vty);
"Set BGP extended community attribute";
}
+ identity set-extcommunity-nt {
+ base frr-route-map:rmap-set-type;
+ description
+ "Set BGP extended community attribute";
+ }
+
identity set-extcommunity-soo {
base frr-route-map:rmap-set-type;
description
}
}
+ case extcommunity-nt {
+ when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:action, 'frr-bgp-route-map:set-extcommunity-nt')";
+ description
+ "Value of the ext-community";
+ leaf extcommunity-nt {
+ type string;
+ description
+ "Set BGP ext-community node-target attribute";
+ }
+ }
+
case extcommunity-soo {
when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:action, 'frr-bgp-route-map:set-extcommunity-soo')";
description
--- /dev/null
+// 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";
+ }
+ }
+ }
+ }
+}
"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";
"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
}
}
+ 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.";
}
}
}
+ 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 {
import ietf-yang-types {
prefix yang;
}
+ import frr-if-rmap {
+ prefix frr-if-rmap;
+ }
+ import frr-bfdd {
+ prefix frr-bfdd;
+ }
import frr-interface {
prefix frr-interface;
}
+ import frr-nexthop {
+ prefix frr-nexthop;
+ }
import frr-vrf {
prefix frr-vrf;
}
description
"Changed interface references to use
frr-interface:interface-ref typedef";
+ reference "FRRouting";
}
revision 2017-12-06 {
description
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
"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;
is 0.";
}
}
+
+ uses frr-if-rmap:if-route-maps-group;
+
leaf-list static-route {
type inet:ipv4-prefix;
description
}
}
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;
"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;
"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.";
}
}
+ leaf default-bfd-profile {
+ description
+ "Use this BFD profile for all peers by default.";
+ type frr-bfdd:profile-ref;
+ }
+
/*
* Operational data.
*/
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.";
- }
}
}
}
* Per-interface configuration data
*/
augment "/frr-interface:lib/frr-interface:interface" {
+ description "rip interface data";
container rip {
description
"RIP interface parameters.";
"Key-chain name.";
}
}
+
+ container bfd-monitoring {
+ presence
+ "Present if BFD is configured for RIP peers in this interface.";
+
+ leaf enable {
+ type boolean;
+ description
+ "Enable/disable BFD monitoring.";
+ default false;
+ }
+
+ leaf profile {
+ type frr-bfdd:profile-ref;
+ description
+ "BFD profile to use.";
+ }
+ }
}
}
import ietf-yang-types {
prefix yang;
}
+ import frr-if-rmap {
+ prefix frr-if-rmap;
+ }
import frr-interface {
prefix frr-interface;
}
is 0.";
}
}
+
+ uses frr-if-rmap:if-route-maps-group;
+
leaf-list static-route {
type inet:ipv6-prefix;
description
"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
}
}
+ 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 {
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
zd_dpdk_vty_init();
frr_with_privs (&zserv_privs) {
- rc = rte_eal_init(ARRAY_SIZE(argv), argv);
+ rc = rte_eal_init(array_size(argv), argv);
}
if (rc < 0) {
zlog_warn("EAL init failed %s", rte_strerror(rte_errno));
zlog_debug("RTM_DELLINK for %s(%u)", name,
ifp->ifindex);
- UNSET_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK);
-
if (IS_ZEBRA_IF_BOND(ifp))
zebra_l2if_update_bond(ifp, false);
if (IS_ZEBRA_IF_BOND_SLAVE(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);
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;
if (ifp->vrf->vrf_id && !vrf_is_backend_netns())
if_handle_vrf_change(ifp, VRF_DEFAULT);
+ UNSET_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK);
+
/* Reset some zebra interface params to default values. */
zif = ifp->info;
if (zif) {
/* This is to issue an UPDATE or a DELETE, as appropriate. */
zebra_interface_vrf_update_del(ifp, vrf_id);
+ if (if_is_vrf(ifp))
+ return;
+
/* update VRF */
if_update_to_new_vrf(ifp, vrf_id);
{
struct zebra_if *zif;
struct interface *link_if;
- struct zebra_vrf *zvrf = ifp->vrf->info;
zif = ifp->info;
zif->up_count++;
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)) {
{
struct zebra_if *zif;
struct interface *link_if;
- struct zebra_vrf *zvrf = ifp->vrf->info;
zif = ifp->info;
zif->down_count++;
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)) {
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);
/* 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,
struct list *mac_list;
/* Link fields - for sub-interfaces. */
+ ns_id_t link_nsid;
ifindex_t link_ifindex;
struct interface *link;
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 *);
if (zrouter.lsp_process_q)
work_queue_free_and_null(&zrouter.lsp_process_q);
- vrf_terminate();
-
- ns_walk_func(zebra_ns_early_shutdown, NULL, NULL);
- zebra_ns_notify_close();
-
access_list_reset();
prefix_list_reset();
/*
*/
zebra_routemap_finish();
+ rib_update_finish();
+
list_delete(&zrouter.client_list);
/* Indicate that all new dplane work has been enqueued. When that
{
zlog_info("Zebra final shutdown");
+ vrf_terminate();
+
+ ns_walk_func(zebra_ns_early_shutdown, NULL, NULL);
+ zebra_ns_notify_close();
+
/* Stop dplane thread and finish any cleanup */
zebra_dplane_shutdown();
* 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 */
RIB_UPDATE_OTHER,
RIB_UPDATE_MAX
};
+void rib_update_finish(void);
int route_entry_update_nhe(struct route_entry *re,
struct nhg_hash_entry *new_nhghe);
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;
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;
}
atomic_fetch_add_explicit(
&zdplane_info.dg_gre_set_errors, 1,
memory_order_relaxed);
- if (ctx)
- dplane_ctx_free(&ctx);
+ dplane_ctx_free(&ctx);
result = ZEBRA_DPLANE_REQUEST_FAILURE;
}
return result;
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 */
char flag_buf[MACIP_BUF_SIZE];
zlog_debug(
- "Send MACIP %s f %s MAC %pEA IP %pIA seq %u L2-VNI %u ESI %s to %s",
+ "Send MACIP %s f %s state %u MAC %pEA IP %pIA seq %u L2-VNI %u ESI %s to %s",
(cmd == ZEBRA_MACIP_ADD) ? "Add" : "Del",
zclient_evpn_dump_macip_flags(flags, flag_buf,
sizeof(flag_buf)),
- macaddr, ip, seq, vni,
- es ? es->esi_str : "-",
+ state, macaddr, ip, seq, vni, es ? es->esi_str : "-",
zebra_route_string(client->proto));
}
int zebra_evpn_mac_send_del_to_client(vni_t vni, const struct ethaddr *macaddr,
uint32_t flags, bool force)
{
+ int state = ZEBRA_NEIGH_ACTIVE;
+
if (!force) {
if (CHECK_FLAG(flags, ZEBRA_MAC_LOCAL_INACTIVE)
&& !CHECK_FLAG(flags, ZEBRA_MAC_ES_PEER_ACTIVE))
/* the host was not advertised - nothing to delete */
return 0;
+
+ /* MAC is LOCAL and DUP_DETECTED, this local mobility event
+ * is not known to bgpd. Upon receiving local delete
+ * ask bgp to reinstall the best route (remote entry).
+ */
+ if (CHECK_FLAG(flags, ZEBRA_MAC_LOCAL) &&
+ CHECK_FLAG(flags, ZEBRA_MAC_DUPLICATE))
+ state = ZEBRA_NEIGH_INACTIVE;
}
return zebra_evpn_macip_send_msg_to_client(
- vni, macaddr, NULL, 0 /* flags */, 0 /* seq */,
- ZEBRA_NEIGH_ACTIVE, NULL, ZEBRA_MACIP_DEL);
+ vni, macaddr, NULL, 0 /* flags */, 0 /* seq */, state, NULL,
+ ZEBRA_MACIP_DEL);
}
/*
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;
*/
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);
if (es_change) {
inform_bgp = true;
inform_dataplane = true;
+ mac_inactive = true;
}
/* if peer-flag is being set notify dataplane that the
* 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;
/* Remove MAC from BGP. */
zebra_evpn_mac_send_del_to_client(zevpn->vni, &mac->macaddr, mac->flags,
- false /* force */);
+ clear_static /* force */);
zebra_evpn_es_mac_deref_entry(mac);
json_array_string_add(json_flags, "local");
if (es->flags & ZEBRA_EVPNES_REMOTE)
json_array_string_add(json_flags, "remote");
+ if (es->flags & ZEBRA_EVPNES_LOCAL &&
+ !(es->flags & ZEBRA_EVPNES_NON_DF))
+ json_array_string_add(json_flags, "df");
if (es->flags & ZEBRA_EVPNES_NON_DF)
json_array_string_add(json_flags, "nonDF");
if (es->flags & ZEBRA_EVPNES_BYPASS)
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.
*/
zlog_debug(msg, ##__VA_ARGS__); \
} while (0)
-
/*
* Client connection functions
*/
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);
}
}
}
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;
}
/*
* 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);
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);
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",
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;
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 */
/* 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;
/* 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
*/
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 */
__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);
}
}
/*
* 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)
__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);
}
/*
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__,
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;
}
*/
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;
* 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);
+}
(protocol) == ZEBRA_ROUTE_OSPF6 || (protocol) == ZEBRA_ROUTE_ISIS || \
(protocol) == ZEBRA_ROUTE_PIM || \
(protocol) == ZEBRA_ROUTE_OPENFABRIC || \
- (protocol) == ZEBRA_ROUTE_STATIC)
+ (protocol) == ZEBRA_ROUTE_STATIC || (protocol) == ZEBRA_ROUTE_RIP)
void zebra_ptm_init(void);
void zebra_ptm_finish(void);
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,
META_QUEUE_NOTBGP,
META_QUEUE_BGP,
META_QUEUE_OTHER,
+ META_QUEUE_GR_RUN,
};
/* Each route type's string and default distance value. */
return "BGP Routes";
case META_QUEUE_OTHER:
return "Other Routes";
+ case META_QUEUE_GR_RUN:
+ return "Graceful Restart";
}
return "Unknown";
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.
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);
}
}
+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;
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]);
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;
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,
*/
static struct event *t_rib_update_threads[RIB_UPDATE_MAX];
+void rib_update_finish(void)
+{
+ int i;
+
+ for (i = RIB_UPDATE_KERNEL; i < RIB_UPDATE_MAX; i++) {
+ if (event_is_scheduled(t_rib_update_threads[i])) {
+ struct rib_update_ctx *ctx;
+
+ ctx = EVENT_ARG(t_rib_update_threads[i]);
+
+ rib_update_ctx_fini(&ctx);
+ EVENT_OFF(t_rib_update_threads[i]);
+ }
+ }
+}
+
/* Schedule a RIB update event for all vrfs */
void rib_update(enum rib_update_event event)
{
if (event_is_scheduled(t_rib_update_threads[event]))
return;
+ if (zebra_router_in_shutdown())
+ return;
+
ctx = rib_update_ctx_init(0, event);
event_add_event(zrouter.master, rib_update_handler, ctx, 0,
seg6local_context2str(buf, sizeof(buf),
&nexthop->nh_srv6->seg6local_ctx,
nexthop->nh_srv6->seg6local_action);
- vty_out(vty, ", seg6local %s %s",
- seg6local_action2str(
- nexthop->nh_srv6->seg6local_action),
- buf);
- vty_out(vty, ", seg6 %pI6", &nexthop->nh_srv6->seg6_segs);
+ if (nexthop->nh_srv6->seg6local_action !=
+ ZEBRA_SEG6_LOCAL_ACTION_UNSPEC)
+ vty_out(vty, ", seg6local %s %s",
+ seg6local_action2str(
+ nexthop->nh_srv6->seg6local_action),
+ buf);
+ if (IPV6_ADDR_CMP(&nexthop->nh_srv6->seg6_segs, &in6addr_any))
+ vty_out(vty, ", seg6 %pI6",
+ &nexthop->nh_srv6->seg6_segs);
}
if (nexthop->weight)
}
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);
}
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);
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);
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);
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;
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",
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;
if (!num_rmacs)
return;
- if (use_json)
- json = json_object_new_object();
-
memset(&wctx, 0, sizeof(wctx));
wctx.vty = vty;
wctx.json = 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,
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);
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,
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);
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;
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.
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;
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;
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;
vni);
return;
}
- if (use_json)
- json = json_object_new_object();
zebra_evpn_print_neigh(n, vty, json);
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;
if (!num_neigh)
return;
- if (use_json)
- json = json_object_new_object();
-
memset(&wctx, 0, sizeof(wctx));
wctx.zevpn = zevpn;
wctx.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;
}
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.
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)
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;
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;
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;
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;
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",
return;
}
- if (use_json)
- json = json_object_new_object();
-
zebra_evpn_print_mac(mac, vty, json);
+
if (use_json)
vty_json(vty, json);
}
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)
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;
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();
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",
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");
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)
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");
/* 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;
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;