diff options
239 files changed, 9796 insertions, 4221 deletions
diff --git a/.dir-locals.el b/.dir-locals.el index 1332f7b6a2..b2d7cf376d 100644 --- a/.dir-locals.el +++ b/.dir-locals.el @@ -5,6 +5,4 @@ ((c-mode . ((indent-tabs-mode . t) (show-trailing-whitespace . t) (c-basic-offset . 8))) - (json-mode . ((js-indent-level 4))) - (python-mode . ((python-formatter . black) - (python-fill-column . 88)))) + (json-mode . ((js-indent-level 4)))) diff --git a/.gitignore b/.gitignore index 40f6475a26..fb40ee52fe 100644 --- a/.gitignore +++ b/.gitignore @@ -113,3 +113,5 @@ refix .emacs.desktop* /test-suite.log +pceplib/test/*.log +pceplib/test/*.trs diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index f1c953f21d..7de7a6628f 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -530,6 +530,12 @@ static uint32_t srv6_l3vpn_hash_key_make(const void *p) key = jhash(&l3vpn->sid, 16, key); key = jhash_1word(l3vpn->sid_flags, key); key = jhash_1word(l3vpn->endpoint_behavior, key); + key = jhash_1word(l3vpn->loc_block_len, key); + key = jhash_1word(l3vpn->loc_node_len, key); + key = jhash_1word(l3vpn->func_len, key); + key = jhash_1word(l3vpn->arg_len, key); + key = jhash_1word(l3vpn->transposition_len, key); + key = jhash_1word(l3vpn->transposition_offset, key); return key; } @@ -540,7 +546,13 @@ static bool srv6_l3vpn_hash_cmp(const void *p1, const void *p2) return sid_same(&l3vpn1->sid, &l3vpn2->sid) && l3vpn1->sid_flags == l3vpn2->sid_flags - && l3vpn1->endpoint_behavior == l3vpn2->endpoint_behavior; + && l3vpn1->endpoint_behavior == l3vpn2->endpoint_behavior + && l3vpn1->loc_block_len == l3vpn2->loc_block_len + && l3vpn1->loc_node_len == l3vpn2->loc_node_len + && l3vpn1->func_len == l3vpn2->func_len + && l3vpn1->arg_len == l3vpn2->arg_len + && l3vpn1->transposition_len == l3vpn2->transposition_len + && l3vpn1->transposition_offset == l3vpn2->transposition_offset; } static bool srv6_l3vpn_same(const struct bgp_attr_srv6_l3vpn *h1, @@ -691,6 +703,8 @@ unsigned int attrhash_key_make(const void *p) key = jhash(attr->mp_nexthop_local.s6_addr, IPV6_MAX_BYTELEN, key); MIX3(attr->nh_ifindex, attr->nh_lla_ifindex, attr->distance); MIX(attr->rmap_table_id); + MIX(attr->nh_type); + MIX(attr->bh_type); return key; } @@ -747,7 +761,9 @@ bool attrhash_cmp(const void *p1, const void *p2) && attr1->distance == attr2->distance && srv6_l3vpn_same(attr1->srv6_l3vpn, attr2->srv6_l3vpn) && srv6_vpn_same(attr1->srv6_vpn, attr2->srv6_vpn) - && attr1->srte_color == attr2->srte_color) + && attr1->srte_color == attr2->srte_color + && attr1->nh_type == attr2->nh_type + && attr1->bh_type == attr2->bh_type) return true; } @@ -2529,6 +2545,172 @@ static int bgp_attr_encap(uint8_t type, struct peer *peer, /* IN */ return 0; } + +/* SRv6 Service Data Sub-Sub-TLV attribute + * draft-ietf-bess-srv6-services-07 + */ +static bgp_attr_parse_ret_t +bgp_attr_srv6_service_data(struct bgp_attr_parser_args *args) +{ + struct peer *const peer = args->peer; + struct attr *const attr = args->attr; + uint8_t type, loc_block_len, loc_node_len, func_len, arg_len, + transposition_len, transposition_offset; + uint16_t length; + size_t headersz = sizeof(type) + sizeof(length); + + if (STREAM_READABLE(peer->curr) < headersz) { + flog_err( + EC_BGP_ATTR_LEN, + "Malformed SRv6 Service Data Sub-Sub-TLV attribute - insufficent data (need %zu for attribute header, have %zu remaining in UPDATE)", + headersz, STREAM_READABLE(peer->curr)); + return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, + args->total); + } + + type = stream_getc(peer->curr); + length = stream_getw(peer->curr); + + if (STREAM_READABLE(peer->curr) < length) { + flog_err( + EC_BGP_ATTR_LEN, + "Malformed SRv6 Service Data Sub-Sub-TLV attribute - insufficent data (need %hu for attribute data, have %zu remaining in UPDATE)", + length, STREAM_READABLE(peer->curr)); + return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, + args->total); + } + + if (type == BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_STRUCTURE) { + loc_block_len = stream_getc(peer->curr); + loc_node_len = stream_getc(peer->curr); + func_len = stream_getc(peer->curr); + arg_len = stream_getc(peer->curr); + transposition_len = stream_getc(peer->curr); + transposition_offset = stream_getc(peer->curr); + + /* Log SRv6 Service Data Sub-Sub-TLV */ + if (BGP_DEBUG(vpn, VPN_LEAK_LABEL)) { + zlog_debug( + "%s: srv6-l3-srv-data loc-block-len=%u, loc-node-len=%u func-len=%u, arg-len=%u, transposition-len=%u, transposition-offset=%u", + __func__, loc_block_len, loc_node_len, func_len, + arg_len, transposition_len, + transposition_offset); + } + + attr->srv6_l3vpn->loc_block_len = loc_block_len; + attr->srv6_l3vpn->loc_node_len = loc_node_len; + attr->srv6_l3vpn->func_len = func_len; + attr->srv6_l3vpn->arg_len = arg_len; + attr->srv6_l3vpn->transposition_len = transposition_len; + attr->srv6_l3vpn->transposition_offset = transposition_offset; + } + + else { + if (bgp_debug_update(peer, NULL, NULL, 1)) + zlog_debug( + "%s attr SRv6 Service Data Sub-Sub-TLV sub-sub-type=%u is not supported, skipped", + peer->host, type); + + stream_forward_getp(peer->curr, length); + } + + return BGP_ATTR_PARSE_PROCEED; +} + +/* SRv6 Service Sub-TLV attribute + * draft-ietf-bess-srv6-services-07 + */ +static bgp_attr_parse_ret_t +bgp_attr_srv6_service(struct bgp_attr_parser_args *args) +{ + struct peer *const peer = args->peer; + struct attr *const attr = args->attr; + struct in6_addr ipv6_sid; + uint8_t type, sid_flags; + uint16_t length, endpoint_behavior; + size_t headersz = sizeof(type) + sizeof(length); + bgp_attr_parse_ret_t err; + char buf[BUFSIZ]; + + if (STREAM_READABLE(peer->curr) < headersz) { + flog_err( + EC_BGP_ATTR_LEN, + "Malformed SRv6 Service Sub-TLV attribute - insufficent data (need %zu for attribute header, have %zu remaining in UPDATE)", + headersz, STREAM_READABLE(peer->curr)); + return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, + args->total); + } + + type = stream_getc(peer->curr); + length = stream_getw(peer->curr); + + if (STREAM_READABLE(peer->curr) < length) { + flog_err( + EC_BGP_ATTR_LEN, + "Malformed SRv6 Service Sub-TLV attribute - insufficent data (need %hu for attribute data, have %zu remaining in UPDATE)", + length, STREAM_READABLE(peer->curr)); + return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, + args->total); + } + + if (type == BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_INFO) { + stream_getc(peer->curr); + stream_get(&ipv6_sid, peer->curr, sizeof(ipv6_sid)); + sid_flags = stream_getc(peer->curr); + endpoint_behavior = stream_getw(peer->curr); + stream_getc(peer->curr); + + /* Log SRv6 Service Sub-TLV */ + if (BGP_DEBUG(vpn, VPN_LEAK_LABEL)) { + inet_ntop(AF_INET6, &ipv6_sid, buf, sizeof(buf)); + zlog_debug( + "%s: srv6-l3-srv sid %s, sid-flags 0x%02x, end-behaviour 0x%04x", + __func__, buf, sid_flags, endpoint_behavior); + } + + /* Configure from Info */ + if (attr->srv6_l3vpn) { + flog_err(EC_BGP_ATTRIBUTE_REPEATED, + "Prefix SID SRv6 L3VPN field repeated"); + return bgp_attr_malformed( + args, BGP_NOTIFY_UPDATE_MAL_ATTR, args->total); + } + attr->srv6_l3vpn = XCALLOC(MTYPE_BGP_SRV6_L3VPN, + sizeof(struct bgp_attr_srv6_l3vpn)); + sid_copy(&attr->srv6_l3vpn->sid, &ipv6_sid); + attr->srv6_l3vpn->sid_flags = sid_flags; + attr->srv6_l3vpn->endpoint_behavior = endpoint_behavior; + attr->srv6_l3vpn->loc_block_len = 0; + attr->srv6_l3vpn->loc_node_len = 0; + attr->srv6_l3vpn->func_len = 0; + attr->srv6_l3vpn->arg_len = 0; + attr->srv6_l3vpn->transposition_len = 0; + attr->srv6_l3vpn->transposition_offset = 0; + + // Sub-Sub-TLV found + if (length > BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_INFO_LENGTH) { + err = bgp_attr_srv6_service_data(args); + + if (err != BGP_ATTR_PARSE_PROCEED) + return err; + } + + attr->srv6_l3vpn = srv6_l3vpn_intern(attr->srv6_l3vpn); + } + + /* Placeholder code for unsupported type */ + else { + if (bgp_debug_update(peer, NULL, NULL, 1)) + zlog_debug( + "%s attr SRv6 Service Sub-TLV sub-type=%u is not supported, skipped", + peer->host, type); + + stream_forward_getp(peer->curr, length); + } + + return BGP_ATTR_PARSE_PROCEED; +} + /* * Read an individual SID value returning how much data we have read * Returns 0 if there was an error that needs to be passed up the stack @@ -2544,7 +2726,6 @@ static bgp_attr_parse_ret_t bgp_attr_psid_sub(uint8_t type, uint16_t length, uint32_t srgb_range; int srgb_count; uint8_t sid_type, sid_flags; - uint16_t endpoint_behavior; char buf[BUFSIZ]; if (type == BGP_PREFIX_SID_LABEL_INDEX) { @@ -2699,45 +2880,20 @@ static bgp_attr_parse_ret_t bgp_attr_psid_sub(uint8_t type, uint16_t length, /* Placeholder code for the SRv6 L3 Service type */ else if (type == BGP_PREFIX_SID_SRV6_L3_SERVICE) { - if (STREAM_READABLE(peer->curr) < length - || length != BGP_PREFIX_SID_SRV6_L3_SERVICE_LENGTH) { - flog_err(EC_BGP_ATTR_LEN, - "Prefix SID SRv6 L3-Service length is %hu instead of %u", - length, BGP_PREFIX_SID_SRV6_L3_SERVICE_LENGTH); + if (STREAM_READABLE(peer->curr) < length) { + 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); } - /* Parse L3-SERVICE Sub-TLV */ - stream_getc(peer->curr); /* reserved */ - stream_get(&ipv6_sid, peer->curr, - sizeof(ipv6_sid)); /* sid_value */ - sid_flags = stream_getc(peer->curr); /* sid_flags */ - endpoint_behavior = stream_getw(peer->curr); /* endpoint */ - stream_getc(peer->curr); /* reserved */ - - /* Log L3-SERVICE Sub-TLV */ - if (BGP_DEBUG(vpn, VPN_LEAK_LABEL)) { - inet_ntop(AF_INET6, &ipv6_sid, buf, sizeof(buf)); - zlog_debug( - "%s: srv6-l3-srv sid %s, sid-flags 0x%02x, end-behaviour 0x%04x", - __func__, buf, sid_flags, endpoint_behavior); - } + /* ignore reserved */ + stream_getc(peer->curr); - /* Configure from Info */ - if (attr->srv6_l3vpn) { - flog_err(EC_BGP_ATTRIBUTE_REPEATED, - "Prefix SID SRv6 L3VPN field repeated"); - return bgp_attr_malformed( - args, BGP_NOTIFY_UPDATE_MAL_ATTR, args->total); - } - attr->srv6_l3vpn = XCALLOC(MTYPE_BGP_SRV6_L3VPN, - sizeof(struct bgp_attr_srv6_l3vpn)); - attr->srv6_l3vpn->sid_flags = sid_flags; - attr->srv6_l3vpn->endpoint_behavior = endpoint_behavior; - sid_copy(&attr->srv6_l3vpn->sid, &ipv6_sid); - attr->srv6_l3vpn = srv6_l3vpn_intern(attr->srv6_l3vpn); + return bgp_attr_srv6_service(args); } /* Placeholder code for Unsupported TLV */ @@ -4119,18 +4275,39 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer, /* SRv6 Service Information Attribute. */ if ((afi == AFI_IP || afi == AFI_IP6) && safi == SAFI_MPLS_VPN) { if (attr->srv6_l3vpn) { + uint8_t subtlv_len = + BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_STRUCTURE_LENGTH + + BGP_ATTR_MIN_LEN + + BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_INFO_LENGTH; + uint8_t tlv_len = subtlv_len + BGP_ATTR_MIN_LEN + 1; + uint8_t attr_len = tlv_len + BGP_ATTR_MIN_LEN; stream_putc(s, BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS); stream_putc(s, BGP_ATTR_PREFIX_SID); - stream_putc(s, 24); /* tlv len */ + stream_putc(s, attr_len); stream_putc(s, BGP_PREFIX_SID_SRV6_L3_SERVICE); - stream_putw(s, 21); /* sub-tlv len */ + stream_putw(s, tlv_len); + stream_putc(s, 0); /* reserved */ + stream_putc(s, BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_INFO); + stream_putw(s, subtlv_len); stream_putc(s, 0); /* reserved */ stream_put(s, &attr->srv6_l3vpn->sid, sizeof(attr->srv6_l3vpn->sid)); /* sid */ stream_putc(s, 0); /* sid_flags */ stream_putw(s, 0xffff); /* endpoint */ stream_putc(s, 0); /* reserved */ + stream_putc( + s, + BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_STRUCTURE); + stream_putw( + s, + BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_STRUCTURE_LENGTH); + stream_putc(s, attr->srv6_l3vpn->loc_block_len); + stream_putc(s, attr->srv6_l3vpn->loc_node_len); + stream_putc(s, attr->srv6_l3vpn->func_len); + stream_putc(s, attr->srv6_l3vpn->arg_len); + stream_putc(s, attr->srv6_l3vpn->transposition_len); + stream_putc(s, attr->srv6_l3vpn->transposition_offset); } else if (attr->srv6_vpn) { stream_putc(s, BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS); diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h index a583581030..3573c2ae03 100644 --- a/bgpd/bgp_attr.h +++ b/bgpd/bgp_attr.h @@ -71,7 +71,22 @@ #define BGP_PREFIX_SID_IPV6_LENGTH 19 #define BGP_PREFIX_SID_ORIGINATOR_SRGB_LENGTH 6 #define BGP_PREFIX_SID_VPN_SID_LENGTH 19 -#define BGP_PREFIX_SID_SRV6_L3_SERVICE_LENGTH 21 + +/* SRv6 Service Sub-TLV types */ +#define BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_INFO 1 +#define BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_INFO_LENGTH 21 + +/* SRv6 Service Data Sub-Sub-TLV types */ +#define BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_STRUCTURE 1 +#define BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_STRUCTURE_LENGTH 6 + +/* SRv6 SID Structure default values */ +#define BGP_PREFIX_SID_SRV6_LOCATOR_BLOCK_LENGTH 40 +#define BGP_PREFIX_SID_SRV6_LOCATOR_NODE_LENGTH 24 +#define BGP_PREFIX_SID_SRV6_FUNCTION_LENGTH 16 +#define BGP_PREFIX_SID_SRV6_ARGUMENT_LENGTH 0 +#define BGP_PREFIX_SID_SRV6_TRANSPOSITION_LENGTH 16 +#define BGP_PREFIX_SID_SRV6_TRANSPOSITION_OFFSET 64 #define BGP_ATTR_NH_AFI(afi, attr) \ ((afi != AFI_L2VPN) ? afi : \ @@ -136,6 +151,12 @@ struct bgp_attr_srv6_l3vpn { uint8_t sid_flags; uint16_t endpoint_behavior; struct in6_addr sid; + uint8_t loc_block_len; + uint8_t loc_node_len; + uint8_t func_len; + uint8_t arg_len; + uint8_t transposition_len; + uint8_t transposition_offset; }; /* BGP core attribute structure. */ @@ -307,6 +328,12 @@ struct attr { /* EVPN DF preference and algorithm for DF election on local ESs */ uint16_t df_pref; uint8_t df_alg; + + /* Nexthop type */ + enum nexthop_types_t nh_type; + + /* If NEXTHOP_TYPE_BLACKHOLE, then blackhole type */ + enum blackhole_type bh_type; }; /* rmap_change_flags definition */ diff --git a/bgpd/bgp_conditional_adv.c b/bgpd/bgp_conditional_adv.c index 329bd3d696..82eb8a815e 100644 --- a/bgpd/bgp_conditional_adv.c +++ b/bgpd/bgp_conditional_adv.c @@ -49,9 +49,9 @@ bgp_check_rmap_prefixes_in_bgp_table(struct bgp_table *table, RESET_FLAG(dummy_attr.rmap_change_flags); ret = route_map_apply(rmap, dest_p, &path); - if (ret != RMAP_PERMITMATCH) - bgp_attr_flush(&dummy_attr); - else { + bgp_attr_flush(&dummy_attr); + + if (ret == RMAP_PERMITMATCH) { bgp_dest_unlock_node(dest); if (BGP_DEBUG(update, UPDATE_OUT)) zlog_debug( @@ -84,6 +84,7 @@ static void bgp_conditional_adv_routes(struct peer *peer, afi_t afi, struct update_subgroup *subgrp; struct attr dummy_attr = {0}, attr = {0}; struct bgp_path_info_extra path_extra = {0}; + route_map_result_t ret; paf = peer_af_find(peer, afi, safi); if (!paf) @@ -114,11 +115,11 @@ static void bgp_conditional_adv_routes(struct peer *peer, afi_t afi, RESET_FLAG(dummy_attr.rmap_change_flags); - if (route_map_apply(rmap, dest_p, &path) - != RMAP_PERMITMATCH) { - bgp_attr_flush(&dummy_attr); + ret = route_map_apply(rmap, dest_p, &path); + bgp_attr_flush(&dummy_attr); + + if (ret != RMAP_PERMITMATCH) continue; - } if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED) || (addpath_capable diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index cbd29c146a..6248ad927b 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -6048,10 +6048,12 @@ bool bgp_evpn_is_prefix_nht_supported(const struct prefix *pfx) * type-5 routes. It may be tweaked later on for other routes, or * even removed completely when all routes are handled. */ - if (pfx && pfx->family == AF_EVPN && - (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE || - evp->prefix.route_type == BGP_EVPN_IMET_ROUTE || - evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE)) + if (pfx && pfx->family == AF_EVPN + && (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE + || evp->prefix.route_type == BGP_EVPN_AD_ROUTE + || evp->prefix.route_type == BGP_EVPN_ES_ROUTE + || evp->prefix.route_type == BGP_EVPN_IMET_ROUTE + || evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE)) return true; return false; diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index 2bda5dbf9a..aced0177ea 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -358,7 +358,7 @@ static void bgp_evpn_show_route_header(struct vty *vty, struct bgp *bgp, "Status codes: s suppressed, d damped, h history, * valid, > best, i - internal\n"); vty_out(vty, "Origin codes: i - IGP, e - EGP, ? - incomplete\n"); vty_out(vty, - "EVPN type-1 prefix: [1]:[ESI]:[EthTag]:[IPlen]:[VTEP-IP]\n"); + "EVPN type-1 prefix: [1]:[EthTag]:[ESI]:[IPlen]:[VTEP-IP]\n"); vty_out(vty, "EVPN type-2 prefix: [2]:[EthTag]:[MAClen]:[MAC]:[IPlen]:[IP]\n"); vty_out(vty, "EVPN type-3 prefix: [3]:[EthTag]:[IPlen]:[OrigIP]\n"); @@ -396,8 +396,6 @@ static void display_l3vni(struct vty *vty, struct bgp *bgp_vrf, originator_ip, sizeof(originator_ip))); json_object_string_add(json, "advertiseGatewayMacip", "n/a"); json_object_string_add(json, "advertiseSviMacIp", "n/a"); - json_object_to_json_string_ext(json, - JSON_C_TO_STRING_NOSLASHESCAPE); json_object_string_add(json, "advertisePip", bgp_vrf->evpn_info->advertise_pip ? "Enabled" : "Disabled"); @@ -967,8 +965,6 @@ static void show_l3vni_entry(struct vty *vty, struct bgp *bgp, json_object_string_add(json_vni, "advertiseGatewayMacip", "n/a"); json_object_string_add(json_vni, "advertiseSviMacIp", "n/a"); - json_object_to_json_string_ext(json_vni, - JSON_C_TO_STRING_NOSLASHESCAPE); json_object_string_add( json_vni, "advertisePip", bgp->evpn_info->advertise_pip ? "Enabled" : "Disabled"); @@ -2727,7 +2723,7 @@ static void evpn_show_route_rd(struct vty *vty, struct bgp *bgp, /* RD header and legend - once overall. */ if (rd_header && !json) { vty_out(vty, - "EVPN type-1 prefix: [1]:[ESI]:[EthTag]:[IPlen]:[VTEP-IP]\n"); + "EVPN type-1 prefix: [1]:[EthTag]:[ESI]:[IPlen]:[VTEP-IP]\n"); vty_out(vty, "EVPN type-2 prefix: [2]:[EthTag]:[MAClen]:[MAC]\n"); vty_out(vty, @@ -4413,8 +4409,11 @@ DEFUN(show_bgp_l2vpn_evpn_vni, } if (uj) { - vty_out(vty, "%s\n", json_object_to_json_string_ext( - json, JSON_C_TO_STRING_PRETTY)); + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + json, + JSON_C_TO_STRING_PRETTY + | JSON_C_TO_STRING_NOSLASHESCAPE)); json_object_free(json); } diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index 1af2ab384f..659029b04c 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -522,13 +522,14 @@ static bool sid_exist(struct bgp *bgp, const struct in6_addr *sid) * if index != 0: try to allocate as index-mode * else: try to allocate as auto-mode */ -static bool alloc_new_sid(struct bgp *bgp, uint32_t index, - struct in6_addr *sid) +static uint32_t alloc_new_sid(struct bgp *bgp, uint32_t index, + struct in6_addr *sid) { struct listnode *node; struct prefix_ipv6 *chunk; struct in6_addr sid_buf; bool alloced = false; + int label; if (!bgp || !sid) return false; @@ -536,7 +537,8 @@ static bool alloc_new_sid(struct bgp *bgp, uint32_t index, for (ALL_LIST_ELEMENTS_RO(bgp->srv6_locator_chunks, node, chunk)) { sid_buf = chunk->prefix; if (index != 0) { - sid_buf.s6_addr[15] = index; + label = index << 12; + transpose_sid(&sid_buf, label, 64, 16); if (sid_exist(bgp, &sid_buf)) return false; alloced = true; @@ -544,9 +546,8 @@ static bool alloc_new_sid(struct bgp *bgp, uint32_t index, } for (size_t i = 1; i < 255; i++) { - sid_buf.s6_addr[15] = (i & 0xff00) >> 8; - sid_buf.s6_addr[14] = (i & 0x00ff); - + label = i << 12; + transpose_sid(&sid_buf, label, 64, 16); if (sid_exist(bgp, &sid_buf)) continue; alloced = true; @@ -555,20 +556,19 @@ static bool alloc_new_sid(struct bgp *bgp, uint32_t index, } if (!alloced) - return false; + return 0; sid_register(bgp, &sid_buf, bgp->srv6_locator_name); *sid = sid_buf; - return true; + return label; } void ensure_vrf_tovpn_sid(struct bgp *bgp_vpn, struct bgp *bgp_vrf, afi_t afi) { int debug = BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF); - bool alloced = false; char buf[256]; struct in6_addr *sid; - uint32_t tovpn_sid_index = 0; + uint32_t tovpn_sid_index = 0, tovpn_sid_transpose_label; bool tovpn_sid_auto = false; if (debug) @@ -602,8 +602,9 @@ void ensure_vrf_tovpn_sid(struct bgp *bgp_vpn, struct bgp *bgp_vrf, afi_t afi) } sid = XCALLOC(MTYPE_BGP_SRV6_SID, sizeof(struct in6_addr)); - alloced = alloc_new_sid(bgp_vpn, tovpn_sid_index, sid); - if (!alloced) { + tovpn_sid_transpose_label = + alloc_new_sid(bgp_vpn, tovpn_sid_index, sid); + if (tovpn_sid_transpose_label == 0) { zlog_debug("%s: not allocated new sid for vrf %s: afi %s", __func__, bgp_vrf->name_pretty, afi2str(afi)); return; @@ -615,9 +616,22 @@ void ensure_vrf_tovpn_sid(struct bgp *bgp_vpn, struct bgp *bgp_vrf, afi_t afi) __func__, buf, bgp_vrf->name_pretty, afi2str(afi)); } + bgp_vrf->vpn_policy[afi].tovpn_sid_transpose_label = + tovpn_sid_transpose_label; bgp_vrf->vpn_policy[afi].tovpn_sid = sid; } +void transpose_sid(struct in6_addr *sid, uint32_t label, uint8_t offset, + uint8_t len) +{ + for (uint8_t idx = 0; idx < len; idx++) { + uint8_t tidx = offset + idx; + sid->s6_addr[tidx / 8] &= ~(0x1 << (7 - tidx % 8)); + if (label >> (19 - idx) & 0x1) + sid->s6_addr[tidx / 8] |= 0x1 << (7 - tidx % 8); + } +} + static bool ecom_intersect(struct ecommunity *e1, struct ecommunity *e2) { uint32_t i, j; @@ -710,10 +724,19 @@ static void setsids(struct bgp_path_info *bpi, extra = bgp_path_info_extra_get(bpi); for (i = 0; i < num_sids; i++) - memcpy(&extra->sid[i], &sid[i], sizeof(struct in6_addr)); + memcpy(&extra->sid[i].sid, &sid[i], sizeof(struct in6_addr)); extra->num_sids = num_sids; } +static void unsetsids(struct bgp_path_info *bpi) +{ + struct bgp_path_info_extra *extra; + + extra = bgp_path_info_extra_get(bpi); + extra->num_sids = 0; + memset(extra->sid, 0, sizeof(extra->sid)); +} + /* * returns pointer to new bgp_path_info upon success */ @@ -729,6 +752,7 @@ leak_update(struct bgp *bgp, /* destination bgp instance */ struct bgp_path_info *bpi; struct bgp_path_info *bpi_ultimate; struct bgp_path_info *new; + struct bgp_path_info_extra *extra; uint32_t num_sids = 0; if (new_attr->srv6_l3vpn || new_attr->srv6_vpn) @@ -815,13 +839,35 @@ leak_update(struct bgp *bgp, /* destination bgp instance */ * rewrite sid */ if (num_sids) { - if (new_attr->srv6_l3vpn) + if (new_attr->srv6_l3vpn) { setsids(bpi, &new_attr->srv6_l3vpn->sid, num_sids); - else if (new_attr->srv6_vpn) + + extra = bgp_path_info_extra_get(bpi); + + extra->sid[0].loc_block_len = + new_attr->srv6_l3vpn->loc_block_len; + extra->sid[0].loc_node_len = + new_attr->srv6_l3vpn->loc_node_len; + extra->sid[0].func_len = + new_attr->srv6_l3vpn->func_len; + extra->sid[0].arg_len = + new_attr->srv6_l3vpn->arg_len; + + if (new_attr->srv6_l3vpn->transposition_len + != 0) + transpose_sid( + &extra->sid[0].sid, + decode_label(label), + new_attr->srv6_l3vpn + ->transposition_offset, + new_attr->srv6_l3vpn + ->transposition_len); + } else if (new_attr->srv6_vpn) setsids(bpi, &new_attr->srv6_vpn->sid, num_sids); - } + } else + unsetsids(bpi); if (nexthop_self_flag) bgp_path_info_set_flag(bn, bpi, BGP_PATH_ANNC_NH_SELF); @@ -847,6 +893,17 @@ leak_update(struct bgp *bgp, /* destination bgp instance */ nh_valid = bgp_find_or_add_nexthop( bgp, bgp_nexthop, afi, safi, bpi, NULL, 0, p); + /* + * If you are using SRv6 VPN instead of MPLS, it need to check + * the SID allocation. If the sid is not allocated, the rib + * will be invalid. + */ + if (bgp->srv6_enabled + && (!new_attr->srv6_l3vpn && !new_attr->srv6_vpn)) { + bgp_path_info_unset_flag(bn, bpi, BGP_PATH_VALID); + nh_valid = false; + } + if (debug) zlog_debug("%s: nexthop is %svalid (in vrf %s)", __func__, (nh_valid ? "" : "not "), @@ -889,11 +946,29 @@ leak_update(struct bgp *bgp, /* destination bgp instance */ * rewrite sid */ if (num_sids) { - if (new_attr->srv6_l3vpn) + if (new_attr->srv6_l3vpn) { setsids(new, &new_attr->srv6_l3vpn->sid, num_sids); - else if (new_attr->srv6_vpn) + + extra = bgp_path_info_extra_get(new); + + extra->sid[0].loc_block_len = + new_attr->srv6_l3vpn->loc_block_len; + extra->sid[0].loc_node_len = + new_attr->srv6_l3vpn->loc_node_len; + extra->sid[0].func_len = new_attr->srv6_l3vpn->func_len; + extra->sid[0].arg_len = new_attr->srv6_l3vpn->arg_len; + + if (new_attr->srv6_l3vpn->transposition_len != 0) + transpose_sid(&extra->sid[0].sid, + decode_label(label), + new_attr->srv6_l3vpn + ->transposition_offset, + new_attr->srv6_l3vpn + ->transposition_len); + } else if (new_attr->srv6_vpn) setsids(new, &new_attr->srv6_vpn->sid, num_sids); - } + } else + unsetsids(new); if (num_labels) setlabels(new, label, num_labels); @@ -933,6 +1008,17 @@ leak_update(struct bgp *bgp, /* destination bgp instance */ nh_valid = bgp_find_or_add_nexthop(bgp, bgp_nexthop, afi, safi, new, NULL, 0, p); + /* + * If you are using SRv6 VPN instead of MPLS, it need to check + * the SID allocation. If the sid is not allocated, the rib + * will be invalid. + */ + if (bgp->srv6_enabled + && (!new->attr->srv6_l3vpn && !new->attr->srv6_vpn)) { + bgp_path_info_unset_flag(bn, new, BGP_PATH_VALID); + nh_valid = false; + } + if (debug) zlog_debug("%s: nexthop is %svalid (in vrf %s)", __func__, (nh_valid ? "" : "not "), @@ -1153,10 +1239,24 @@ void vpn_leak_from_vrf_update(struct bgp *bgp_vpn, /* to */ /* Set SID for SRv6 VPN */ if (bgp_vrf->vpn_policy[afi].tovpn_sid) { + encode_label(bgp_vrf->vpn_policy[afi].tovpn_sid_transpose_label, + &label); static_attr.srv6_l3vpn = XCALLOC(MTYPE_BGP_SRV6_L3VPN, sizeof(struct bgp_attr_srv6_l3vpn)); static_attr.srv6_l3vpn->sid_flags = 0x00; static_attr.srv6_l3vpn->endpoint_behavior = 0xffff; + static_attr.srv6_l3vpn->loc_block_len = + BGP_PREFIX_SID_SRV6_LOCATOR_BLOCK_LENGTH; + static_attr.srv6_l3vpn->loc_node_len = + BGP_PREFIX_SID_SRV6_LOCATOR_NODE_LENGTH; + static_attr.srv6_l3vpn->func_len = + BGP_PREFIX_SID_SRV6_FUNCTION_LENGTH; + static_attr.srv6_l3vpn->arg_len = + BGP_PREFIX_SID_SRV6_ARGUMENT_LENGTH; + static_attr.srv6_l3vpn->transposition_len = + BGP_PREFIX_SID_SRV6_TRANSPOSITION_LENGTH; + static_attr.srv6_l3vpn->transposition_offset = + BGP_PREFIX_SID_SRV6_TRANSPOSITION_OFFSET; memcpy(&static_attr.srv6_l3vpn->sid, bgp_vrf->vpn_policy[afi].tovpn_sid, sizeof(static_attr.srv6_l3vpn->sid)); diff --git a/bgpd/bgp_mplsvpn.h b/bgpd/bgp_mplsvpn.h index 38193721b3..b0d586223f 100644 --- a/bgpd/bgp_mplsvpn.h +++ b/bgpd/bgp_mplsvpn.h @@ -81,6 +81,8 @@ extern void vpn_leak_zebra_vrf_sid_update(struct bgp *bgp, afi_t afi); extern void vpn_leak_zebra_vrf_sid_withdraw(struct bgp *bgp, afi_t afi); extern int vpn_leak_label_callback(mpls_label_t label, void *lblid, bool alloc); extern void ensure_vrf_tovpn_sid(struct bgp *vpn, struct bgp *vrf, afi_t afi); +extern void transpose_sid(struct in6_addr *sid, uint32_t label, uint8_t offset, + uint8_t size); extern void vrf_import_from_vrf(struct bgp *to_bgp, struct bgp *from_bgp, afi_t afi, safi_t safi); void vrf_unimport_from_vrf(struct bgp *to_bgp, struct bgp *from_bgp, @@ -243,6 +245,10 @@ static inline void vpn_leak_postchange(vpn_policy_direction_t direction, if (!bgp_vrf->vpn_policy[afi].tovpn_sid) ensure_vrf_tovpn_sid(bgp_vpn, bgp_vrf, afi); + if (!bgp_vrf->vpn_policy[afi].tovpn_sid + && bgp_vrf->vpn_policy[afi].tovpn_zebra_vrf_sid_last_sent) + vpn_leak_zebra_vrf_sid_withdraw(bgp_vrf, afi); + if (sid_diff(bgp_vrf->vpn_policy[afi].tovpn_sid, bgp_vrf->vpn_policy[afi] .tovpn_zebra_vrf_sid_last_sent)) { diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c index f72f44f8a2..09abb69968 100644 --- a/bgpd/bgp_network.c +++ b/bgpd/bgp_network.c @@ -46,6 +46,7 @@ #include "bgpd/bgp_errors.h" #include "bgpd/bgp_network.h" #include "bgpd/bgp_zebra.h" +#include "bgpd/bgp_nht.h" extern struct zebra_privs_t bgpd_privs; @@ -603,6 +604,12 @@ static int bgp_accept(struct thread *thread) BGP_EVENT_ADD(peer, TCP_connection_open); } + /* + * If we are doing nht for a peer that is v6 LL based + * massage the event system to make things happy + */ + bgp_nht_interface_events(peer); + return 0; } diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index b164d710a5..647ad55306 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -4042,15 +4042,48 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, /* Update SRv6 SID */ if (attr->srv6_l3vpn) { extra = bgp_path_info_extra_get(pi); - if (sid_diff(&extra->sid[0], &attr->srv6_l3vpn->sid)) { - sid_copy(&extra->sid[0], + if (sid_diff(&extra->sid[0].sid, + &attr->srv6_l3vpn->sid)) { + sid_copy(&extra->sid[0].sid, &attr->srv6_l3vpn->sid); extra->num_sids = 1; + + extra->sid[0].loc_block_len = 0; + extra->sid[0].loc_node_len = 0; + extra->sid[0].func_len = 0; + extra->sid[0].arg_len = 0; + + if (attr->srv6_l3vpn->loc_block_len != 0) { + extra->sid[0].loc_block_len = + attr->srv6_l3vpn->loc_block_len; + extra->sid[0].loc_node_len = + attr->srv6_l3vpn->loc_node_len; + extra->sid[0].func_len = + attr->srv6_l3vpn->func_len; + extra->sid[0].arg_len = + attr->srv6_l3vpn->arg_len; + } + + /* + * draft-ietf-bess-srv6-services-07 + * The part of SRv6 SID may be encoded as MPLS + * Label for the efficient packing. + */ + if (attr->srv6_l3vpn->transposition_len != 0) + transpose_sid( + &extra->sid[0].sid, + decode_label(label), + attr->srv6_l3vpn + ->transposition_offset, + attr->srv6_l3vpn + ->transposition_len); } } else if (attr->srv6_vpn) { extra = bgp_path_info_extra_get(pi); - if (sid_diff(&extra->sid[0], &attr->srv6_vpn->sid)) { - sid_copy(&extra->sid[0], &attr->srv6_vpn->sid); + if (sid_diff(&extra->sid[0].sid, + &attr->srv6_vpn->sid)) { + sid_copy(&extra->sid[0].sid, + &attr->srv6_vpn->sid); extra->num_sids = 1; } } @@ -4231,10 +4264,28 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, if (safi == SAFI_MPLS_VPN) { extra = bgp_path_info_extra_get(new); if (attr->srv6_l3vpn) { - sid_copy(&extra->sid[0], &attr->srv6_l3vpn->sid); + sid_copy(&extra->sid[0].sid, &attr->srv6_l3vpn->sid); extra->num_sids = 1; + + extra->sid[0].loc_block_len = + attr->srv6_l3vpn->loc_block_len; + extra->sid[0].loc_node_len = + attr->srv6_l3vpn->loc_node_len; + extra->sid[0].func_len = attr->srv6_l3vpn->func_len; + extra->sid[0].arg_len = attr->srv6_l3vpn->arg_len; + + /* + * draft-ietf-bess-srv6-services-07 + * The part of SRv6 SID may be encoded as MPLS Label for + * the efficient packing. + */ + if (attr->srv6_l3vpn->transposition_len != 0) + transpose_sid( + &extra->sid[0].sid, decode_label(label), + attr->srv6_l3vpn->transposition_offset, + attr->srv6_l3vpn->transposition_len); } else if (attr->srv6_vpn) { - sid_copy(&extra->sid[0], &attr->srv6_vpn->sid); + sid_copy(&extra->sid[0].sid, &attr->srv6_vpn->sid); extra->num_sids = 1; } } @@ -8066,8 +8117,9 @@ DEFPY(aggregate_addressv6, aggregate_addressv6_cmd, void bgp_redistribute_add(struct bgp *bgp, struct prefix *p, const union g_addr *nexthop, ifindex_t ifindex, enum nexthop_types_t nhtype, uint8_t distance, - uint32_t metric, uint8_t type, - unsigned short instance, route_tag_t tag) + enum blackhole_type bhtype, uint32_t metric, + uint8_t type, unsigned short instance, + route_tag_t tag) { struct bgp_path_info *new; struct bgp_path_info *bpi; @@ -8109,8 +8161,10 @@ void bgp_redistribute_add(struct bgp *bgp, struct prefix *p, attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV6_GLOBAL; break; } + attr.bh_type = bhtype; break; } + attr.nh_type = nhtype; attr.nh_ifindex = ifindex; attr.med = metric; @@ -8977,8 +9031,6 @@ void route_vty_out(struct vty *vty, const struct prefix *p, vty_out(vty, "\n"); if (safi == SAFI_EVPN) { - struct bgp_path_es_info *path_es_info = NULL; - if (bgp_evpn_is_esi_valid(&attr->esi)) { /* XXX - add these params to the json out */ vty_out(vty, "%*s", 20, " "); @@ -8986,13 +9038,6 @@ void route_vty_out(struct vty *vty, const struct prefix *p, esi_to_str(&attr->esi, esi_buf, sizeof(esi_buf))); - if (path->extra && path->extra->mh_info) - path_es_info = - path->extra->mh_info->es_info; - - if (path_es_info && path_es_info->es) - vty_out(vty, " VNI: %u", - path_es_info->vni); vty_out(vty, "\n"); } if (attr->flag & @@ -10456,7 +10501,7 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn, /* Remote SID */ if (path->extra && path->extra->num_sids > 0 && safi != SAFI_EVPN) { - inet_ntop(AF_INET6, &path->extra->sid, buf, sizeof(buf)); + inet_ntop(AF_INET6, &path->extra->sid[0].sid, buf, sizeof(buf)); if (json_paths) json_object_string_add(json_path, "remoteSid", buf); else @@ -10812,6 +10857,7 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, path.attr = &dummy_attr; ret = route_map_apply(rmap, dest_p, &path); + bgp_attr_flush(&dummy_attr); if (ret == RMAP_DENYMATCH) continue; } diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index 75da2723e6..7609f7196d 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -145,6 +145,14 @@ struct bgp_path_mh_info { struct bgp_path_evpn_nh_info *nh_info; }; +struct bgp_sid_info { + struct in6_addr sid; + uint8_t loc_block_len; + uint8_t loc_node_len; + uint8_t func_len; + uint8_t arg_len; +}; + /* Ancillary information to struct bgp_path_info, * used for uncommonly used data (aggregation, MPLS, etc.) * and lazily allocated to save memory. @@ -168,7 +176,7 @@ struct bgp_path_info_extra { #define BGP_EVPN_MACIP_TYPE_SVI_IP (1 << 0) /* SRv6 SID(s) for SRv6-VPN */ - struct in6_addr sid[BGP_MAX_SIDS]; + struct bgp_sid_info sid[BGP_MAX_SIDS]; uint32_t num_sids; #ifdef ENABLE_BGP_VNC @@ -642,8 +650,9 @@ extern bool bgp_maximum_prefix_overflow(struct peer *, afi_t, safi_t, int); extern void bgp_redistribute_add(struct bgp *bgp, struct prefix *p, const union g_addr *nexthop, ifindex_t ifindex, enum nexthop_types_t nhtype, uint8_t distance, - uint32_t metric, uint8_t type, - unsigned short instance, route_tag_t tag); + enum blackhole_type bhtype, uint32_t metric, + uint8_t type, unsigned short instance, + route_tag_t tag); extern void bgp_redistribute_delete(struct bgp *, struct prefix *, uint8_t, unsigned short); extern void bgp_redistribute_withdraw(struct bgp *, afi_t, int, unsigned short); diff --git a/bgpd/bgp_rpki.c b/bgpd/bgp_rpki.c index 648456ac49..6a89a7195c 100644 --- a/bgpd/bgp_rpki.c +++ b/bgpd/bgp_rpki.c @@ -923,25 +923,25 @@ static int config_write(struct vty *vty) #endif case TCP: tcp_config = cache->tr_config.tcp_config; - vty_out(vty, " rpki cache %s%s%s %s ", tcp_config->host, - tcp_config->bindaddr ? " source " : "", - tcp_config->bindaddr ? tcp_config->bindaddr - : "", + vty_out(vty, " rpki cache %s %s ", tcp_config->host, tcp_config->port); + if (tcp_config->bindaddr) + vty_out(vty, "source %s ", + tcp_config->bindaddr); break; #if defined(FOUND_SSH) case SSH: ssh_config = cache->tr_config.ssh_config; - vty_out(vty, " rpki cache %s%s%s %u %s %s %s ", - ssh_config->host, - ssh_config->bindaddr ? "source " : "", - ssh_config->bindaddr ? ssh_config->bindaddr - : "", - ssh_config->port, ssh_config->username, + vty_out(vty, " rpki cache %s %u %s %s %s ", + ssh_config->host, ssh_config->port, + ssh_config->username, ssh_config->client_privkey_path, ssh_config->server_hostkey_path != NULL ? ssh_config->server_hostkey_path : " "); + if (ssh_config->bindaddr) + vty_out(vty, "source %s ", + ssh_config->bindaddr); break; #endif default: @@ -1067,19 +1067,18 @@ DEFUN (no_rpki_retry_interval, } DEFPY(rpki_cache, rpki_cache_cmd, - "rpki cache <A.B.C.D|WORD> [source <A.B.C.D>$bindaddr] " - "<TCPPORT|(1-65535)$sshport SSH_UNAME SSH_PRIVKEY SSH_PUBKEY [SERVER_PUBKEY]> preference (1-255)", + "rpki cache <A.B.C.D|WORD> <TCPPORT|(1-65535)$sshport SSH_UNAME SSH_PRIVKEY SSH_PUBKEY [SERVER_PUBKEY]> [source <A.B.C.D>$bindaddr] preference (1-255)", RPKI_OUTPUT_STRING "Install a cache server to current group\n" "IP address of cache server\n Hostname of cache server\n" - "Configure source IP address of RPKI connection\n" - "Define a Source IP Address\n" "TCP port number\n" "SSH port number\n" "SSH user name\n" "Path to own SSH private key\n" "Path to own SSH public key\n" "Path to Public key of cache server\n" + "Configure source IP address of RPKI connection\n" + "Define a Source IP Address\n" "Preference of the cache server\n" "Preference value\n") { diff --git a/bgpd/bgp_updgrp_adv.c b/bgpd/bgp_updgrp_adv.c index 9c2288cba3..96febcd5df 100644 --- a/bgpd/bgp_updgrp_adv.c +++ b/bgpd/bgp_updgrp_adv.c @@ -798,6 +798,9 @@ void subgroup_default_originate(struct update_subgroup *subgrp, int withdraw) bgp_attr_default_set(&attr, BGP_ORIGIN_IGP); + /* make coverity happy */ + assert(attr.aspath); + attr.local_pref = bgp->default_local_pref; if ((afi == AFI_IP6) || peer_cap_enhe(peer, afi, safi)) { @@ -812,6 +815,10 @@ void subgroup_default_originate(struct update_subgroup *subgrp, int withdraw) } if (peer->default_rmap[afi][safi].name) { + struct bgp_path_info tmp_pi = {0}; + + tmp_pi.peer = bgp->peer_self; + SET_FLAG(bgp->peer_self->rmap_type, PEER_RMAP_TYPE_DEFAULT); /* Iterate over the RIB to see if we can announce @@ -825,24 +832,16 @@ void subgroup_default_originate(struct update_subgroup *subgrp, int withdraw) for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) { - struct attr tmp_attr; - struct bgp_path_info tmp_pi; - struct bgp_path_info_extra tmp_pie; + struct attr tmp_attr = attr; - tmp_attr = *pi->attr; - tmp_attr.aspath = attr.aspath; + tmp_pi.attr = &tmp_attr; - prep_for_rmap_apply(&tmp_pi, &tmp_pie, dest, pi, - pi->peer, &tmp_attr); - - ret = route_map_apply( + ret = route_map_apply_ext( peer->default_rmap[afi][safi].map, - bgp_dest_get_prefix(dest), &tmp_pi); + bgp_dest_get_prefix(dest), pi, &tmp_pi); if (ret == RMAP_DENYMATCH) { - /* The aspath belongs to 'attr' */ - tmp_attr.aspath = NULL; - bgp_attr_flush(&tmp_attr); + bgp_attr_undup(&tmp_attr, &attr); continue; } else { new_attr = bgp_attr_intern(&tmp_attr); @@ -939,6 +938,8 @@ void subgroup_default_originate(struct update_subgroup *subgrp, int withdraw) subgroup_default_update_packet(subgrp, new_attr, from); } } + + aspath_unintern(&attr.aspath); } /* diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 4a1153651e..8a3e74e8a4 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -282,6 +282,57 @@ static const char *get_afi_safi_json_str(afi_t afi, safi_t safi) return "Unknown"; } +/* unset srv6 locator */ +static int bgp_srv6_locator_unset(struct bgp *bgp) +{ + int ret; + struct listnode *node, *nnode; + struct prefix_ipv6 *chunk; + struct bgp_srv6_function *func; + struct bgp *bgp_vrf; + struct in6_addr *tovpn_sid; + + /* release chunk notification via ZAPI */ + ret = bgp_zebra_srv6_manager_release_locator_chunk( + bgp->srv6_locator_name); + if (ret < 0) + return -1; + + /* refresh chunks */ + for (ALL_LIST_ELEMENTS(bgp->srv6_locator_chunks, node, nnode, chunk)) + listnode_delete(bgp->srv6_locator_chunks, chunk); + + /* refresh functions */ + for (ALL_LIST_ELEMENTS(bgp->srv6_functions, node, nnode, func)) + listnode_delete(bgp->srv6_functions, func); + + /* refresh tovpn_sid */ + for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp_vrf)) { + if (bgp_vrf->inst_type != BGP_INSTANCE_TYPE_VRF) + continue; + + /* refresh vpnv4 tovpn_sid */ + tovpn_sid = bgp_vrf->vpn_policy[AFI_IP].tovpn_sid; + if (tovpn_sid) + XFREE(MTYPE_BGP_SRV6_SID, + bgp_vrf->vpn_policy[AFI_IP].tovpn_sid); + + /* refresh vpnv6 tovpn_sid */ + tovpn_sid = bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid; + if (tovpn_sid) + XFREE(MTYPE_BGP_SRV6_SID, + bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid); + } + + /* update vpn bgp processes */ + vpn_leak_postchange_all(); + + /* clear locator name */ + memset(bgp->srv6_locator_name, 0, sizeof(bgp->srv6_locator_name)); + + return 0; +} + /* Utility function to get address family from current node. */ afi_t bgp_node_afi(struct vty *vty) { @@ -6196,11 +6247,12 @@ DEFUN (no_neighbor_disable_connected_check, PEER_FLAG_DISABLE_CONNECTED_CHECK); } -/* link-bw-encoding-ieee */ -DEFUN(neighbor_link_bw_encoding_ieee, neighbor_link_bw_encoding_ieee_cmd, +/* disable-link-bw-encoding-ieee */ +DEFUN(neighbor_disable_link_bw_encoding_ieee, + neighbor_disable_link_bw_encoding_ieee_cmd, "neighbor <A.B.C.D|X:X::X:X|WORD> disable-link-bw-encoding-ieee", NEIGHBOR_STR NEIGHBOR_ADDR_STR2 - "Enable IEEE floating-point encoding for extended community bandwidth\n") + "Disable IEEE floating-point encoding for extended community bandwidth\n") { int idx_peer = 1; @@ -6208,10 +6260,11 @@ DEFUN(neighbor_link_bw_encoding_ieee, neighbor_link_bw_encoding_ieee_cmd, PEER_FLAG_DISABLE_LINK_BW_ENCODING_IEEE); } -DEFUN(no_neighbor_link_bw_encoding_ieee, no_neighbor_link_bw_encoding_ieee_cmd, +DEFUN(no_neighbor_disable_link_bw_encoding_ieee, + no_neighbor_disable_link_bw_encoding_ieee_cmd, "no neighbor <A.B.C.D|X:X::X:X|WORD> disable-link-bw-encoding-ieee", NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2 - "Enable IEEE floating-point encoding for extended community bandwidth\n") + "Disable IEEE floating-point encoding for extended community bandwidth\n") { int idx_peer = 2; @@ -9094,6 +9147,23 @@ DEFUN_NOSH (bgp_segment_routing_srv6, return CMD_SUCCESS; } +DEFUN (no_bgp_segment_routing_srv6, + no_bgp_segment_routing_srv6_cmd, + "no segment-routing srv6", + NO_STR + "Segment-Routing configuration\n" + "Segment-Routing SRv6 configuration\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + + if (strlen(bgp->srv6_locator_name) > 0) + if (bgp_srv6_locator_unset(bgp) < 0) + return CMD_WARNING_CONFIG_FAILED; + + bgp->srv6_enabled = false; + return CMD_SUCCESS; +} + DEFPY (bgp_srv6_locator, bgp_srv6_locator_cmd, "locator NAME$name", @@ -9119,6 +9189,32 @@ DEFPY (bgp_srv6_locator, return CMD_SUCCESS; } +DEFPY (no_bgp_srv6_locator, + no_bgp_srv6_locator_cmd, + "no locator NAME$name", + NO_STR + "Specify SRv6 locator\n" + "Specify SRv6 locator\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + + /* when locator isn't configured, do nothing */ + if (strlen(bgp->srv6_locator_name) < 1) + return CMD_SUCCESS; + + /* name validation */ + if (strcmp(name, bgp->srv6_locator_name) != 0) { + vty_out(vty, "%% No srv6 locator is configured\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + /* unset locator */ + if (bgp_srv6_locator_unset(bgp) < 0) + return CMD_WARNING_CONFIG_FAILED; + + return CMD_SUCCESS; +} + DEFPY (show_bgp_srv6, show_bgp_srv6_cmd, "show bgp segment-routing srv6", @@ -9972,21 +10068,12 @@ static void bgp_show_failed_summary(struct vty *vty, struct bgp *bgp, } } -/* If the peer's description includes whitespaces - * then return the first occurrence. Also strip description - * to the given size if needed. - */ +/* Strip peer's description to the given size. */ static char *bgp_peer_description_stripped(char *desc, uint32_t size) { static char stripped[BUFSIZ]; - char *pnt; uint32_t len = size > strlen(desc) ? strlen(desc) : size; - pnt = strchr(desc, ' '); - if (pnt) - len = size > (uint32_t)(pnt - desc) ? (uint32_t)(pnt - desc) - : size; - strlcpy(stripped, desc, len + 1); return stripped; @@ -10018,7 +10105,15 @@ static bool bgp_show_summary_is_peer_filtered(struct peer *peer, return false; } -/* Show BGP peer's summary information. */ +/* Show BGP peer's summary information. + * + * Peer's description is stripped according to if `wide` option is given + * or not. + * + * When adding new columns to `show bgp summary` output, please make + * sure `Desc` is the lastest column to show because it can contain + * whitespaces and the whole output will be tricky. + */ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, struct peer *fpeer, int as_type, as_t as, uint16_t show_flags) @@ -10683,6 +10778,9 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, vty_out(vty, " %8u", 0); } + /* Make sure `Desc` column is the lastest in + * the output. + */ if (peer->desc) vty_out(vty, " %s", bgp_peer_description_stripped( @@ -18400,9 +18498,10 @@ void bgp_vty_init(void) install_element(BGP_NODE, &neighbor_disable_connected_check_cmd); install_element(BGP_NODE, &no_neighbor_disable_connected_check_cmd); - /* "neighbor link-bw-encoding-ieee" commands. */ - install_element(BGP_NODE, &neighbor_link_bw_encoding_ieee_cmd); - install_element(BGP_NODE, &no_neighbor_link_bw_encoding_ieee_cmd); + /* "neighbor disable-link-bw-encoding-ieee" commands. */ + install_element(BGP_NODE, &neighbor_disable_link_bw_encoding_ieee_cmd); + install_element(BGP_NODE, + &no_neighbor_disable_link_bw_encoding_ieee_cmd); /* "neighbor enforce-first-as" commands. */ install_element(BGP_NODE, &neighbor_enforce_first_as_cmd); @@ -18910,7 +19009,9 @@ void bgp_vty_init(void) /* srv6 commands */ install_element(VIEW_NODE, &show_bgp_srv6_cmd); install_element(BGP_NODE, &bgp_segment_routing_srv6_cmd); + install_element(BGP_NODE, &no_bgp_segment_routing_srv6_cmd); install_element(BGP_SRV6_NODE, &bgp_srv6_locator_cmd); + install_element(BGP_SRV6_NODE, &no_bgp_srv6_locator_cmd); install_element(BGP_IPV4_NODE, &af_sid_vpn_export_cmd); install_element(BGP_IPV6_NODE, &af_sid_vpn_export_cmd); } diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 5ef49e5108..e8ca544c23 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -472,8 +472,9 @@ static int bgp_interface_vrf_update(ZAPI_CALLBACK_ARGS) static int zebra_read_route(ZAPI_CALLBACK_ARGS) { enum nexthop_types_t nhtype; + enum blackhole_type bhtype = BLACKHOLE_UNSPEC; struct zapi_route api; - union g_addr nexthop; + union g_addr nexthop = {}; ifindex_t ifindex; int add, i; struct bgp *bgp; @@ -494,10 +495,16 @@ static int zebra_read_route(ZAPI_CALLBACK_ARGS) && IN6_IS_ADDR_LINKLOCAL(&api.prefix.u.prefix6)) return 0; - nexthop = api.nexthops[0].gate; ifindex = api.nexthops[0].ifindex; nhtype = api.nexthops[0].type; + /* api_nh structure has union of gate and bh_type */ + if (nhtype == NEXTHOP_TYPE_BLACKHOLE) { + /* bh_type is only applicable if NEXTHOP_TYPE_BLACKHOLE*/ + bhtype = api.nexthops[0].bh_type; + } else + nexthop = api.nexthops[0].gate; + add = (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD); if (add) { /* @@ -517,8 +524,8 @@ static int zebra_read_route(ZAPI_CALLBACK_ARGS) /* Now perform the add/update. */ bgp_redistribute_add(bgp, &api.prefix, &nexthop, ifindex, - nhtype, api.distance, api.metric, api.type, - api.instance, api.tag); + nhtype, bhtype, api.distance, api.metric, + api.type, api.instance, api.tag); } else { bgp_redistribute_delete(bgp, &api.prefix, api.type, api.instance); @@ -1076,8 +1083,10 @@ static bool update_ipv4nh_for_route_install(int nh_othervrf, struct bgp *nh_bgp, * a VRF (which are programmed as onlink on l3-vni SVI) as well as * connected routes leaked into a VRF. */ - if (is_evpn) { - + if (attr->nh_type == NEXTHOP_TYPE_BLACKHOLE) { + api_nh->type = attr->nh_type; + api_nh->bh_type = attr->bh_type; + } else if (is_evpn) { /* * If the nexthop is EVPN overlay index gateway IP, * treat the nexthop as NEXTHOP_TYPE_IPV4 @@ -1090,8 +1099,7 @@ static bool update_ipv4nh_for_route_install(int nh_othervrf, struct bgp *nh_bgp, SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK); api_nh->ifindex = nh_bgp->l3vni_svi_ifindex; } - } else if (nh_othervrf && - api_nh->gate.ipv4.s_addr == INADDR_ANY) { + } else if (nh_othervrf && api_nh->gate.ipv4.s_addr == INADDR_ANY) { api_nh->type = NEXTHOP_TYPE_IFINDEX; api_nh->ifindex = attr->nh_ifindex; } else @@ -1113,8 +1121,10 @@ static bool update_ipv6nh_for_route_install(int nh_othervrf, struct bgp *nh_bgp, attr = pi->attr; api_nh->vrf_id = nh_bgp->vrf_id; - if (is_evpn) { - + if (attr->nh_type == NEXTHOP_TYPE_BLACKHOLE) { + api_nh->type = attr->nh_type; + api_nh->bh_type = attr->bh_type; + } else if (is_evpn) { /* * If the nexthop is EVPN overlay index gateway IP, * treat the nexthop as NEXTHOP_TYPE_IPV4 @@ -1169,7 +1179,8 @@ static bool update_ipv6nh_for_route_install(int nh_othervrf, struct bgp *nh_bgp, api_nh->ifindex = 0; } } - if (nexthop) + /* api_nh structure has union of gate and bh_type */ + if (nexthop && api_nh->type != NEXTHOP_TYPE_BLACKHOLE) api_nh->gate.ipv6 = *nexthop; return true; @@ -1440,11 +1451,10 @@ void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p, api_nh->weight = nh_weight; - if (mpinfo->extra - && !sid_zero(&mpinfo->extra->sid[0]) + if (mpinfo->extra && !sid_zero(&mpinfo->extra->sid[0].sid) && !CHECK_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE)) { has_valid_sid = 1; - memcpy(&api_nh->seg6_segs, &mpinfo->extra->sid[0], + memcpy(&api_nh->seg6_segs, &mpinfo->extra->sid[0].sid, sizeof(api_nh->seg6_segs)); } @@ -3077,6 +3087,88 @@ static void bgp_zebra_process_srv6_locator_chunk(ZAPI_CALLBACK_ARGS) vpn_leak_postchange_all(); } +static int bgp_zebra_process_srv6_locator_add(ZAPI_CALLBACK_ARGS) +{ + struct srv6_locator loc = {}; + struct bgp *bgp = bgp_get_default(); + const char *loc_name = bgp->srv6_locator_name; + + if (zapi_srv6_locator_decode(zclient->ibuf, &loc) < 0) + return -1; + + if (!bgp || !bgp->srv6_enabled) + return 0; + + if (bgp_zebra_srv6_manager_get_locator_chunk(loc_name) < 0) + return -1; + + return 0; +} + +static int bgp_zebra_process_srv6_locator_delete(ZAPI_CALLBACK_ARGS) +{ + struct srv6_locator loc = {}; + struct bgp *bgp = bgp_get_default(); + struct listnode *node, *nnode; + struct prefix_ipv6 *chunk; + struct bgp_srv6_function *func; + struct bgp *bgp_vrf; + struct in6_addr *tovpn_sid; + struct prefix_ipv6 tmp_prefi; + + if (zapi_srv6_locator_decode(zclient->ibuf, &loc) < 0) + return -1; + + // refresh chunks + for (ALL_LIST_ELEMENTS(bgp->srv6_locator_chunks, node, nnode, chunk)) + if (prefix_match((struct prefix *)&loc.prefix, + (struct prefix *)chunk)) + listnode_delete(bgp->srv6_locator_chunks, chunk); + + // refresh functions + for (ALL_LIST_ELEMENTS(bgp->srv6_functions, node, nnode, func)) { + tmp_prefi.family = AF_INET6; + tmp_prefi.prefixlen = 128; + tmp_prefi.prefix = func->sid; + if (prefix_match((struct prefix *)&loc.prefix, + (struct prefix *)&tmp_prefi)) + listnode_delete(bgp->srv6_functions, func); + } + + // refresh tovpn_sid + for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp_vrf)) { + if (bgp_vrf->inst_type != BGP_INSTANCE_TYPE_VRF) + continue; + + // refresh vpnv4 tovpn_sid + tovpn_sid = bgp_vrf->vpn_policy[AFI_IP].tovpn_sid; + if (tovpn_sid) { + tmp_prefi.family = AF_INET6; + tmp_prefi.prefixlen = 128; + tmp_prefi.prefix = *tovpn_sid; + if (prefix_match((struct prefix *)&loc.prefix, + (struct prefix *)&tmp_prefi)) + XFREE(MTYPE_BGP_SRV6_SID, + bgp_vrf->vpn_policy[AFI_IP].tovpn_sid); + } + + // refresh vpnv6 tovpn_sid + tovpn_sid = bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid; + if (tovpn_sid) { + tmp_prefi.family = AF_INET6; + tmp_prefi.prefixlen = 128; + tmp_prefi.prefix = *tovpn_sid; + if (prefix_match((struct prefix *)&loc.prefix, + (struct prefix *)&tmp_prefi)) + XFREE(MTYPE_BGP_SRV6_SID, + bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid); + } + } + + vpn_leak_postchange_all(); + return 0; +} + void bgp_zebra_init(struct thread_master *master, unsigned short instance) { zclient_num_connects = 0; @@ -3119,6 +3211,8 @@ void bgp_zebra_init(struct thread_master *master, unsigned short instance) zclient->iptable_notify_owner = iptable_notify_owner; zclient->route_notify_owner = bgp_zebra_route_notify_owner; zclient->instance = instance; + zclient->srv6_locator_add = bgp_zebra_process_srv6_locator_add; + zclient->srv6_locator_delete = bgp_zebra_process_srv6_locator_delete; zclient->process_srv6_locator_chunk = bgp_zebra_process_srv6_locator_chunk; } @@ -3530,3 +3624,8 @@ int bgp_zebra_srv6_manager_get_locator_chunk(const char *name) { return srv6_manager_get_locator_chunk(zclient, name); } + +int bgp_zebra_srv6_manager_release_locator_chunk(const char *name) +{ + return srv6_manager_release_locator_chunk(zclient, name); +} diff --git a/bgpd/bgp_zebra.h b/bgpd/bgp_zebra.h index 02b6484943..9c0a1d8f1f 100644 --- a/bgpd/bgp_zebra.h +++ b/bgpd/bgp_zebra.h @@ -114,4 +114,5 @@ extern int bgp_zebra_send_capabilities(struct bgp *bgp, bool disable); extern int bgp_zebra_update(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type); extern int bgp_zebra_stale_timer_update(struct bgp *bgp); extern int bgp_zebra_srv6_manager_get_locator_chunk(const char *name); +extern int bgp_zebra_srv6_manager_release_locator_chunk(const char *name); #endif /* _QUAGGA_BGP_ZEBRA_H */ diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 13b530a613..5e1eacbb9e 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -241,6 +241,7 @@ struct vpn_policy { */ uint32_t tovpn_sid_index; /* unset => set to 0 */ struct in6_addr *tovpn_sid; + uint32_t tovpn_sid_transpose_label; struct in6_addr *tovpn_zebra_vrf_sid_last_sent; }; diff --git a/bgpd/rfapi/rfapi_vty.c b/bgpd/rfapi/rfapi_vty.c index 45ef7230b5..6762c2b4a2 100644 --- a/bgpd/rfapi/rfapi_vty.c +++ b/bgpd/rfapi/rfapi_vty.c @@ -435,8 +435,16 @@ void rfapi_vty_out_vncinfo(struct vty *vty, const struct prefix *p, char buf[BUFSIZ]; vty_out(vty, " sid=%s", - inet_ntop(AF_INET6, &bpi->extra->sid[0], buf, - sizeof(buf))); + inet_ntop(AF_INET6, &bpi->extra->sid[0].sid, + buf, sizeof(buf))); + + if (bpi->extra->sid[0].loc_block_len != 0) { + vty_out(vty, " sid_structure=[%d,%d,%d,%d]", + bpi->extra->sid[0].loc_block_len, + bpi->extra->sid[0].loc_node_len, + bpi->extra->sid[0].func_len, + bpi->extra->sid[0].arg_len); + } } } diff --git a/configure.ac b/configure.ac index 917e791182..609d3c3230 100644 --- a/configure.ac +++ b/configure.ac @@ -1926,7 +1926,7 @@ dnl ----- dnl LTTng dnl ----- if test "$enable_lttng" = "yes"; then - PKG_CHECK_MODULES([UST], [lttng-ust >= 2.12.0], [ + PKG_CHECK_MODULES([UST], [lttng-ust >= 2.10.0], [ AC_DEFINE([HAVE_LTTNG], [1], [Enable LTTng support]) LTTNG=true ], [ @@ -2519,6 +2519,7 @@ AC_DEFINE_UNQUOTED([LDPD_SOCKET], ["$frr_statedir%s%s/ldpd.sock"], [ldpd control AC_DEFINE_UNQUOTED([ZEBRA_SERV_PATH], ["$frr_statedir%s%s/zserv.api"], [zebra api socket]) AC_DEFINE_UNQUOTED([BFDD_CONTROL_SOCKET], ["$frr_statedir%s%s/bfdd.sock"], [bfdd control socket]) AC_DEFINE_UNQUOTED([OSPFD_GR_STATE], ["$frr_statedir%s/ospfd-gr.json"], [ospfd GR state information]) +AC_DEFINE_UNQUOTED([OSPF6D_GR_STATE], ["$frr_statedir/ospf6d-gr.json"], [ospf6d GR state information]) AC_DEFINE_UNQUOTED([DAEMON_VTY_DIR], ["$frr_statedir%s%s"], [daemon vty directory]) AC_DEFINE_UNQUOTED([DAEMON_DB_DIR], ["$frr_statedir"], [daemon database directory]) diff --git a/doc/developer/cli.rst b/doc/developer/cli.rst index edabe61d92..9254eb4739 100644 --- a/doc/developer/cli.rst +++ b/doc/developer/cli.rst @@ -139,6 +139,7 @@ by the parser. selector: "<" `selector_seq_seq` ">" `varname_token` : "{" `selector_seq_seq` "}" `varname_token` : "[" `selector_seq_seq` "]" `varname_token` + : "![" `selector_seq_seq` "]" `varname_token` selector_seq_seq: `selector_seq_seq` "|" `selector_token_seq` : `selector_token_seq` selector_token_seq: `selector_token_seq` `selector_token` @@ -218,6 +219,10 @@ one-or-more selection and repetition. provide mutual exclusion. User input matches at most one option. - ``[square brackets]`` -- Contains sequences of tokens that can be omitted. ``[<a|b>]`` can be shortened to ``[a|b]``. +- ``![exclamation square brackets]`` -- same as ``[square brackets]``, but + only allow skipping the contents if the command input starts with ``no``. + (For cases where the positive command needs a parameter, but the parameter + is optional for the negative case.) - ``{curly|braces}`` -- similar to angle brackets, but instead of mutual exclusion, curly braces indicate that one or more of the pipe-separated sequences may be provided in any order. @@ -767,6 +772,172 @@ User input: ``ip`` partially matches ``ipv6`` but exactly matches ``ip``, so ``ip`` will win. +Adding a CLI Node +----------------- + +To add a new CLI node, you should: + +- define a new numerical node constant +- define a node structure in the relevant daemon +- call ``install_node()`` in the relevant daemon +- define and install the new node in vtysh +- define corresponding node entry commands in daemon and vtysh +- add a new entry to the ``ctx_keywords`` dictionary in ``tools/frr-reload.py`` + +Defining the numerical node constant +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Add your new node value to the enum before ``NODE_TYPE_MAX`` in +``lib/command.h``: + +.. code-block:: c + + enum node_type { + AUTH_NODE, // Authentication mode of vty interface. + VIEW_NODE, // View node. Default mode of vty interface. + [...] + MY_NEW_NODE, + NODE_TYPE_MAX, // maximum + }; + +Defining a node structure +^^^^^^^^^^^^^^^^^^^^^^^^^ +In your daemon-specific code where you define your new commands that +attach to the new node, add a node definition: + +.. code-block:: c + + static struct cmd_node my_new_node = { + .name = "my new node name", + .node = MY_NEW_NODE, // enum node_type lib/command.h + .parent_node = CONFIG_NODE, + .prompt = "%s(my-new-node-prompt)# ", + .config_write = my_new_node_config_write, + }; + +You will need to define ``my_new_node_config_write(struct vty \*vty)`` +(or omit this field if you have no relevant configuration to save). + +Calling ``install_node()`` +^^^^^^^^^^^^^^^^^^^^^^^^^^ +In the daemon's initialization function, before installing your new commands +with ``install_element()``, add a call ``install_node(&my_new_node)``. + +Defining and installing the new node in vtysh +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The build tools automatically collect command definitions for vtysh. +However, new nodes must be coded in vtysh specifically. + +In ``vtysh/vtysh.c``, define a stripped-down node structure and +call ``install_node()``: + +.. code-block:: c + + static struct cmd_node my_new_node = { + .name = "my new node name", + .node = MY_NEW_NODE, /* enum node_type lib/command.h */ + .parent_node = CONFIG_NODE, + .prompt = "%s(my-new-node-prompt)# ", + }; + [...] + void vtysh_init_vty(void) + { + [...] + install_node(&my_new_node) + [...] + } + +Defining corresponding node entry commands in daemon and vtysh +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The command that descends into the new node is typically programmed +with ``VTY_PUSH_CONTEXT`` or equivalent in the daemon's CLI handler function. +(If the CLI has been updated to use the new northbound architecture, +``VTY_PUSH_XPATH`` is used instead.) + +In vtysh, you must implement a corresponding node change so that vtysh +tracks the daemon's movement through the node tree. + +Although the build tools typically scan daemon code for CLI definitions +to replicate their parsing in vtysh, the node-descent function in the +daemon must be blocked from this replication so that a hand-coded +skeleton can be written in ``vtysh.c``. + +Accordingly, use one of the ``*_NOSH`` macros such as ``DEFUN_NOSH``, +``DEFPY_NOSH``, or ``DEFUN_YANG_NOSH`` for the daemon's node-descent +CLI definition, and use ``DEFUNSH`` in ``vtysh.c`` for the vtysh equivalent. + +.. seealso:: :ref:`vtysh-special-defuns` + +Examples: + +``zebra_whatever.c`` + +.. code-block:: c + + DEFPY_NOSH(my_new_node, + my_new_node_cmd, + "my-new-node foo", + "New Thing\n" + "A foo\n") + { + [...] + VTY_PUSH_CONTEXT(MY_NEW_NODE, bar); + [...] + } + + +``ripd_whatever.c`` + +.. code-block:: c + + DEFPY_YANG_NOSH(my_new_node, + my_new_node_cmd, + "my-new-node foo", + "New Thing\n" + "A foo\n") + { + [...] + VTY_PUSH_XPATH(MY_NEW_NODE, xbar); + [...] + } + + +``vtysh.c`` + +.. code-block:: c + + DEFUNSH(VTYSH_ZEBRA, my_new_node, + my_new_node_cmd, + "my-new-node foo", + "New Thing\n" + "A foo\n") + { + vty->node = MY_NEW_NODE; + return CMD_SUCCESS; + } + [...] + install_element(CONFIG_NODE, &my_new_node_cmd); + + +Adding a new entry to the ``ctx_keywords`` dictionary +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +In file ``tools/frr-reload.py``, the ``ctx_keywords`` dictionary +describes the various node relationships. +Add a new node entry at the appropriate level in this dictionary. + +.. code-block:: python + + ctx_keywords = { + [...] + "key chain ": { + "key ": {} + }, + [...] + "my-new-node": {}, + [...] + } + + + Inspection & Debugging ---------------------- diff --git a/doc/developer/logging.rst b/doc/developer/logging.rst index b827afd6cc..681fc1173c 100644 --- a/doc/developer/logging.rst +++ b/doc/developer/logging.rst @@ -191,6 +191,10 @@ Networking data types ``%pNHs``: :frrfmtout:`1.2.3.4 if 15` — same as :c:func:`nexthop2str()` + ``%pNHcg``: :frrfmtout:`1.2.3.4` — compact gateway only + + ``%pNHci``: :frrfmtout:`eth0` — compact interface only + .. frrfmt:: %pBD (struct bgp_dest *) :frrfmtout:`fe80::1234/64` diff --git a/doc/developer/topotests-jsontopo.rst b/doc/developer/topotests-jsontopo.rst index 866f27337f..e2cc72cc56 100644 --- a/doc/developer/topotests-jsontopo.rst +++ b/doc/developer/topotests-jsontopo.rst @@ -23,19 +23,18 @@ On top of current topotests framework following enhancements are done: Logging of test case executions ------------------------------- -* The user can enable logging of testcases execution messages into log file by - adding ``frrtest_log_dir = /tmp/topotests/`` in :file:`pytest.ini`. -* Router's current configuration can be displyed on console or sent to logs by - adding ``show_router_config = True`` in :file:`pytest.ini`. +* The execution log for each test is saved in the test specific directory create + under `/tmp/topotests` (e.g., + `/tmp/topotests/<testdirname.testfilename>/exec.log`) -Log file name will be displayed when we start execution: +* Additionally all test logs are captured in the `topotest.xml` results file. + This file will be saved in `/tmp/topotests/topotests.xml`. In order to extract + the logs for a particular test one can use the `analyze.py` utility found in + the topotests base directory. -.. code-block:: console - - root@test:# python ./test_topo_json_single_link.py - - Logs will be sent to logfile: - /tmp/topotests/test_topo_json_single_link_11:57:01.353797 +* Router's current configuration, as it is changed during the test, can be + displayed on console or sent to logs by adding ``show_router_config = True`` in + :file:`pytest.ini`. Note: directory "/tmp/topotests/" is created by topotests by default, making use of same directory to save execution logs. @@ -51,18 +50,18 @@ topology test. This is the recommended test writing routine: -* Create a json file , which will have routers and protocol configurations -* Create topology from json -* Create configuration from json -* Write the tests +* Create a json file which will have routers and protocol configurations +* Write and debug the tests * Format the new code using `black <https://github.com/psf/black>`_ * Create a Pull Request .. Note:: - BGP tests MUST use generous convergence timeouts - you must ensure - that any test involving BGP uses a convergence timeout of at least - 130 seconds. + BGP tests MUST use generous convergence timeouts - you must ensure that any + test involving BGP uses a convergence timeout that is proportional to the + configured BGP timers. If the timers are not reduced from their defaults this + means 130 seconds; however, it is highly recommended that timers be reduced + from the default values unless the test requires they not be. File Hierarchy ^^^^^^^^^^^^^^ @@ -72,21 +71,17 @@ repository hierarchy looks like this: .. code-block:: console - $ cd path/to/topotests + $ cd frr/tests/topotests $ find ./* ... - ./example-topojson-test # the basic example test topology-1 - ./example-topojson-test/test_example_topojson.json # input json file, having - topology, interfaces, bgp and other configuration - ./example-topojson-test/test_example_topojson.py # test script to write and - execute testcases + ./example_test/ + ./example_test/test_template_json.json # input json file, having topology, interfaces, bgp and other configuration + ./example_test/test_template_json.py # test script to write and execute testcases ... ./lib # shared test/topology functions - ./lib/topojson.py # library to create topology and configurations dynamically - from json file - ./lib/common_config.py # library to create protocol's common configurations ex- - static_routes, prefix_lists, route_maps etc. - ./lib/bgp.py # library to create only bgp configurations + ./lib/topojson.py # library to create topology and configurations dynamically from json file + ./lib/common_config.py # library to create protocol's common configurations ex- static_routes, prefix_lists, route_maps etc. + ./lib/bgp.py # library to create and test bgp configurations Defining the Topology and initial configuration in JSON file ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -370,34 +365,32 @@ Optional keywords/options in JSON: Building topology and configurations """""""""""""""""""""""""""""""""""" -Topology and initial configuration will be created in setup_module(). Following -is the sample code:: +Topology and initial configuration as well as teardown are invoked through the +use of a pytest fixture:: - def setup_module(mod): - json_file = "{}/my_test_name.json".format(CWD) - tgen = Topogen(json_file, mod.__name__) - # json topo object is now available in tgen.json_topo + from lib import fixtures - # Starting topology, create tmp files which are loaded to routers - # to start deamons and then start routers - start_topology(tgen) + tgen = pytest.fixture(fixtures.tgen_json, scope="module") - # Creating configuration from JSON - build_config_from_json(tgen) - def teardown_module(mod): - tgen = get_topogen() + # tgen is defined above + # topo is a fixture defined in ../conftest.py and automatically available + def test_bgp_convergence(tgen, topo): + bgp_convergence = bgp.verify_bgp_convergence(tgen, topo) + assert bgp_convergence - # Stop toplogy and Remove tmp files - stop_topology(tgen) +The `fixtures.topo_json` function calls `topojson.setup_module_from_json()` to +create and return a new `topogen.Topogen()` object using the JSON config file +with the same base filename as the test (i.e., `test_file.py` -> +`test_file.json`). Additionally, the fixture calls `tgen.stop_topology()` after +all the tests have run to cleanup. The function is only invoked once per +file/module (scope="module"), but the resulting object is passed to each +function that has `tgen` as an argument. +For more info on the powerful pytest fixtures feature please see `FIXTURES`_. -* Note: Topology will be created in setup module but routers will not be - started until we load zebra.conf and bgpd.conf to routers. For all routers - dirs will be created in /tmp/topotests/<test_folder_name>/<router_name> - zebra.conf and bgpd.conf empty files will be created and laoded to routers. - All folder and files are deleted in teardown module.. +.. _FIXTURES: https://docs.pytest.org/en/6.2.x/fixture.html Creating configuration files """""""""""""""""""""""""""" @@ -425,49 +418,37 @@ Writing Tests """"""""""""" Test topologies should always be bootstrapped from the -example_test/test_template_json.py, because it contains important boilerplate -code that can't be avoided, like: - -imports: os, sys, pytest, and topotest/topogen. - -The global variable CWD (Current Working directory): which is most likely going -to be used to reference the routers configuration file location +`example_test/test_template_json.py` when possible in order to take advantage of +the most recent infrastructure support code. Example: -* The topology class that inherits from Mininet Topo class; - - .. code-block:: python - - class TemplateTopo(Topo): - def build(self, *_args, **_opts): - tgen = get_topogen(self) - # topology build code - - -* pytest setup_module() and teardown_module() to start the topology: +* Define a module scoped fixture to setup/teardown and supply the tests with the + `Topogen` object. - .. code-block:: python +.. code-block:: python - def setup_module(_m): - tgen = Topogen(TemplateTopo) + import pytest + from lib import fixtures - # Starting topology, create tmp files which are loaded to routers - # to start deamons and then start routers - start_topology(tgen, CWD) + tgen = pytest.fixture(fixtures.tgen_json, scope="module") - def teardown_module(_m): - tgen = get_topogen() - # Stop toplogy and Remove tmp files - stop_topology(tgen, CWD) +* Define test functions using pytest fixtures +.. code-block:: python -* ``__main__`` initialization code (to support running the script directly) + from lib import bgp - .. code-block:: python + # tgen is defined above + # topo is a global available fixture defined in ../conftest.py + def test_bgp_convergence(tgen, topo): + "Test for BGP convergence." - if **name** == '\ **main**\ ': - sys.exit(pytest.main(["-s"])) + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + bgp_convergence = bgp.verify_bgp_convergence(tgen, topo) + assert bgp_convergence diff --git a/doc/developer/topotests.rst b/doc/developer/topotests.rst index fa6a1ba660..c52d210ee5 100644 --- a/doc/developer/topotests.rst +++ b/doc/developer/topotests.rst @@ -983,22 +983,20 @@ Writing Tests """"""""""""" Test topologies should always be bootstrapped from -:file:`tests/topotests/example-test/test_template.py` because it contains +:file:`tests/topotests/example_test/test_template.py` because it contains important boilerplate code that can't be avoided, like: Example: .. code:: py - # For all registered routers, load the zebra configuration file - CWD = os.path.dirname(os.path.realpath(__file__)) - for rname, router in router_list.items(): - router.load_config( - TopoRouter.RD_ZEBRA, - os.path.join(CWD, '{}/zebra.conf'.format(rname)) - ) - # os.path.join() joins the CWD string with arguments adding the necessary - # slashes ('/'). Arguments must not begin with '/'. + # For all routers arrange for: + # - starting zebra using config file from <rtrname>/zebra.conf + # - starting ospfd using an empty config file. + for rname, router in router_list.items(): + router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf") + router.load_config(TopoRouter.RD_OSPF) + - The topology definition or build function @@ -1013,27 +1011,31 @@ Example: # topology build code ... -- pytest ``setup_module()`` and ``teardown_module()`` to start the topology +- pytest setup/teardown fixture to start the topology and supply `tgen` argument + to tests. .. code:: py - def setup_module(module): + + @pytest.fixture(scope="module") + def tgen(request): + "Setup/Teardown the environment and provide tgen argument to tests" + tgen = Topogen(topodef, module.__name__) # or tgen = Topogen(build_topo, module.__name__) - tgen.start_topology('debug') + ... - def teardown_module(_m): - tgen = get_topogen() - tgen.stop_topology() + # Start and configure the router daemons + tgen.start_router() -- ``__main__`` initialization code (to support running the script directly) + # Provide tgen as argument to each test function + yield tgen -.. code:: py + # Teardown after last test runs + tgen.stop_topology() - if __name__ == '__main__': - sys.exit(pytest.main(["-s"])) Requirements: diff --git a/doc/user/ospf6d.rst b/doc/user/ospf6d.rst index 499788ae87..f0b0638eeb 100644 --- a/doc/user/ospf6d.rst +++ b/doc/user/ospf6d.rst @@ -176,9 +176,9 @@ OSPF6 area The `not-advertise` option, when present, prevents the summary route from being advertised, effectively filtering the summarized routes. -.. clicmd:: area A.B.C.D nssa +.. clicmd:: area A.B.C.D nssa [no-summary] -.. clicmd:: area (0-4294967295) nssa +.. clicmd:: area (0-4294967295) nssa [no-summary] Configure the area to be a NSSA (Not-So-Stubby Area). @@ -194,6 +194,51 @@ OSPF6 area 4. Support for NSSA Translator functionality when there are multiple NSSA ABR in an area. + An NSSA ABR can be configured with the `no-summary` option to prevent the + advertisement of summaries into the area. In that case, a single Type-3 LSA + containing a default route is originated into the NSSA. + +.. clicmd:: area A.B.C.D export-list NAME + +.. clicmd:: area (0-4294967295) export-list NAME + + Filter Type-3 summary-LSAs announced to other areas originated from intra- + area paths from specified area. + + .. code-block:: frr + + router ospf6 + area 0.0.0.10 export-list foo + ! + ipv6 access-list foo permit 2001:db8:1000::/64 + ipv6 access-list foo deny any + + With example above any intra-area paths from area 0.0.0.10 and from range + 2001:db8::/32 (for example 2001:db8:1::/64 and 2001:db8:2::/64) are announced + into other areas as Type-3 summary-LSA's, but any others (for example + 2001:200::/48) aren't. + + This command is only relevant if the router is an ABR for the specified + area. + +.. clicmd:: area A.B.C.D import-list NAME + +.. clicmd:: area (0-4294967295) import-list NAME + + Same as export-list, but it applies to paths announced into specified area + as Type-3 summary-LSAs. + +.. clicmd:: area A.B.C.D filter-list prefix NAME in + +.. clicmd:: area A.B.C.D filter-list prefix NAME out + +.. clicmd:: area (0-4294967295) filter-list prefix NAME in + +.. clicmd:: area (0-4294967295) filter-list prefix NAME out + + Filtering Type-3 summary-LSAs to/from area using prefix lists. This command + makes sense in ABR only. + .. _ospf6-interface: OSPF6 interface @@ -260,8 +305,17 @@ Redistribute routes to OSPF6 argument injects the default route regardless of it being present in the router. Metric values and route-map can also be specified optionally. -Graceful Restart Helper -======================= +Graceful Restart +================ + +.. clicmd:: graceful-restart [grace-period (1-1800)] + + + Configure Graceful Restart (RFC 5187) restarting support. + When enabled, the default grace period is 120 seconds. + + To perform a graceful shutdown, the "graceful-restart prepare ipv6 ospf" + EXEC-level command needs to be issued before restarting the ospf6d daemon. .. clicmd:: graceful-restart helper-only [A.B.C.D] @@ -293,6 +347,16 @@ Graceful Restart Helper restarts. By default, it supports both planned and unplanned outages. +.. clicmd:: graceful-restart prepare ipv6 ospf + + + Initiate a graceful restart for all OSPFv3 instances configured with the + "graceful-restart" command. The ospf6d daemon should be restarted during + the instance-specific grace period, otherwise the graceful restart will fail. + + This is an EXEC-level command. + + .. _showing-ospf6-information: Showing OSPF6 information diff --git a/doc/user/routemap.rst b/doc/user/routemap.rst index 2714b81dbe..e1fe4bbbdb 100644 --- a/doc/user/routemap.rst +++ b/doc/user/routemap.rst @@ -279,6 +279,10 @@ Route Map Set Command Set the BGP AS path to prepend. +.. clicmd:: set as-path exclude AS-NUMBER... + + Drop AS-NUMBER from the BGP AS path. + .. clicmd:: set community COMMUNITY Set the BGP community attribute. diff --git a/doc/user/rpki.rst b/doc/user/rpki.rst index 8d836bfc4b..235df56528 100644 --- a/doc/user/rpki.rst +++ b/doc/user/rpki.rst @@ -109,7 +109,7 @@ The following commands are independent of a specific cache server. The following commands configure one or multiple cache servers. -.. clicmd:: rpki cache (A.B.C.D|WORD) [source A.B.C.D] PORT [SSH_USERNAME] [SSH_PRIVKEY_PATH] [SSH_PUBKEY_PATH] [KNOWN_HOSTS_PATH] PREFERENCE +.. clicmd:: rpki cache (A.B.C.D|WORD) PORT [SSH_USERNAME] [SSH_PRIVKEY_PATH] [SSH_PUBKEY_PATH] [KNOWN_HOSTS_PATH] [source A.B.C.D] PREFERENCE Add a cache server to the socket. By default, the connection between router @@ -120,9 +120,6 @@ The following commands are independent of a specific cache server. A.B.C.D|WORD Address of the cache server. - source A.B.C.D - Source address of the RPKI connection to access cache server. - PORT Port number to connect to the cache server @@ -143,6 +140,9 @@ The following commands are independent of a specific cache server. on the configuration of the operating system environment, usually :file:`~/.ssh/known_hosts`. + source A.B.C.D + Source address of the RPKI connection to access cache server. + .. _validating-bgp-updates: diff --git a/doc/user/setup.rst b/doc/user/setup.rst index 64a33765c2..dbbfca21e7 100644 --- a/doc/user/setup.rst +++ b/doc/user/setup.rst @@ -176,6 +176,27 @@ Operations This section covers a few common operational tasks and how to perform them. +Interactive Shell +^^^^^^^^^^^^^^^^^ +FRR offers an IOS-like interactive shell called ``vtysh`` where a user can run +individual configuration or show commands. To get into this shell, issue the +``vtysh`` command from either a privilege user (root, or with sudo) or a user +account that is part of the ``frrvty`` group. +e.g. + +.. code-block:: console + + root@ub18:~# vtysh + + Hello, this is FRRouting (version 8.1-dev). + Copyright 1996-2005 Kunihiro Ishiguro, et al. + + ub18# + +.. note:: + The default install location for vtysh is /usr/bin/vtysh + + Restarting ^^^^^^^^^^ diff --git a/doc/user/zebra.rst b/doc/user/zebra.rst index 79036320b8..3a9cd11055 100644 --- a/doc/user/zebra.rst +++ b/doc/user/zebra.rst @@ -1241,36 +1241,103 @@ For protocols requiring an IPv6 router-id, the following commands are available: .. _zebra-sysctl: -Expected sysctl settings -======================== +sysctl settings +=============== The linux kernel has a variety of sysctl's that affect it's operation as a router. This section is meant to act as a starting point for those sysctl's that must be used in order to provide FRR with smooth operation as a router. This section is not meant as the full documentation for sysctl's. The operator must use the sysctl documentation -with the linux kernel for that. +with the linux kernel for that. The following link has helpful references to many relevant +sysctl values: https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt + +Expected sysctl settings +------------------------ .. option:: net.ipv4.ip_forward = 1 - This option allows the linux kernel to forward ipv4 packets incoming from one interface - to an outgoing interface. Without this no forwarding will take place from off box packets. + This global option allows the linux kernel to forward (route) ipv4 packets incoming from one + interface to an outgoing interface. If this is set to 0, the system will not route transit + ipv4 packets, i.e. packets that are not sent to/from a process running on the local system. -.. option:: net.ipv6.conf.all_forwarding=1 +.. option:: net.ipv4.conf.{all,default,<interface>}.forwarding = 1 - This option allows the linux kernel to forward ipv6 packets incoming from one interface - to an outgoing interface. Without this no forwarding will take place from off box packets. + The linux kernel can selectively enable forwarding (routing) of ipv4 packets on a per + interface basis. The forwarding check in the kernel dataplane occurs against the ingress + Layer 3 interface, i.e. if the ingress L3 interface has forwarding set to 0, packets will not + be routed. -.. option:: net.ipv6.conf.all.keep_addr_on_down=1 +.. option:: net.ipv6.conf.{all,default,<interface>}.forwarding = 1 + + This per interface option allows the linux kernel to forward (route) transit ipv6 packets + i.e. incoming from one Layer 3 interface to an outgoing Layer 3 interface. + The forwarding check in the kernel dataplane occurs against the ingress Layer 3 interface, + i.e. if the ingress L3 interface has forwarding set to 0, packets will not be routed. + +.. option:: net.ipv6.conf.all.keep_addr_on_down = 1 When an interface is taken down, do not remove the v6 addresses associated with the interface. This option is recommended because this is the default behavior for v4 as well. -.. option:: net.ipv6.route.skip_notify_on_dev_down=1 +.. option:: net.ipv6.route.skip_notify_on_dev_down = 1 When an interface is taken down, the linux kernel will not notify, via netlink, about routes that used that interface being removed from the FIB. This option is recommended because this is the default behavior for v4 as well. +Optional sysctl settings +------------------------ + +.. option:: net.ipv4.conf.{all,default,<interface>}.bc_forwarding = 0 + + This per interface option allows the linux kernel to optionally allow Directed Broadcast + (i.e. Routed Broadcast or Subnet Broadcast) packets to be routed onto the connected network + segment where the subnet exists. + If the local router receives a routed packet destined for a broadcast address of a connected + subnet, setting bc_forwarding to 1 on the interface with the target subnet assigned to it will + allow non locally-generated packets to be routed via the broadcast route. + If bc_forwarding is set to 0, routed packets destined for a broadcast route will be dropped. + e.g. + Host1 (SIP:192.0.2.10, DIP:10.0.0.255) -> (eth0:192.0.2.1/24) Router1 (eth1:10.0.0.1/24) -> BC + If net.ipv4.conf.{all,default,<interface>}.bc_forwarding=1, then Router1 will forward each + packet destined to 10.0.0.255 onto the eth1 interface with a broadcast DMAC (ff:ff:ff:ff:ff:ff). + +.. option:: net.ipv4.conf.{all,default,<interface>}.arp_accept = 1 + + This per interface option allows the linux kernel to optionally skip the creation of ARP + entries upon the receipt of a Gratuitous ARP (GARP) frame carrying an IP that is not already + present in the ARP cache. Setting arp_accept to 0 on an interface will ensure NEW ARP entries + are not created due to the arrival of a GARP frame. + Note: This does not impact how the kernel reacts to GARP frames that carry a "known" IP + (that is already in the ARP cache) -- an existing ARP entry will always be updated + when a GARP for that IP is received. + +.. option:: net.ipv4.conf.{all,default,<interface>}.arp_ignore = 0 + + This per interface option allows the linux kernel to control what conditions must be met in + order for an ARP reply to be sent in response to an ARP request targeting a local IP address. + When arp_ignore is set to 0, the kernel will send ARP replies in response to any ARP Request + with a Target-IP matching a local address. + When arp_ignore is set to 1, the kernel will send ARP replies if the Target-IP in the ARP + Request matches an IP address on the interface the Request arrived at. + When arp_ignore is set to 2, the kernel will send ARP replies only if the Target-IP matches an + IP address on the interface where the Request arrived AND the Sender-IP falls within the subnet + assigned to the local IP/interface. + +.. option:: net.ipv4.conf.{all,default,<interface>}.arp_notify = 1 + + This per interface option allows the linux kernel to decide whether to send a Gratuitious ARP + (GARP) frame when the Layer 3 interface comes UP. + When arp_notify is set to 0, no GARP is sent. + When arp_notify is set to 1, a GARP is sent when the interface comes UP. + +.. option:: net.ipv6.conf.{all,default,<interface>}.ndisc_notify = 1 + + This per interface option allows the linux kernel to decide whether to send an Unsolicited + Neighbor Advertisement (U-NA) frame when the Layer 3 interface comes UP. + When ndisc_notify is set to 0, no U-NA is sent. + When ndisc_notify is set to 1, a U-NA is sent when the interface comes UP. + Debugging ========= diff --git a/eigrpd/eigrp_northbound.c b/eigrpd/eigrp_northbound.c index 482667f633..3ad711164b 100644 --- a/eigrpd/eigrp_northbound.c +++ b/eigrpd/eigrp_northbound.c @@ -79,6 +79,7 @@ static int eigrpd_instance_create(struct nb_cb_create_args *args) { struct eigrp *eigrp; const char *vrf; + struct vrf *pVrf; vrf_id_t vrfid; switch (args->event) { @@ -87,7 +88,12 @@ static int eigrpd_instance_create(struct nb_cb_create_args *args) break; case NB_EV_PREPARE: vrf = yang_dnode_get_string(args->dnode, "./vrf"); - vrfid = vrf_name_to_id(vrf); + + pVrf = vrf_lookup_by_name(vrf); + if (pVrf) + vrfid = pVrf->vrf_id; + else + vrfid = VRF_DEFAULT; eigrp = eigrp_get(yang_dnode_get_uint16(args->dnode, "./asn"), vrfid); @@ -719,12 +725,19 @@ static int eigrpd_instance_redistribute_create(struct nb_cb_create_args *args) struct eigrp *eigrp; uint32_t proto; vrf_id_t vrfid; + struct vrf *pVrf; switch (args->event) { case NB_EV_VALIDATE: proto = yang_dnode_get_enum(args->dnode, "./protocol"); vrfname = yang_dnode_get_string(args->dnode, "../vrf"); - vrfid = vrf_name_to_id(vrfname); + + pVrf = vrf_lookup_by_name(vrfname); + if (pVrf) + vrfid = pVrf->vrf_id; + else + vrfid = VRF_DEFAULT; + if (vrf_bitmap_check(zclient->redist[AFI_IP][proto], vrfid)) return NB_ERR_INCONSISTENCY; break; diff --git a/isisd/isis_snmp.c b/isisd/isis_snmp.c index d530faa151..c530eb9169 100644 --- a/isisd/isis_snmp.c +++ b/isisd/isis_snmp.c @@ -283,13 +283,6 @@ SNMP_LOCAL_VARIABLES * * 2. I could be replaced in unit test environment */ -#ifndef ISIS_SNMP_HAVE_TIME_FUNC -static uint32_t isis_snmp_time(void) -{ - return (uint32_t)time(NULL); -} - -#endif /* ISIS-MIB instances. */ static oid isis_oid[] = {ISIS_MIB}; @@ -2083,7 +2076,7 @@ static uint8_t *isis_snmp_find_circ(struct variable *v, oid *name, struct isis_circuit *circuit; uint32_t up_ticks; uint32_t delta_ticks; - uint32_t now_time; + time_t now_time; int res; *write_method = NULL; @@ -2191,7 +2184,7 @@ static uint8_t *isis_snmp_find_circ(struct variable *v, oid *name, return SNMP_INTEGER(0); up_ticks = (uint32_t)netsnmp_get_agent_uptime(); - now_time = isis_snmp_time(); + now_time = time(NULL); if (circuit->last_uptime >= now_time) return SNMP_INTEGER(up_ticks); @@ -2501,11 +2494,11 @@ static uint8_t *isis_snmp_find_isadj(struct variable *v, oid *name, oid *oid_idx; size_t oid_idx_len; int res; - uint32_t val; + time_t val; struct isis_adjacency *adj; uint32_t up_ticks; uint32_t delta_ticks; - uint32_t now_time; + time_t now_time; *write_method = NULL; @@ -2577,7 +2570,7 @@ static uint8_t *isis_snmp_find_isadj(struct variable *v, oid *name, * It seems that we want remaining timer */ if (adj->last_upd != 0) { - val = isis_snmp_time(); + val = time(NULL); if (val < (adj->last_upd + adj->hold_time)) return SNMP_INTEGER(adj->last_upd + adj->hold_time - val); @@ -2594,7 +2587,7 @@ static uint8_t *isis_snmp_find_isadj(struct variable *v, oid *name, up_ticks = (uint32_t)netsnmp_get_agent_uptime(); - now_time = isis_snmp_time(); + now_time = time(NULL); if (adj->last_flap >= now_time) return SNMP_INTEGER(up_ticks); @@ -2853,7 +2846,7 @@ static int isis_snmp_trap_throttle(oid trap_id) if (isis == NULL || !isis->snmp_notifications || !smux_enabled()) return 0; - time_now = isis_snmp_time(); + time_now = time(NULL); if ((isis_snmp_trap_timestamp[trap_id] + 5) > time_now) /* Throttle trap rate at 1 in 5 secs */ diff --git a/lib/command.c b/lib/command.c index fcaf466c65..53aa064705 100644 --- a/lib/command.c +++ b/lib/command.c @@ -74,6 +74,7 @@ const struct message tokennames[] = { item(JOIN_TKN), item(START_TKN), item(END_TKN), + item(NEG_ONLY_TKN), {0}, }; /* clang-format on */ diff --git a/lib/command_graph.c b/lib/command_graph.c index c6c3840455..15c8302e63 100644 --- a/lib/command_graph.c +++ b/lib/command_graph.c @@ -388,6 +388,7 @@ static void cmd_node_names(struct graph_node *gn, struct graph_node *join, case START_TKN: case JOIN_TKN: + case NEG_ONLY_TKN: /* "<foo|bar> WORD" -> word is not "bar" or "foo" */ prevname = NULL; break; @@ -511,6 +512,9 @@ void cmd_graph_node_print_cb(struct graph_node *gn, struct buffer *buf) case JOIN_TKN: color = "#ddaaff"; break; + case NEG_ONLY_TKN: + color = "#ffddaa"; + break; case WORD_TKN: color = "#ffffff"; break; diff --git a/lib/command_graph.h b/lib/command_graph.h index 2754dca67d..c20c9874c2 100644 --- a/lib/command_graph.h +++ b/lib/command_graph.h @@ -64,6 +64,7 @@ enum cmd_token_type { JOIN_TKN, // marks subgraph end START_TKN, // first token in line END_TKN, // last token in line + NEG_ONLY_TKN, // filter token, match if "no ..." command SPECIAL_TKN = FORK_TKN, }; diff --git a/lib/command_lex.l b/lib/command_lex.l index 9c096995f5..ec366ce7e1 100644 --- a/lib/command_lex.l +++ b/lib/command_lex.l @@ -82,6 +82,7 @@ RANGE \({NUMBER}[ ]?\-[ ]?{NUMBER}\) {VARIABLE} {yylval->string = XSTRDUP(MTYPE_LEX, yytext); return VARIABLE;} {WORD} {yylval->string = XSTRDUP(MTYPE_LEX, yytext); return WORD;} {RANGE} {yylval->string = XSTRDUP(MTYPE_LEX, yytext); return RANGE;} +!\[ {yylval->string = NULL; return EXCL_BRACKET;} . {return yytext[0];} %% diff --git a/lib/command_match.c b/lib/command_match.c index 5703510148..f221e0a02c 100644 --- a/lib/command_match.c +++ b/lib/command_match.c @@ -42,7 +42,7 @@ DEFINE_MTYPE_STATIC(LIB, CMD_MATCHSTACK, "Command Match Stack"); /* matcher helper prototypes */ static int add_nexthops(struct list *, struct graph_node *, - struct graph_node **, size_t); + struct graph_node **, size_t, bool); static enum matcher_rv command_match_r(struct graph_node *, vector, unsigned int, struct graph_node **, @@ -79,6 +79,13 @@ static enum match_type match_variable(struct cmd_token *, const char *); static enum match_type match_mac(const char *, bool); +static bool is_neg(vector vline, size_t idx) +{ + if (idx >= vector_active(vline) || !vector_slot(vline, idx)) + return false; + return !strcmp(vector_slot(vline, idx), "no"); +} + enum matcher_rv command_match(struct graph *cmdgraph, vector vline, struct list **argv, const struct cmd_element **el) { @@ -248,7 +255,7 @@ static enum matcher_rv command_match_r(struct graph_node *start, vector vline, // get all possible nexthops struct list *next = list_new(); - add_nexthops(next, start, NULL, 0); + add_nexthops(next, start, NULL, 0, is_neg(vline, 1)); // determine the best match for (ALL_LIST_ELEMENTS_RO(next, ln, gn)) { @@ -349,6 +356,7 @@ enum matcher_rv command_complete(struct graph *graph, vector vline, { // pointer to next input token to match char *input_token; + bool neg = is_neg(vline, 0); struct list * current = @@ -363,7 +371,7 @@ enum matcher_rv command_complete(struct graph *graph, vector vline, // add all children of start node to list struct graph_node *start = vector_slot(graph->nodes, 0); - add_nexthops(next, start, &start, 0); + add_nexthops(next, start, &start, 0, neg); unsigned int idx; for (idx = 0; idx < vector_active(vline) && next->count > 0; idx++) { @@ -428,7 +436,7 @@ enum matcher_rv command_complete(struct graph *graph, vector vline, listnode_add(next, newstack); } else if (matchtype >= minmatch) add_nexthops(next, gstack[0], gstack, - idx + 1); + idx + 1, neg); break; default: trace_matcher("no_match\n"); @@ -478,7 +486,7 @@ enum matcher_rv command_complete(struct graph *graph, vector vline, * output, instead of direct node pointers! */ static int add_nexthops(struct list *list, struct graph_node *node, - struct graph_node **stack, size_t stackpos) + struct graph_node **stack, size_t stackpos, bool neg) { int added = 0; struct graph_node *child; @@ -494,8 +502,13 @@ static int add_nexthops(struct list *list, struct graph_node *node, if (j != stackpos) continue; } + + if (token->type == NEG_ONLY_TKN && !neg) + continue; + if (token->type >= SPECIAL_TKN && token->type != END_TKN) { - added += add_nexthops(list, child, stack, stackpos); + added += + add_nexthops(list, child, stack, stackpos, neg); } else { if (stack) { nextstack = XMALLOC( diff --git a/lib/command_parse.y b/lib/command_parse.y index f5e42cc304..3e2cdc79af 100644 --- a/lib/command_parse.y +++ b/lib/command_parse.y @@ -105,6 +105,9 @@ %token <string> MAC %token <string> MAC_PREFIX +/* special syntax, value is irrelevant */ +%token <string> EXCL_BRACKET + /* union types for parsed rules */ %type <node> start %type <node> literal_token @@ -372,6 +375,19 @@ selector: '[' selector_seq_seq ']' varname_token } ; +/* ![option] productions */ +selector: EXCL_BRACKET selector_seq_seq ']' varname_token +{ + struct graph_node *neg_only = new_token_node (ctx, NEG_ONLY_TKN, NULL, NULL); + + $$ = $2; + graph_add_edge ($$.start, neg_only); + graph_add_edge (neg_only, $$.end); + cmd_token_varname_set ($2.end->data, $4); + XFREE (MTYPE_LEX, $4); +} +; + %% #undef scanner diff --git a/lib/command_py.c b/lib/command_py.c index 7f19008fbf..90344ae1e5 100644 --- a/lib/command_py.c +++ b/lib/command_py.c @@ -197,21 +197,30 @@ static PyObject *graph_to_pyobj(struct wrap_graph *wgraph, if (gn->data) { struct cmd_token *tok = gn->data; switch (tok->type) { -#define item(x) case x: wrap->type = #x; break; - item(WORD_TKN) // words - item(VARIABLE_TKN) // almost anything - item(RANGE_TKN) // integer range - item(IPV4_TKN) // IPV4 addresses - item(IPV4_PREFIX_TKN) // IPV4 network prefixes - item(IPV6_TKN) // IPV6 prefixes - item(IPV6_PREFIX_TKN) // IPV6 network prefixes - item(MAC_TKN) // MAC address - item(MAC_PREFIX_TKN) // MAC address with mask - - /* plumbing types */ - item(FORK_TKN) item(JOIN_TKN) item(START_TKN) - item(END_TKN) default - : wrap->type = "???"; +#define item(x) \ + case x: \ + wrap->type = #x; \ + break /* no semicolon */ + + item(WORD_TKN); // words + item(VARIABLE_TKN); // almost anything + item(RANGE_TKN); // integer range + item(IPV4_TKN); // IPV4 addresses + item(IPV4_PREFIX_TKN); // IPV4 network prefixes + item(IPV6_TKN); // IPV6 prefixes + item(IPV6_PREFIX_TKN); // IPV6 network prefixes + item(MAC_TKN); // MAC address + item(MAC_PREFIX_TKN); // MAC address with mask + + /* plumbing types */ + item(FORK_TKN); + item(JOIN_TKN); + item(START_TKN); + item(END_TKN); + item(NEG_ONLY_TKN); +#undef item + default: + wrap->type = "???"; } wrap->deprecated = (tok->attr == CMD_ATTR_DEPRECATED); diff --git a/lib/nexthop.c b/lib/nexthop.c index 23e3a2b733..2e09cb4bcc 100644 --- a/lib/nexthop.c +++ b/lib/nexthop.c @@ -519,12 +519,13 @@ struct nexthop *nexthop_from_ipv6_ifindex(const struct in6_addr *ipv6, return nexthop; } -struct nexthop *nexthop_from_blackhole(enum blackhole_type bh_type) +struct nexthop *nexthop_from_blackhole(enum blackhole_type bh_type, + vrf_id_t nh_vrf_id) { struct nexthop *nexthop; nexthop = nexthop_new(); - nexthop->vrf_id = VRF_DEFAULT; + nexthop->vrf_id = nh_vrf_id; nexthop->type = NEXTHOP_TYPE_BLACKHOLE; nexthop->bh_type = bh_type; @@ -633,9 +634,6 @@ const char *nexthop2str(const struct nexthop *nexthop, char *str, int size) case NEXTHOP_TYPE_BLACKHOLE: snprintf(str, size, "blackhole"); break; - default: - snprintf(str, size, "unknown"); - break; } return str; @@ -938,6 +936,12 @@ int nexthop_str2backups(const char *str, int *num_backups, * unreachable (blackhole) * %pNHs * nexthop2str() + * %pNHcg + * 1.2.3.4 + * (0-length if no IP address present) + * %pNHci + * eth0 + * (0-length if no interface present) */ printfrr_ext_autoreg_p("NH", printfrr_nh) static ssize_t printfrr_nh(struct fbuf *buf, struct printfrr_eargs *ea, @@ -992,12 +996,10 @@ static ssize_t printfrr_nh(struct fbuf *buf, struct printfrr_eargs *ea, case BLACKHOLE_NULL: ret += bputs(buf, " (blackhole)"); break; - default: + case BLACKHOLE_UNSPEC: break; } break; - default: - break; } if (do_ifi && nexthop->ifindex) ret += bprintfrr(buf, ", %s%s", v_viaif, @@ -1028,9 +1030,54 @@ static ssize_t printfrr_nh(struct fbuf *buf, struct printfrr_eargs *ea, case NEXTHOP_TYPE_BLACKHOLE: ret += bputs(buf, "blackhole"); break; - default: - ret += bputs(buf, "unknown"); - break; + } + return ret; + case 'c': + ea->fmt++; + if (*ea->fmt == 'g') { + ea->fmt++; + if (!nexthop) + return bputs(buf, "(null)"); + switch (nexthop->type) { + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + ret += bprintfrr(buf, "%pI4", + &nexthop->gate.ipv4); + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + ret += bprintfrr(buf, "%pI6", + &nexthop->gate.ipv6); + break; + case NEXTHOP_TYPE_IFINDEX: + case NEXTHOP_TYPE_BLACKHOLE: + break; + } + } else if (*ea->fmt == 'i') { + ea->fmt++; + if (!nexthop) + return bputs(buf, "(null)"); + switch (nexthop->type) { + case NEXTHOP_TYPE_IFINDEX: + ret += bprintfrr( + buf, "%s", + ifindex2ifname(nexthop->ifindex, + nexthop->vrf_id)); + break; + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + if (nexthop->ifindex) + ret += bprintfrr( + buf, "%s", + ifindex2ifname( + nexthop->ifindex, + nexthop->vrf_id)); + break; + case NEXTHOP_TYPE_BLACKHOLE: + break; + } } return ret; } diff --git a/lib/nexthop.h b/lib/nexthop.h index dd65509aec..320b46315e 100644 --- a/lib/nexthop.h +++ b/lib/nexthop.h @@ -182,7 +182,8 @@ struct nexthop *nexthop_from_ipv6(const struct in6_addr *ipv6, vrf_id_t vrf_id); struct nexthop *nexthop_from_ipv6_ifindex(const struct in6_addr *ipv6, ifindex_t ifindex, vrf_id_t vrf_id); -struct nexthop *nexthop_from_blackhole(enum blackhole_type bh_type); +struct nexthop *nexthop_from_blackhole(enum blackhole_type bh_type, + vrf_id_t nh_vrf_id); /* * Hash a nexthop. Suitable for use with hash tables. diff --git a/lib/northbound_cli.c b/lib/northbound_cli.c index b74a0e6c23..6676c0b072 100644 --- a/lib/northbound_cli.c +++ b/lib/northbound_cli.c @@ -550,6 +550,13 @@ void nb_cli_show_config_prepare(struct nb_config *config, bool with_defaults) LYD_VALIDATE_NO_STATE, NULL); } +static int lyd_node_cmp(struct lyd_node **dnode1, struct lyd_node **dnode2) +{ + struct nb_node *nb_node = (*dnode1)->schema->priv; + + return nb_node->cbs.cli_cmp(*dnode1, *dnode2); +} + static void show_dnode_children_cmds(struct vty *vty, struct lyd_node *root, bool with_defaults) { @@ -567,6 +574,10 @@ static void show_dnode_children_cmds(struct vty *vty, struct lyd_node *root, * it's time to print the config. */ if (sort_node && nb_node != sort_node) { + list_sort(sort_list, + (int (*)(const void **, + const void **))lyd_node_cmp); + for (ALL_LIST_ELEMENTS_RO(sort_list, listnode, data)) nb_cli_show_dnode_cmds(vty, data, with_defaults); @@ -584,11 +595,9 @@ static void show_dnode_children_cmds(struct vty *vty, struct lyd_node *root, if (!sort_node) { sort_node = nb_node; sort_list = list_new(); - sort_list->cmp = (int (*)(void *, void *)) - nb_node->cbs.cli_cmp; } - listnode_add_sort(sort_list, child); + listnode_add(sort_list, child); continue; } @@ -596,6 +605,9 @@ static void show_dnode_children_cmds(struct vty *vty, struct lyd_node *root, } if (sort_node) { + list_sort(sort_list, + (int (*)(const void **, const void **))lyd_node_cmp); + for (ALL_LIST_ELEMENTS_RO(sort_list, listnode, data)) nb_cli_show_dnode_cmds(vty, data, with_defaults); diff --git a/lib/prefix.h b/lib/prefix.h index 944c94f57f..c92f5cec5a 100644 --- a/lib/prefix.h +++ b/lib/prefix.h @@ -512,7 +512,7 @@ extern char *esi_to_str(const esi_t *esi, char *buf, int size); extern char *evpn_es_df_alg2str(uint8_t df_alg, char *buf, int buf_len); extern void prefix_evpn_hexdump(const struct prefix_evpn *p); -static inline int ipv6_martian(struct in6_addr *addr) +static inline int ipv6_martian(const struct in6_addr *addr) { struct in6_addr localhost_addr; @@ -527,7 +527,7 @@ static inline int ipv6_martian(struct in6_addr *addr) extern int macstr2prefix_evpn(const char *str, struct prefix_evpn *p); /* NOTE: This routine expects the address argument in network byte order. */ -static inline int ipv4_martian(struct in_addr *addr) +static inline int ipv4_martian(const struct in_addr *addr) { in_addr_t ip = ntohl(addr->s_addr); diff --git a/lib/routemap.c b/lib/routemap.c index 594dcf97cb..5c60b7d1c6 100644 --- a/lib/routemap.c +++ b/lib/routemap.c @@ -2488,8 +2488,9 @@ void route_map_notify_pentry_dependencies(const char *affected_name, We need to make sure our route-map processing matches the above */ -route_map_result_t route_map_apply(struct route_map *map, - const struct prefix *prefix, void *object) +route_map_result_t route_map_apply_ext(struct route_map *map, + const struct prefix *prefix, + void *match_object, void *set_object) { static int recursion = 0; enum route_map_cmd_result_t match_ret = RMAP_NOMATCH; @@ -2516,7 +2517,7 @@ route_map_result_t route_map_apply(struct route_map *map, if ((!map->optimization_disabled) && (map->ipv4_prefix_table || map->ipv6_prefix_table)) { - index = route_map_get_index(map, prefix, object, + index = route_map_get_index(map, prefix, match_object, (uint8_t *)&match_ret); if (index) { index->applied++; @@ -2551,7 +2552,7 @@ route_map_result_t route_map_apply(struct route_map *map, index->applied++; /* Apply this index. */ match_ret = route_map_apply_match(&index->match_list, - prefix, object); + prefix, match_object); if (rmap_debug) { zlog_debug( "Route-map: %s, sequence: %d, prefix: %pFX, result: %s", @@ -2610,7 +2611,7 @@ route_map_result_t route_map_apply(struct route_map *map, * return code. */ (void)(*set->cmd->func_apply)( - set->value, prefix, object); + set->value, prefix, set_object); /* Call another route-map if available */ if (index->nextrm) { @@ -2622,8 +2623,10 @@ route_map_result_t route_map_apply(struct route_map *map, jump to it */ { recursion++; - ret = route_map_apply( - nextrm, prefix, object); + ret = route_map_apply_ext( + nextrm, prefix, + match_object, + set_object); recursion--; } diff --git a/lib/routemap.h b/lib/routemap.h index b356dbf52e..2c8eb24537 100644 --- a/lib/routemap.h +++ b/lib/routemap.h @@ -443,9 +443,12 @@ extern struct route_map *route_map_lookup_by_name(const char *name); struct route_map *route_map_lookup_warn_noexist(struct vty *vty, const char *name); /* Apply route map to the object. */ -extern route_map_result_t route_map_apply(struct route_map *map, - const struct prefix *prefix, - void *object); +extern route_map_result_t route_map_apply_ext(struct route_map *map, + const struct prefix *prefix, + void *match_object, + void *set_object); +#define route_map_apply(map, prefix, object) \ + route_map_apply_ext(map, prefix, object, object) extern void route_map_add_hook(void (*func)(const char *)); extern void route_map_delete_hook(void (*func)(const char *)); @@ -384,21 +384,6 @@ const char *vrf_id_to_name(vrf_id_t vrf_id) return VRF_LOGNAME(vrf); } -vrf_id_t vrf_name_to_id(const char *name) -{ - struct vrf *vrf; - vrf_id_t vrf_id = VRF_DEFAULT; // Pending: need a way to return invalid - // id/ routine not used. - - if (!name) - return vrf_id; - vrf = vrf_lookup_by_name(name); - if (vrf) - vrf_id = vrf->vrf_id; - - return vrf_id; -} - /* Get the data pointer of the specified VRF. If not found, create one. */ void *vrf_info_get(vrf_id_t vrf_id) { @@ -119,7 +119,6 @@ extern struct vrf *vrf_lookup_by_name(const char *); extern struct vrf *vrf_get(vrf_id_t, const char *); extern struct vrf *vrf_update(vrf_id_t new_vrf_id, const char *name); extern const char *vrf_id_to_name(vrf_id_t vrf_id); -extern vrf_id_t vrf_name_to_id(const char *); #define VRF_LOGNAME(V) V ? V->name : "Unknown" diff --git a/lib/zclient.c b/lib/zclient.c index a1e7194890..dde60a6c90 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -1105,6 +1105,33 @@ stream_failure: return -1; } +int zapi_srv6_locator_encode(struct stream *s, const struct srv6_locator *l) +{ + stream_putw(s, strlen(l->name)); + stream_put(s, l->name, strlen(l->name)); + stream_putw(s, l->prefix.prefixlen); + stream_put(s, &l->prefix.prefix, sizeof(l->prefix.prefix)); + return 0; +} + +int zapi_srv6_locator_decode(struct stream *s, struct srv6_locator *l) +{ + uint16_t len = 0; + + STREAM_GETW(s, len); + if (len > SRV6_LOCNAME_SIZE) + goto stream_failure; + + STREAM_GET(l->name, s, len); + STREAM_GETW(s, l->prefix.prefixlen); + STREAM_GET(&l->prefix.prefix, s, sizeof(l->prefix.prefix)); + l->prefix.family = AF_INET6; + return 0; + +stream_failure: + return -1; +} + static int zapi_nhg_encode(struct stream *s, int cmd, struct zapi_nhg *api_nhg) { int i; diff --git a/lib/zclient.h b/lib/zclient.h index 71187ccae7..f9438d5db7 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -1090,6 +1090,9 @@ extern int zapi_labels_encode(struct stream *s, int cmd, struct zapi_labels *zl); extern int zapi_labels_decode(struct stream *s, struct zapi_labels *zl); +extern int zapi_srv6_locator_encode(struct stream *s, + const struct srv6_locator *l); +extern int zapi_srv6_locator_decode(struct stream *s, struct srv6_locator *l); extern int zapi_srv6_locator_chunk_encode(struct stream *s, const struct srv6_locator_chunk *c); extern int zapi_srv6_locator_chunk_decode(struct stream *s, diff --git a/ospf6d/ospf6_abr.c b/ospf6d/ospf6_abr.c index 650262f1ae..57165201bd 100644 --- a/ospf6d/ospf6_abr.c +++ b/ospf6d/ospf6_abr.c @@ -231,6 +231,69 @@ int ospf6_abr_originate_summary_to_area(struct ospf6_route *route, return 0; } + if (route->type == OSPF6_DEST_TYPE_NETWORK) { + bool filter = false; + + route_area = + ospf6_area_lookup(route->path.area_id, area->ospf6); + assert(route_area); + + /* Check export-list */ + if (EXPORT_LIST(route_area) + && access_list_apply(EXPORT_LIST(route_area), + &route->prefix) + == FILTER_DENY) { + if (IS_OSPF6_DEBUG_ABR) + zlog_debug( + "%s: prefix %pFX was denied by export-list", + __func__, &route->prefix); + filter = true; + } + + /* Check output prefix-list */ + if (PREFIX_LIST_OUT(route_area) + && prefix_list_apply(PREFIX_LIST_OUT(route_area), + &route->prefix) + != PREFIX_PERMIT) { + if (IS_OSPF6_DEBUG_ABR) + zlog_debug( + "%s: prefix %pFX was denied by prefix-list out", + __func__, &route->prefix); + filter = true; + } + + /* Check import-list */ + if (IMPORT_LIST(area) + && access_list_apply(IMPORT_LIST(area), &route->prefix) + == FILTER_DENY) { + if (IS_OSPF6_DEBUG_ABR) + zlog_debug( + "%s: prefix %pFX was denied by import-list", + __func__, &route->prefix); + filter = true; + } + + /* Check input prefix-list */ + if (PREFIX_LIST_IN(area) + && prefix_list_apply(PREFIX_LIST_IN(area), &route->prefix) + != PREFIX_PERMIT) { + if (IS_OSPF6_DEBUG_ABR) + zlog_debug( + "%s: prefix %pFX was denied by prefix-list in", + __func__, &route->prefix); + filter = true; + } + + if (filter) { + if (summary) { + ospf6_route_remove(summary, summary_table); + if (old) + ospf6_lsa_purge(old); + } + return 0; + } + } + /* do not generate if the nexthops belongs to the target area */ if (ospf6_abr_nexthops_belong_to_area(route, area)) { if (IS_OSPF6_DEBUG_ABR) @@ -430,39 +493,6 @@ int ospf6_abr_originate_summary_to_area(struct ospf6_route *route, } } - /* Check export list */ - if (EXPORT_NAME(area)) { - if (EXPORT_LIST(area) == NULL) - EXPORT_LIST(area) = - access_list_lookup(AFI_IP6, EXPORT_NAME(area)); - - if (EXPORT_LIST(area)) - if (access_list_apply(EXPORT_LIST(area), &route->prefix) - == FILTER_DENY) { - if (is_debug) - zlog_debug( - "prefix %pFX was denied by export list", - &route->prefix); - ospf6_abr_delete_route(route, summary, - summary_table, old); - return 0; - } - } - - /* Check filter-list */ - if (PREFIX_LIST_OUT(area)) - if (prefix_list_apply(PREFIX_LIST_OUT(area), &route->prefix) - != PREFIX_PERMIT) { - if (is_debug) - zlog_debug( - "prefix %pFX was denied by filter-list out", - &route->prefix); - ospf6_abr_delete_route(route, summary, summary_table, - old); - - return 0; - } - /* the route is going to be originated. store it in area's summary_table */ if (summary == NULL) { @@ -748,7 +778,15 @@ void ospf6_abr_defaults_to_stub(struct ospf6 *o) def->path.cost = metric_value(o, type, 0); for (ALL_LIST_ELEMENTS(o->area_list, node, nnode, oa)) { - if (!IS_AREA_STUB(oa)) { + if (IS_AREA_STUB(oa) || (IS_AREA_NSSA(oa) && oa->no_summary)) { + /* announce defaults to stubby areas */ + if (IS_OSPF6_DEBUG_ABR) + zlog_debug( + "Announcing default route into stubby area %s", + oa->name); + UNSET_FLAG(def->flag, OSPF6_ROUTE_REMOVE); + ospf6_abr_originate_summary_to_area(def, oa); + } else { /* withdraw defaults when an area switches from stub to * non-stub */ route = ospf6_route_lookup(&def->prefix, @@ -762,14 +800,6 @@ void ospf6_abr_defaults_to_stub(struct ospf6 *o) SET_FLAG(def->flag, OSPF6_ROUTE_REMOVE); ospf6_abr_originate_summary_to_area(def, oa); } - } else { - /* announce defaults to stubby areas */ - if (IS_OSPF6_DEBUG_ABR) - zlog_debug( - "Announcing default route into stubby area %s", - oa->name); - UNSET_FLAG(def->flag, OSPF6_ROUTE_REMOVE); - ospf6_abr_originate_summary_to_area(def, oa); } } ospf6_route_delete(def); @@ -1134,39 +1164,6 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa) return; } - /* Check import list */ - if (IMPORT_NAME(oa)) { - if (IMPORT_LIST(oa) == NULL) - IMPORT_LIST(oa) = - access_list_lookup(AFI_IP6, IMPORT_NAME(oa)); - - if (IMPORT_LIST(oa)) - if (access_list_apply(IMPORT_LIST(oa), &prefix) - == FILTER_DENY) { - if (is_debug) - zlog_debug( - "Prefix %pFX was denied by import-list", - &prefix); - if (old) - ospf6_route_remove(old, table); - return; - } - } - - /* Check input prefix-list */ - if (PREFIX_LIST_IN(oa)) { - if (prefix_list_apply(PREFIX_LIST_IN(oa), &prefix) - != PREFIX_PERMIT) { - if (is_debug) - zlog_debug( - "Prefix %pFX was denied by prefix-list in", - &prefix); - if (old) - ospf6_route_remove(old, table); - return; - } - } - /* (5),(6): the path preference is handled by the sorting in the routing table. Always install the path by substituting old route (if any). */ @@ -1201,9 +1198,16 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa) __func__, &prefix, listcount(old->paths)); } for (old_route = old; old_route; old_route = old_route->next) { - if (!ospf6_route_is_same(old_route, route) || - (old_route->type != route->type) || - (old_route->path.type != route->path.type)) + + /* The route linked-list is grouped in batches of prefix. + * If the new prefix is not the same as the one of interest + * then we have walked over the end of the batch and so we + * should break rather than continuing unnecessarily. + */ + if (!ospf6_route_is_same(old_route, route)) + break; + if ((old_route->type != route->type) + || (old_route->path.type != route->path.type)) continue; if ((ospf6_route_cmp(route, old_route) != 0)) { @@ -1348,35 +1352,6 @@ void ospf6_abr_examin_brouter(uint32_t router_id, struct ospf6_route *route, ospf6_abr_examin_summary(lsa, oa); } -void ospf6_abr_reimport(struct ospf6_area *oa) -{ - struct ospf6_lsa *lsa; - uint16_t type; - - type = htons(OSPF6_LSTYPE_INTER_ROUTER); - for (ALL_LSDB_TYPED(oa->lsdb, type, lsa)) - ospf6_abr_examin_summary(lsa, oa); - - type = htons(OSPF6_LSTYPE_INTER_PREFIX); - for (ALL_LSDB_TYPED(oa->lsdb, type, lsa)) - ospf6_abr_examin_summary(lsa, oa); -} - -/* export filter removed so determine if we should reoriginate summary LSAs */ -void ospf6_abr_reexport(struct ospf6_area *oa) -{ - struct ospf6_route *route; - - /* if not a ABR return success */ - if (!ospf6_check_and_set_router_abr(oa->ospf6)) - return; - - /* Redo summaries if required */ - for (route = ospf6_route_head(oa->ospf6->route_table); route; - route = ospf6_route_next(route)) - ospf6_abr_originate_summary_to_area(route, oa); -} - void ospf6_abr_prefix_resummarize(struct ospf6 *o) { struct ospf6_route *route; diff --git a/ospf6d/ospf6_abr.h b/ospf6d/ospf6_abr.h index a5f0f124b9..08521ecb0f 100644 --- a/ospf6d/ospf6_abr.h +++ b/ospf6d/ospf6_abr.h @@ -73,8 +73,6 @@ extern void ospf6_abr_defaults_to_stub(struct ospf6 *ospf6); extern void ospf6_abr_examin_brouter(uint32_t router_id, struct ospf6_route *route, struct ospf6 *ospf6); -extern void ospf6_abr_reimport(struct ospf6_area *oa); -extern void ospf6_abr_reexport(struct ospf6_area *oa); extern void ospf6_abr_range_reset_cost(struct ospf6 *ospf6); extern void ospf6_abr_prefix_resummarize(struct ospf6 *ospf6); @@ -88,7 +86,6 @@ extern void ospf6_abr_old_path_update(struct ospf6_route *old_route, struct ospf6_route *route, struct ospf6_route_table *table); extern void ospf6_abr_init(void); -extern void ospf6_abr_reexport(struct ospf6_area *oa); extern void ospf6_abr_range_update(struct ospf6_route *range, struct ospf6 *ospf6); extern void ospf6_abr_remove_unapproved_summaries(struct ospf6 *ospf6); diff --git a/ospf6d/ospf6_area.c b/ospf6d/ospf6_area.c index 098132b1f6..2f5328c662 100644 --- a/ospf6d/ospf6_area.c +++ b/ospf6d/ospf6_area.c @@ -46,6 +46,9 @@ #include "ospf6d.h" #include "lib/json.h" #include "ospf6_nssa.h" +#ifndef VTYSH_EXTRACT_PL +#include "ospf6d/ospf6_area_clippy.c" +#endif DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_AREA, "OSPF6 area"); DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_PLISTNAME, "Prefix list name"); @@ -643,8 +646,12 @@ void ospf6_area_config_write(struct vty *vty, struct ospf6 *ospf6) else vty_out(vty, " area %s stub\n", oa->name); } - if (IS_AREA_NSSA(oa)) - vty_out(vty, " area %s nssa\n", oa->name); + if (IS_AREA_NSSA(oa)) { + vty_out(vty, " area %s nssa", oa->name); + if (oa->no_summary) + vty_out(vty, " no-summary"); + vty_out(vty, "\n"); + } if (PREFIX_NAME_IN(oa)) vty_out(vty, " area %s filter-list prefix %s in\n", oa->name, PREFIX_NAME_IN(oa)); @@ -689,17 +696,17 @@ DEFUN (area_filter_list, XFREE(MTYPE_OSPF6_PLISTNAME, PREFIX_NAME_IN(area)); PREFIX_NAME_IN(area) = XSTRDUP(MTYPE_OSPF6_PLISTNAME, plistname); - ospf6_abr_reimport(area); } else { PREFIX_LIST_OUT(area) = plist; XFREE(MTYPE_OSPF6_PLISTNAME, PREFIX_NAME_OUT(area)); PREFIX_NAME_OUT(area) = XSTRDUP(MTYPE_OSPF6_PLISTNAME, plistname); - - /* Redo summaries if required */ - ospf6_abr_reexport(area); } + /* Redo summaries if required */ + if (ospf6_check_and_set_router_abr(area->ospf6)) + ospf6_schedule_abr_task(ospf6); + return CMD_SUCCESS; } @@ -732,7 +739,6 @@ DEFUN (no_area_filter_list, PREFIX_LIST_IN(area) = NULL; XFREE(MTYPE_OSPF6_PLISTNAME, PREFIX_NAME_IN(area)); - ospf6_abr_reimport(area); } else { if (PREFIX_NAME_OUT(area)) if (!strmatch(PREFIX_NAME_OUT(area), plistname)) @@ -740,9 +746,12 @@ DEFUN (no_area_filter_list, XFREE(MTYPE_OSPF6_PLISTNAME, PREFIX_NAME_OUT(area)); PREFIX_LIST_OUT(area) = NULL; - ospf6_abr_reexport(area); } + /* Redo summaries if required */ + if (ospf6_check_and_set_router_abr(area->ospf6)) + ospf6_schedule_abr_task(ospf6); + return CMD_SUCCESS; } @@ -753,19 +762,30 @@ void ospf6_filter_update(struct access_list *access) struct ospf6 *ospf6; for (ALL_LIST_ELEMENTS(om6->ospf6, node, nnode, ospf6)) { + bool update = false; + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, n, oa)) { if (IMPORT_NAME(oa) - && strcmp(IMPORT_NAME(oa), access->name) == 0) - ospf6_abr_reimport(oa); + && strcmp(IMPORT_NAME(oa), access->name) == 0) { + IMPORT_LIST(oa) = access_list_lookup( + AFI_IP6, IMPORT_NAME(oa)); + update = true; + } if (EXPORT_NAME(oa) - && strcmp(EXPORT_NAME(oa), access->name) == 0) - ospf6_abr_reexport(oa); + && strcmp(EXPORT_NAME(oa), access->name) == 0) { + EXPORT_LIST(oa) = access_list_lookup( + AFI_IP6, EXPORT_NAME(oa)); + update = true; + } } + + if (update && ospf6_check_and_set_router_abr(ospf6)) + ospf6_schedule_abr_task(ospf6); } } -void ospf6_area_plist_update(struct prefix_list *plist, int add) +void ospf6_plist_update(struct prefix_list *plist) { struct listnode *node, *nnode; struct ospf6_area *oa; @@ -773,23 +793,29 @@ void ospf6_area_plist_update(struct prefix_list *plist, int add) const char *name = prefix_list_name(plist); struct ospf6 *ospf6 = NULL; - - if (!om6->ospf6) + if (prefix_list_afi(plist) != AFI_IP6) return; for (ALL_LIST_ELEMENTS(om6->ospf6, node, nnode, ospf6)) { + bool update = false; + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, n, oa)) { if (PREFIX_NAME_IN(oa) && !strcmp(PREFIX_NAME_IN(oa), name)) { - PREFIX_LIST_IN(oa) = add ? plist : NULL; - ospf6_abr_reexport(oa); + PREFIX_LIST_IN(oa) = prefix_list_lookup( + AFI_IP6, PREFIX_NAME_IN(oa)); + update = true; } if (PREFIX_NAME_OUT(oa) && !strcmp(PREFIX_NAME_OUT(oa), name)) { - PREFIX_LIST_OUT(oa) = add ? plist : NULL; - ospf6_abr_reexport(oa); + PREFIX_LIST_OUT(oa) = prefix_list_lookup( + AFI_IP6, PREFIX_NAME_OUT(oa)); + update = true; } } + + if (update && ospf6_check_and_set_router_abr(ospf6)) + ospf6_schedule_abr_task(ospf6); } } @@ -819,7 +845,8 @@ DEFUN (area_import_list, free(IMPORT_NAME(area)); IMPORT_NAME(area) = strdup(argv[idx_name]->arg); - ospf6_abr_reimport(area); + if (ospf6_check_and_set_router_abr(area->ospf6)) + ospf6_schedule_abr_task(ospf6); return CMD_SUCCESS; } @@ -841,13 +868,14 @@ DEFUN (no_area_import_list, OSPF6_CMD_AREA_GET(argv[idx_ipv4]->arg, area, ospf6); - IMPORT_LIST(area) = 0; + IMPORT_LIST(area) = NULL; if (IMPORT_NAME(area)) free(IMPORT_NAME(area)); IMPORT_NAME(area) = NULL; - ospf6_abr_reimport(area); + if (ospf6_check_and_set_router_abr(area->ospf6)) + ospf6_schedule_abr_task(ospf6); return CMD_SUCCESS; } @@ -880,7 +908,8 @@ DEFUN (area_export_list, EXPORT_NAME(area) = strdup(argv[idx_name]->arg); /* Redo summaries if required */ - ospf6_abr_reexport(area); + if (ospf6_check_and_set_router_abr(area->ospf6)) + ospf6_schedule_abr_task(ospf6); return CMD_SUCCESS; } @@ -902,13 +931,14 @@ DEFUN (no_area_export_list, OSPF6_CMD_AREA_GET(argv[idx_ipv4]->arg, area, ospf6); - EXPORT_LIST(area) = 0; + EXPORT_LIST(area) = NULL; if (EXPORT_NAME(area)) free(EXPORT_NAME(area)); EXPORT_NAME(area) = NULL; - ospf6_abr_reexport(area); + if (ospf6_check_and_set_router_abr(area->ospf6)) + ospf6_schedule_abr_task(ospf6); return CMD_SUCCESS; } @@ -981,7 +1011,6 @@ DEFUN(show_ipv6_ospf6_spf_tree, show_ipv6_ospf6_spf_tree_cmd, int idx_vrf = 0; bool uj = use_json(argc, argv); - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) { @@ -1041,7 +1070,6 @@ DEFUN(show_ipv6_ospf6_area_spf_tree, show_ipv6_ospf6_area_spf_tree_cmd, bool all_vrf = false; int idx_vrf = 0; - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) idx_ipv4 += 2; @@ -1125,7 +1153,6 @@ DEFUN(show_ipv6_ospf6_simulate_spf_tree_root, bool all_vrf = false; int idx_vrf = 0; - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) { idx_ipv4 += 2; @@ -1250,18 +1277,18 @@ DEFUN (no_ospf6_area_stub_no_summary, return CMD_SUCCESS; } -DEFUN(ospf6_area_nssa, ospf6_area_nssa_cmd, - "area <A.B.C.D|(0-4294967295)> nssa", +DEFPY(ospf6_area_nssa, ospf6_area_nssa_cmd, + "area <A.B.C.D|(0-4294967295)>$area_str nssa [no-summary$no_summary]", "OSPF6 area parameters\n" "OSPF6 area ID in IP address format\n" "OSPF6 area ID as a decimal value\n" - "Configure OSPF6 area as nssa\n") + "Configure OSPF6 area as nssa\n" + "Do not inject inter-area routes into area\n") { - int idx_ipv4_number = 1; struct ospf6_area *area; VTY_DECLVAR_CONTEXT(ospf6, ospf6); - OSPF6_CMD_AREA_GET(argv[idx_ipv4_number]->arg, area, ospf6); + OSPF6_CMD_AREA_GET(area_str, area, ospf6); if (!ospf6_area_nssa_set(ospf6, area)) { vty_out(vty, @@ -1269,26 +1296,32 @@ DEFUN(ospf6_area_nssa, ospf6_area_nssa_cmd, return CMD_WARNING_CONFIG_FAILED; } - ospf6_area_no_summary_unset(ospf6, area); + if (no_summary) + ospf6_area_no_summary_set(ospf6, area); + else + ospf6_area_no_summary_unset(ospf6, area); + if (ospf6_check_and_set_router_abr(ospf6)) + ospf6_abr_defaults_to_stub(ospf6); return CMD_SUCCESS; } -DEFUN(no_ospf6_area_nssa, no_ospf6_area_nssa_cmd, - "no area <A.B.C.D|(0-4294967295)> nssa", +DEFPY(no_ospf6_area_nssa, no_ospf6_area_nssa_cmd, + "no area <A.B.C.D|(0-4294967295)>$area_str nssa [no-summary$no_summary]", NO_STR "OSPF6 area parameters\n" "OSPF6 area ID in IP address format\n" "OSPF6 area ID as a decimal value\n" - "Configure OSPF6 area as nssa\n") + "Configure OSPF6 area as nssa\n" + "Do not inject inter-area routes into area\n") { - int idx_ipv4_number = 2; struct ospf6_area *area; VTY_DECLVAR_CONTEXT(ospf6, ospf6); - OSPF6_CMD_AREA_GET(argv[idx_ipv4_number]->arg, area, ospf6); + OSPF6_CMD_AREA_GET(area_str, area, ospf6); ospf6_area_nssa_unset(ospf6, area); + ospf6_area_no_summary_unset(ospf6, area); return CMD_SUCCESS; } @@ -1327,8 +1360,6 @@ void ospf6_area_interface_delete(struct ospf6_interface *oi) struct listnode *node, *nnode; struct ospf6 *ospf6; - if (!om6->ospf6) - return; for (ALL_LIST_ELEMENTS(om6->ospf6, node, nnode, ospf6)) { for (ALL_LIST_ELEMENTS(ospf6->area_list, node, nnode, oa)) if (listnode_lookup(oa->if_list, oi)) diff --git a/ospf6d/ospf6_area.h b/ospf6d/ospf6_area.h index b2a275d745..43ac60b261 100644 --- a/ospf6d/ospf6_area.h +++ b/ospf6d/ospf6_area.h @@ -53,7 +53,7 @@ struct ospf6_area { int no_summary; /* Brouter traversal protection */ - int intra_brouter_calc; + bool intra_brouter_calc; /* OSPF interface list */ struct list *if_list; @@ -149,19 +149,21 @@ extern void area_id2str(char *buf, int len, uint32_t area_id, int area_id_fmt); extern int ospf6_area_cmp(void *va, void *vb); -extern struct ospf6_area *ospf6_area_create(uint32_t, struct ospf6 *, int); -extern void ospf6_area_delete(struct ospf6_area *); -extern struct ospf6_area *ospf6_area_lookup(uint32_t, struct ospf6 *); +extern struct ospf6_area *ospf6_area_create(uint32_t area_id, + struct ospf6 *ospf6, int df); +extern void ospf6_area_delete(struct ospf6_area *oa); +extern struct ospf6_area *ospf6_area_lookup(uint32_t area_id, + struct ospf6 *ospf6); extern struct ospf6_area *ospf6_area_lookup_by_area_id(uint32_t area_id); extern void ospf6_area_stub_unset(struct ospf6 *ospf6, struct ospf6_area *area); -extern void ospf6_area_enable(struct ospf6_area *); -extern void ospf6_area_disable(struct ospf6_area *); +extern void ospf6_area_enable(struct ospf6_area *oa); +extern void ospf6_area_disable(struct ospf6_area *oa); -extern void ospf6_area_show(struct vty *, struct ospf6_area *, +extern void ospf6_area_show(struct vty *vty, struct ospf6_area *oa, json_object *json_areas, bool use_json); -extern void ospf6_area_plist_update(struct prefix_list *plist, int add); +extern void ospf6_plist_update(struct prefix_list *plist); extern void ospf6_filter_update(struct access_list *access); extern void ospf6_area_config_write(struct vty *vty, struct ospf6 *ospf6); extern void ospf6_area_init(void); diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c index f16a1975a8..5a7023126d 100644 --- a/ospf6d/ospf6_asbr.c +++ b/ospf6d/ospf6_asbr.c @@ -52,6 +52,7 @@ #include "ospf6d.h" #include "ospf6_spf.h" #include "ospf6_nssa.h" +#include "ospf6_gr.h" #include "lib/json.h" DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_EXTERNAL_INFO, "OSPF6 ext. info"); @@ -102,6 +103,13 @@ struct ospf6_lsa *ospf6_as_external_lsa_originate(struct ospf6_route *route, struct ospf6_as_external_lsa *as_external_lsa; caddr_t p; + if (ospf6->gr_info.restart_in_progress) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "Graceful Restart in progress, don't originate LSA"); + return NULL; + } + if (IS_OSPF6_DEBUG_ASBR || IS_OSPF6_DEBUG_ORIGINATE(AS_EXTERNAL)) zlog_debug("Originate AS-External-LSA for %pFX", &route->prefix); @@ -262,8 +270,14 @@ void ospf6_asbr_update_route_ecmp_path(struct ospf6_route *old, next_route = old_route->next; - if (!ospf6_route_is_same(old_route, route) - || (old_route->path.type != route->path.type)) + /* The route linked-list is grouped in batches of prefix. + * If the new prefix is not the same as the one of interest + * then we have walked over the end of the batch and so we + * should break rather than continuing unnecessarily. + */ + if (!ospf6_route_is_same(old_route, route)) + break; + if (old_route->path.type != route->path.type) continue; /* Current and New route has same origin, @@ -367,11 +381,14 @@ void ospf6_asbr_update_route_ecmp_path(struct ospf6_route *old, /* Add new route */ for (old_route = old; old_route; old_route = old_route->next) { - /* Current and New Route prefix or route type - * is not same skip this current node. + /* The route linked-list is grouped in batches of prefix. + * If the new prefix is not the same as the one of interest + * then we have walked over the end of the batch and so we + * should break rather than continuing unnecessarily. */ - if (!ospf6_route_is_same(old_route, route) - || (old_route->path.type != route->path.type)) + if (!ospf6_route_is_same(old_route, route)) + break; + if (old_route->path.type != route->path.type) continue; /* Old Route and New Route have Equal Cost, Merge NHs */ @@ -588,6 +605,32 @@ void ospf6_asbr_lsa_add(struct ospf6_lsa *lsa) } } + /* + * RFC 3101 - Section 2.5: + * "If the destination is a Type-7 default route (destination ID = + * DefaultDestination) and one of the following is true, then do + * nothing with this LSA and consider the next in the list: + * + * o The calculating router is a border router and the LSA has + * its P-bit clear. Appendix E describes a technique + * whereby an NSSA border router installs a Type-7 default + * LSA without propagating it. + * + * o The calculating router is a border router and is + * suppressing the import of summary routes as Type-3 + * summary-LSAs". + */ + if (ntohs(lsa->header->type) == OSPF6_LSTYPE_TYPE_7 + && external->prefix.prefix_length == 0 + && CHECK_FLAG(ospf6->flag, OSPF6_FLAG_ABR) + && (CHECK_FLAG(external->prefix.prefix_options, + OSPF6_PREFIX_OPTION_P) + || oa->no_summary)) { + if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) + zlog_debug("Skipping Type-7 default route"); + return; + } + /* Check the forwarding address */ if (CHECK_FLAG(external->bits_metric, OSPF6_ASBR_BIT_F)) { offset = sizeof(*external) @@ -2487,7 +2530,6 @@ DEFUN(show_ipv6_ospf6_redistribute, show_ipv6_ospf6_redistribute_cmd, json_object *json_array_routes = NULL; json_object *json_array_redistribute = NULL; - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (uj) { diff --git a/ospf6d/ospf6_flood.c b/ospf6d/ospf6_flood.c index f13ed3e3bb..186eac35a5 100644 --- a/ospf6d/ospf6_flood.c +++ b/ospf6d/ospf6_flood.c @@ -85,7 +85,7 @@ struct ospf6_lsdb *ospf6_get_scoped_lsdb_self(struct ospf6_lsa *lsa) return lsdb_self; } -void ospf6_lsa_originate(struct ospf6_lsa *lsa) +void ospf6_lsa_originate(struct ospf6 *ospf6, struct ospf6_lsa *lsa) { struct ospf6_lsa *old; struct ospf6_lsdb *lsdb_self; @@ -106,7 +106,8 @@ void ospf6_lsa_originate(struct ospf6_lsa *lsa) /* if the new LSA does not differ from previous, suppress this update of the LSA */ - if (old && !OSPF6_LSA_IS_DIFFER(lsa, old)) { + if (old && !OSPF6_LSA_IS_DIFFER(lsa, old) + && !ospf6->gr_info.finishing_restart) { if (IS_OSPF6_DEBUG_ORIGINATE_TYPE(lsa->header->type)) zlog_debug("Suppress updating LSA: %s", lsa->name); ospf6_lsa_delete(lsa); @@ -134,20 +135,20 @@ void ospf6_lsa_originate(struct ospf6_lsa *lsa) void ospf6_lsa_originate_process(struct ospf6_lsa *lsa, struct ospf6 *process) { lsa->lsdb = process->lsdb; - ospf6_lsa_originate(lsa); + ospf6_lsa_originate(process, lsa); } void ospf6_lsa_originate_area(struct ospf6_lsa *lsa, struct ospf6_area *oa) { lsa->lsdb = oa->lsdb; - ospf6_lsa_originate(lsa); + ospf6_lsa_originate(oa->ospf6, lsa); } void ospf6_lsa_originate_interface(struct ospf6_lsa *lsa, struct ospf6_interface *oi) { lsa->lsdb = oi->lsdb; - ospf6_lsa_originate(lsa); + ospf6_lsa_originate(oi->area->ospf6, lsa); } void ospf6_remove_id_from_external_id_table(struct ospf6 *ospf6, @@ -326,7 +327,8 @@ void ospf6_install_lsa(struct ospf6_lsa *lsa) lsa->installed = now; /* Topo change handling */ - if (CHECK_LSA_TOPO_CHG_ELIGIBLE(ntohs(lsa->header->type))) { + if (CHECK_LSA_TOPO_CHG_ELIGIBLE(ntohs(lsa->header->type)) + && !CHECK_FLAG(lsa->flag, OSPF6_LSA_DUPLICATE)) { /* check if it is new lsa ? or existing lsa got modified ?*/ if (!old || OSPF6_LSA_IS_CHANGED(old, lsa)) { @@ -991,6 +993,8 @@ void ospf6_receive_lsa(struct ospf6_neighbor *from, /* if no database copy or received is more recent */ if (old == NULL || ismore_recent < 0) { + bool self_originated; + /* in case we have no database copy */ ismore_recent = -1; @@ -1029,12 +1033,13 @@ void ospf6_receive_lsa(struct ospf6_neighbor *from, reoriginated instance of the LSA not to be rejected by other routers due to MinLSArrival. */ - if (new->header->adv_router - != from->ospf6_if->area->ospf6->router_id) + self_originated = (new->header->adv_router + == from->ospf6_if->area->ospf6->router_id); + if (!self_originated) ospf6_flood(from, new); - /* Received Grace-LSA */ - if (IS_GRACE_LSA(new)) { + /* Received non-self-originated Grace LSA. */ + if (IS_GRACE_LSA(new) && !self_originated) { struct ospf6 *ospf6; ospf6 = ospf6_get_by_lsdb(new); @@ -1088,8 +1093,16 @@ void ospf6_receive_lsa(struct ospf6_neighbor *from, ospf6_acknowledge_lsa(new, ismore_recent, from); /* (f) Self Originated LSA, section 13.4 */ - if (new->header->adv_router - == from->ospf6_if->area->ospf6->router_id) { + if (self_originated) { + if (from->ospf6_if->area->ospf6->gr_info + .restart_in_progress) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "Graceful Restart in progress -- not flushing self-originated LSA: %s", + new->name); + return; + } + /* Self-originated LSA (newer than ours) is received from another router. We have to make a new instance of the @@ -1105,6 +1118,11 @@ void ospf6_receive_lsa(struct ospf6_neighbor *from, &new->refresh); } + struct ospf6 *ospf6 = from->ospf6_if->area->ospf6; + struct ospf6_area *area = from->ospf6_if->area; + if (ospf6->gr_info.restart_in_progress) + ospf6_gr_check_lsdb_consistency(ospf6, area); + return; } diff --git a/ospf6d/ospf6_flood.h b/ospf6d/ospf6_flood.h index 4e4fc55ed4..775d0d289d 100644 --- a/ospf6d/ospf6_flood.h +++ b/ospf6d/ospf6_flood.h @@ -32,7 +32,7 @@ extern struct ospf6_lsdb *ospf6_get_scoped_lsdb(struct ospf6_lsa *lsa); extern struct ospf6_lsdb *ospf6_get_scoped_lsdb_self(struct ospf6_lsa *lsa); /* origination & purging */ -extern void ospf6_lsa_originate(struct ospf6_lsa *lsa); +extern void ospf6_lsa_originate(struct ospf6 *ospf6, struct ospf6_lsa *lsa); extern void ospf6_lsa_originate_process(struct ospf6_lsa *lsa, struct ospf6 *process); extern void ospf6_lsa_originate_area(struct ospf6_lsa *lsa, diff --git a/ospf6d/ospf6_gr.c b/ospf6d/ospf6_gr.c new file mode 100644 index 0000000000..40893ed998 --- /dev/null +++ b/ospf6d/ospf6_gr.c @@ -0,0 +1,749 @@ +/* + * This is an implementation of RFC 5187 Graceful Restart. + * + * Copyright 2021 NetDEF (c), All rights reserved. + * + * 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 "memory.h" +#include "command.h" +#include "table.h" +#include "vty.h" +#include "log.h" +#include "hook.h" +#include "printfrr.h" + +#include "ospf6d/ospf6_lsa.h" +#include "ospf6d/ospf6_lsdb.h" +#include "ospf6d/ospf6_route.h" +#include "ospf6d/ospf6_area.h" +#include "ospf6d/ospf6_interface.h" +#include "ospf6d/ospf6d.h" +#include "ospf6d/ospf6_asbr.h" +#include "ospf6d/ospf6_zebra.h" +#include "ospf6d/ospf6_message.h" +#include "ospf6d/ospf6_neighbor.h" +#include "ospf6d/ospf6_flood.h" +#include "ospf6d/ospf6_intra.h" +#include "ospf6d/ospf6_spf.h" +#include "ospf6d/ospf6_gr.h" +#ifndef VTYSH_EXTRACT_PL +#include "ospf6d/ospf6_gr_clippy.c" +#endif + +static void ospf6_gr_nvm_delete(struct ospf6 *ospf6); + +/* Originate and install Grace-LSA for a given interface. */ +static int ospf6_gr_lsa_originate(struct ospf6_interface *oi) +{ + struct ospf6_gr_info *gr_info = &oi->area->ospf6->gr_info; + struct ospf6_lsa_header *lsa_header; + struct ospf6_grace_lsa *grace_lsa; + struct ospf6_lsa *lsa; + char buffer[OSPF6_MAX_LSASIZE]; + + if (IS_OSPF6_DEBUG_ORIGINATE(LINK)) + zlog_debug("Originate Link-LSA for Interface %s", + oi->interface->name); + + /* prepare buffer */ + memset(buffer, 0, sizeof(buffer)); + lsa_header = (struct ospf6_lsa_header *)buffer; + grace_lsa = + (struct ospf6_grace_lsa *)((caddr_t)lsa_header + + sizeof(struct ospf6_lsa_header)); + + /* Put grace period. */ + grace_lsa->tlv_period.header.type = htons(GRACE_PERIOD_TYPE); + grace_lsa->tlv_period.header.length = htons(GRACE_PERIOD_LENGTH); + grace_lsa->tlv_period.interval = htonl(gr_info->grace_period); + + /* Put restart reason. */ + grace_lsa->tlv_reason.header.type = htons(RESTART_REASON_TYPE); + grace_lsa->tlv_reason.header.length = htons(RESTART_REASON_LENGTH); + if (gr_info->restart_support) + grace_lsa->tlv_reason.reason = OSPF6_GR_SW_RESTART; + else + grace_lsa->tlv_reason.reason = OSPF6_GR_UNKNOWN_RESTART; + + /* Fill LSA Header */ + lsa_header->age = 0; + lsa_header->type = htons(OSPF6_LSTYPE_GRACE_LSA); + lsa_header->id = htonl(oi->interface->ifindex); + lsa_header->adv_router = oi->area->ospf6->router_id; + lsa_header->seqnum = + ospf6_new_ls_seqnum(lsa_header->type, lsa_header->id, + lsa_header->adv_router, oi->lsdb); + lsa_header->length = htons(sizeof(*lsa_header) + sizeof(*grace_lsa)); + + /* LSA checksum */ + ospf6_lsa_checksum(lsa_header); + + /* create LSA */ + lsa = ospf6_lsa_create(lsa_header); + + /* Originate */ + ospf6_lsa_originate_interface(lsa, oi); + + return 0; +} + +/* Flush all self-originated Grace-LSAs. */ +static void ospf6_gr_flush_grace_lsas(struct ospf6 *ospf6) +{ + struct ospf6_area *area; + struct listnode *anode; + + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, anode, area)) { + struct ospf6_lsa *lsa; + struct ospf6_interface *oi; + struct listnode *inode; + + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "GR: flushing self-originated Grace-LSAs [area %pI4]", + &area->area_id); + + for (ALL_LIST_ELEMENTS_RO(area->if_list, inode, oi)) { + lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_GRACE_LSA), + htonl(oi->interface->ifindex), + oi->area->ospf6->router_id, + oi->lsdb); + if (!lsa) { + zlog_warn( + "%s: Grace-LSA not found [interface %s] [area %pI4]", + __func__, oi->interface->name, + &area->area_id); + continue; + } + + ospf6_lsa_purge(lsa); + } + } +} + +/* Exit from the Graceful Restart mode. */ +static void ospf6_gr_restart_exit(struct ospf6 *ospf6, const char *reason) +{ + struct ospf6_area *area; + struct listnode *onode, *anode; + + if (IS_DEBUG_OSPF6_GR) + zlog_debug("GR: exiting graceful restart: %s", reason); + + ospf6->gr_info.restart_in_progress = false; + ospf6->gr_info.finishing_restart = true; + THREAD_OFF(ospf6->gr_info.t_grace_period); + + /* Record in non-volatile memory that the restart is complete. */ + ospf6_gr_nvm_delete(ospf6); + + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, onode, area)) { + struct ospf6_interface *oi; + + /* + * 1) The router should reoriginate its router-LSAs for all + * attached areas in order to make sure they have the correct + * contents. + */ + OSPF6_ROUTER_LSA_EXECUTE(area); + + for (ALL_LIST_ELEMENTS_RO(area->if_list, anode, oi)) { + OSPF6_LINK_LSA_EXECUTE(oi); + + /* + * 2) The router should reoriginate network-LSAs on all + * segments where it is the Designated Router. + */ + if (oi->state == OSPF6_INTERFACE_DR) + OSPF6_NETWORK_LSA_EXECUTE(oi); + } + } + + /* + * 3) The router reruns its OSPF routing calculations, this time + * installing the results into the system forwarding table, and + * originating summary-LSAs, Type-7 LSAs and AS-external-LSAs as + * necessary. + * + * 4) Any remnant entries in the system forwarding table that were + * installed before the restart, but that are no longer valid, + * should be removed. + */ + ospf6_spf_schedule(ospf6, OSPF6_SPF_FLAGS_GR_FINISH); + + /* 6) Any grace-LSAs that the router originated should be flushed. */ + ospf6_gr_flush_grace_lsas(ospf6); +} + +#define RTR_LSA_MISSING 0 +#define RTR_LSA_ADJ_FOUND 1 +#define RTR_LSA_ADJ_NOT_FOUND 2 + +/* Check if a Router-LSA exists and if it contains a given link. */ +static int ospf6_router_lsa_contains_adj(struct ospf6_area *area, + in_addr_t adv_router, + in_addr_t neighbor_router_id) +{ + uint16_t type; + struct ospf6_lsa *lsa; + bool empty = true; + + type = ntohs(OSPF6_LSTYPE_ROUTER); + for (ALL_LSDB_TYPED_ADVRTR(area->lsdb, type, adv_router, lsa)) { + struct ospf6_router_lsa *router_lsa; + char *start, *end, *current; + + empty = false; + router_lsa = (struct ospf6_router_lsa + *)((char *)lsa->header + + sizeof(struct ospf6_lsa_header)); + + /* Iterate over all interfaces in the Router-LSA. */ + start = (char *)router_lsa + sizeof(struct ospf6_router_lsa); + end = (char *)lsa->header + ntohs(lsa->header->length); + for (current = start; + current + sizeof(struct ospf6_router_lsdesc) <= end; + current += sizeof(struct ospf6_router_lsdesc)) { + struct ospf6_router_lsdesc *lsdesc; + + lsdesc = (struct ospf6_router_lsdesc *)current; + if (lsdesc->type != OSPF6_ROUTER_LSDESC_POINTTOPOINT) + continue; + + if (lsdesc->neighbor_router_id == neighbor_router_id) + return RTR_LSA_ADJ_FOUND; + } + } + + if (empty) + return RTR_LSA_MISSING; + + return RTR_LSA_ADJ_NOT_FOUND; +} + +static bool ospf6_gr_check_router_lsa_consistency(struct ospf6 *ospf6, + struct ospf6_area *area, + struct ospf6_lsa *lsa) +{ + if (lsa->header->adv_router == ospf6->router_id) { + struct ospf6_router_lsa *router_lsa; + char *start, *end, *current; + + router_lsa = (struct ospf6_router_lsa + *)((char *)lsa->header + + sizeof(struct ospf6_lsa_header)); + + /* Iterate over all interfaces in the Router-LSA. */ + start = (char *)router_lsa + sizeof(struct ospf6_router_lsa); + end = (char *)lsa->header + ntohs(lsa->header->length); + for (current = start; + current + sizeof(struct ospf6_router_lsdesc) <= end; + current += sizeof(struct ospf6_router_lsdesc)) { + struct ospf6_router_lsdesc *lsdesc; + + lsdesc = (struct ospf6_router_lsdesc *)current; + if (lsdesc->type != OSPF6_ROUTER_LSDESC_POINTTOPOINT) + continue; + + if (ospf6_router_lsa_contains_adj( + area, lsdesc->neighbor_router_id, + ospf6->router_id) + == RTR_LSA_ADJ_NOT_FOUND) + return false; + } + } else { + int adj1, adj2; + + adj1 = ospf6_router_lsa_contains_adj(area, ospf6->router_id, + lsa->header->adv_router); + adj2 = ospf6_router_lsa_contains_adj( + area, lsa->header->adv_router, ospf6->router_id); + if ((adj1 == RTR_LSA_ADJ_FOUND && adj2 == RTR_LSA_ADJ_NOT_FOUND) + || (adj1 == RTR_LSA_ADJ_NOT_FOUND + && adj2 == RTR_LSA_ADJ_FOUND)) + return false; + } + + return true; +} + +/* + * Check for LSAs that are inconsistent with the pre-restart LSAs, and abort the + * ongoing graceful restart when that's the case. + */ +void ospf6_gr_check_lsdb_consistency(struct ospf6 *ospf6, + struct ospf6_area *area) +{ + uint16_t type; + struct ospf6_lsa *lsa; + + type = ntohs(OSPF6_LSTYPE_ROUTER); + for (ALL_LSDB_TYPED(area->lsdb, type, lsa)) { + if (!ospf6_gr_check_router_lsa_consistency(ospf6, area, lsa)) { + char reason[256]; + + snprintfrr(reason, sizeof(reason), + "detected inconsistent LSA %s [area %pI4]", + lsa->name, &area->area_id); + ospf6_gr_restart_exit(ospf6, reason); + return; + } + } +} + +/* Check if there's a fully formed adjacency with the given neighbor ID. */ +static bool ospf6_gr_check_adj_id(struct ospf6_area *area, + in_addr_t neighbor_router_id) +{ + struct ospf6_neighbor *nbr; + + nbr = ospf6_area_neighbor_lookup(area, neighbor_router_id); + if (!nbr || nbr->state < OSPF6_NEIGHBOR_FULL) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug("GR: missing adjacency to router %pI4", + &neighbor_router_id); + return false; + } + + return true; +} + +static bool ospf6_gr_check_adjs_lsa_transit(struct ospf6_area *area, + in_addr_t neighbor_router_id, + uint32_t neighbor_interface_id) +{ + struct ospf6 *ospf6 = area->ospf6; + + /* Check if we are the DR. */ + if (neighbor_router_id == ospf6->router_id) { + struct ospf6_lsa *lsa; + char *start, *end, *current; + struct ospf6_network_lsa *network_lsa; + struct ospf6_network_lsdesc *lsdesc; + + /* Lookup Network LSA corresponding to this interface. */ + lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_NETWORK), + neighbor_interface_id, + neighbor_router_id, area->lsdb); + if (!lsa) + return false; + + /* Iterate over all routers present in the network. */ + network_lsa = (struct ospf6_network_lsa + *)((char *)lsa->header + + sizeof(struct ospf6_lsa_header)); + start = (char *)network_lsa + sizeof(struct ospf6_network_lsa); + end = (char *)lsa->header + ntohs(lsa->header->length); + for (current = start; + current + sizeof(struct ospf6_network_lsdesc) <= end; + current += sizeof(struct ospf6_network_lsdesc)) { + lsdesc = (struct ospf6_network_lsdesc *)current; + + /* Skip self in the pseudonode. */ + if (lsdesc->router_id == ospf6->router_id) + continue; + + /* + * Check if there's a fully formed adjacency with this + * router. + */ + if (!ospf6_gr_check_adj_id(area, lsdesc->router_id)) + return false; + } + } else { + struct ospf6_neighbor *nbr; + + /* Check if there's a fully formed adjacency with the DR. */ + nbr = ospf6_area_neighbor_lookup(area, neighbor_router_id); + if (!nbr || nbr->state < OSPF6_NEIGHBOR_FULL) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "GR: missing adjacency to DR router %pI4", + &neighbor_router_id); + return false; + } + } + + return true; +} + +static bool ospf6_gr_check_adjs_lsa(struct ospf6_area *area, + struct ospf6_lsa *lsa) +{ + struct ospf6_router_lsa *router_lsa; + char *start, *end, *current; + + router_lsa = + (struct ospf6_router_lsa *)((char *)lsa->header + + sizeof(struct ospf6_lsa_header)); + + /* Iterate over all interfaces in the Router-LSA. */ + start = (char *)router_lsa + sizeof(struct ospf6_router_lsa); + end = (char *)lsa->header + ntohs(lsa->header->length); + for (current = start; + current + sizeof(struct ospf6_router_lsdesc) <= end; + current += sizeof(struct ospf6_router_lsdesc)) { + struct ospf6_router_lsdesc *lsdesc; + + lsdesc = (struct ospf6_router_lsdesc *)current; + switch (lsdesc->type) { + case OSPF6_ROUTER_LSDESC_POINTTOPOINT: + if (!ospf6_gr_check_adj_id(area, + lsdesc->neighbor_router_id)) + return false; + break; + case OSPF6_ROUTER_LSDESC_TRANSIT_NETWORK: + if (!ospf6_gr_check_adjs_lsa_transit( + area, lsdesc->neighbor_router_id, + lsdesc->neighbor_interface_id)) + return false; + break; + default: + break; + } + } + + return true; +} + +/* + * Check if all adjacencies prior to the restart were reestablished. + * + * This is done using pre-restart Router LSAs and pre-restart Network LSAs + * received from the helping neighbors. + */ +static bool ospf6_gr_check_adjs(struct ospf6 *ospf6) +{ + struct ospf6_area *area; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, area)) { + uint16_t type; + uint32_t router; + struct ospf6_lsa *lsa_self; + bool found = false; + + type = ntohs(OSPF6_LSTYPE_ROUTER); + router = ospf6->router_id; + for (ALL_LSDB_TYPED_ADVRTR(area->lsdb, type, router, + lsa_self)) { + found = true; + if (!ospf6_gr_check_adjs_lsa(area, lsa_self)) + return false; + } + if (!found) + return false; + } + + return true; +} + +/* Handling of grace period expiry. */ +static int ospf6_gr_grace_period_expired(struct thread *thread) +{ + struct ospf6 *ospf6 = THREAD_ARG(thread); + + ospf6->gr_info.t_grace_period = NULL; + ospf6_gr_restart_exit(ospf6, "grace period has expired"); + + return 0; +} + +/* + * Record in non-volatile memory that the given OSPF instance is attempting to + * perform a graceful restart. + */ +static void ospf6_gr_nvm_update(struct ospf6 *ospf6) +{ + const char *inst_name; + json_object *json; + json_object *json_instances; + json_object *json_instance; + + inst_name = ospf6->name ? ospf6->name : VRF_DEFAULT_NAME; + + json = json_object_from_file((char *)OSPF6D_GR_STATE); + if (json == NULL) + json = json_object_new_object(); + + json_object_object_get_ex(json, "instances", &json_instances); + if (!json_instances) { + json_instances = json_object_new_object(); + json_object_object_add(json, "instances", json_instances); + } + + json_object_object_get_ex(json_instances, inst_name, &json_instance); + if (!json_instance) { + json_instance = json_object_new_object(); + json_object_object_add(json_instances, inst_name, + json_instance); + } + + /* + * Record not only the grace period, but also a UNIX timestamp + * corresponding to the end of that period. That way, once ospf6d is + * restarted, it will be possible to take into account the time that + * passed while ospf6d wasn't running. + */ + json_object_int_add(json_instance, "gracePeriod", + ospf6->gr_info.grace_period); + json_object_int_add(json_instance, "timestamp", + time(NULL) + ospf6->gr_info.grace_period); + + json_object_to_file_ext((char *)OSPF6D_GR_STATE, json, + JSON_C_TO_STRING_PRETTY); + json_object_free(json); +} + +/* + * Delete GR status information about the given OSPF instance from non-volatile + * memory. + */ +static void ospf6_gr_nvm_delete(struct ospf6 *ospf6) +{ + const char *inst_name; + json_object *json; + json_object *json_instances; + + inst_name = ospf6->name ? ospf6->name : VRF_DEFAULT_NAME; + + json = json_object_from_file((char *)OSPF6D_GR_STATE); + if (json == NULL) + json = json_object_new_object(); + + json_object_object_get_ex(json, "instances", &json_instances); + if (!json_instances) { + json_instances = json_object_new_object(); + json_object_object_add(json, "instances", json_instances); + } + + json_object_object_del(json_instances, inst_name); + + json_object_to_file_ext((char *)OSPF6D_GR_STATE, json, + JSON_C_TO_STRING_PRETTY); + json_object_free(json); +} + +/* + * Fetch from non-volatile memory whether the given OSPF instance is performing + * a graceful shutdown or not. + */ +void ospf6_gr_nvm_read(struct ospf6 *ospf6) +{ + const char *inst_name; + json_object *json; + json_object *json_instances; + json_object *json_instance; + json_object *json_timestamp; + time_t timestamp = 0; + + inst_name = ospf6->name ? ospf6->name : VRF_DEFAULT_NAME; + + json = json_object_from_file((char *)OSPF6D_GR_STATE); + if (json == NULL) + json = json_object_new_object(); + + json_object_object_get_ex(json, "instances", &json_instances); + if (!json_instances) { + json_instances = json_object_new_object(); + json_object_object_add(json, "instances", json_instances); + } + + json_object_object_get_ex(json_instances, inst_name, &json_instance); + if (!json_instance) { + json_instance = json_object_new_object(); + json_object_object_add(json_instances, inst_name, + json_instance); + } + + json_object_object_get_ex(json_instance, "timestamp", &json_timestamp); + if (json_timestamp) { + time_t now; + unsigned long remaining_time; + + /* Check if the grace period has already expired. */ + now = time(NULL); + timestamp = json_object_get_int(json_timestamp); + if (now > timestamp) { + ospf6_gr_restart_exit( + ospf6, "grace period has expired already"); + } else { + /* Schedule grace period timeout. */ + ospf6->gr_info.restart_in_progress = true; + remaining_time = timestamp - time(NULL); + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "GR: remaining time until grace period expires: %lu(s)", + remaining_time); + thread_add_timer(master, ospf6_gr_grace_period_expired, + ospf6, remaining_time, + &ospf6->gr_info.t_grace_period); + } + } + + json_object_object_del(json_instances, inst_name); + + json_object_to_file_ext((char *)OSPF6D_GR_STATE, json, + JSON_C_TO_STRING_PRETTY); + json_object_free(json); +} + +/* Prepare to start a Graceful Restart. */ +static void ospf6_gr_prepare(void) +{ + struct ospf6 *ospf6; + struct ospf6_interface *oi; + struct listnode *onode, *anode, *inode; + + for (ALL_LIST_ELEMENTS_RO(om6->ospf6, onode, ospf6)) { + struct ospf6_area *area; + + if (!ospf6->gr_info.restart_support + || ospf6->gr_info.prepare_in_progress) + continue; + + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "GR: preparing to perform a graceful restart [period %u second(s)] [vrf %s]", + ospf6->gr_info.grace_period, + ospf6_vrf_id_to_name(ospf6->vrf_id)); + + /* Freeze OSPF routes in the RIB. */ + if (ospf6_zebra_gr_enable(ospf6, ospf6->gr_info.grace_period)) { + zlog_warn( + "%s: failed to activate graceful restart: not connected to zebra", + __func__); + continue; + } + + /* Send a Grace-LSA to all neighbors. */ + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, anode, area)) { + for (ALL_LIST_ELEMENTS_RO(area->if_list, inode, oi)) { + if (oi->state < OSPF6_INTERFACE_POINTTOPOINT) + continue; + ospf6_gr_lsa_originate(oi); + } + } + + /* Record end of the grace period in non-volatile memory. */ + ospf6_gr_nvm_update(ospf6); + + /* + * Mark that a Graceful Restart preparation is in progress, to + * prevent ospf6d from flushing its self-originated LSAs on + * exit. + */ + ospf6->gr_info.prepare_in_progress = true; + } +} + +static int ospf6_gr_neighbor_change(struct ospf6_neighbor *on, int next_state, + int prev_state) +{ + struct ospf6 *ospf6 = on->ospf6_if->area->ospf6; + + if (next_state == OSPF6_NEIGHBOR_FULL + && ospf6->gr_info.restart_in_progress) { + if (ospf6_gr_check_adjs(ospf6)) { + ospf6_gr_restart_exit( + ospf6, "all adjacencies were reestablished"); + } else { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "GR: not all adjacencies were reestablished yet"); + } + } + + return 0; +} + +int config_write_ospf6_gr(struct vty *vty, struct ospf6 *ospf6) +{ + if (!ospf6->gr_info.restart_support) + return 0; + + if (ospf6->gr_info.grace_period == OSPF6_DFLT_GRACE_INTERVAL) + vty_out(vty, " graceful-restart\n"); + else + vty_out(vty, " graceful-restart grace-period %u\n", + ospf6->gr_info.grace_period); + + return 0; +} + +DEFPY(ospf6_graceful_restart_prepare, ospf6_graceful_restart_prepare_cmd, + "graceful-restart prepare ipv6 ospf", + "Graceful Restart commands\n" + "Prepare upcoming graceful restart\n" IPV6_STR + "Prepare to restart the OSPFv3 process") +{ + ospf6_gr_prepare(); + + return CMD_SUCCESS; +} + +DEFPY(ospf6_graceful_restart, ospf6_graceful_restart_cmd, + "graceful-restart [grace-period (1-1800)$grace_period]", + OSPF_GR_STR + "Maximum length of the 'grace period'\n" + "Maximum length of the 'grace period' in seconds\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf6, ospf6); + + /* Check and get restart period if present. */ + if (!grace_period_str) + grace_period = OSPF6_DFLT_GRACE_INTERVAL; + + ospf6->gr_info.restart_support = true; + ospf6->gr_info.grace_period = grace_period; + + return CMD_SUCCESS; +} + +DEFPY(ospf6_no_graceful_restart, ospf6_no_graceful_restart_cmd, + "no graceful-restart [period (1-1800)]", + NO_STR OSPF_GR_STR + "Maximum length of the 'grace period'\n" + "Maximum length of the 'grace period' in seconds\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf6, ospf6); + + if (!ospf6->gr_info.restart_support) + return CMD_SUCCESS; + + if (ospf6->gr_info.prepare_in_progress) { + vty_out(vty, + "%% Error: Graceful Restart preparation in progress\n"); + return CMD_WARNING; + } + + ospf6->gr_info.restart_support = false; + ospf6->gr_info.grace_period = OSPF6_DFLT_GRACE_INTERVAL; + + return CMD_SUCCESS; +} + +void ospf6_gr_init(void) +{ + hook_register(ospf6_neighbor_change, ospf6_gr_neighbor_change); + + install_element(ENABLE_NODE, &ospf6_graceful_restart_prepare_cmd); + install_element(OSPF6_NODE, &ospf6_graceful_restart_cmd); + install_element(OSPF6_NODE, &ospf6_no_graceful_restart_cmd); +} diff --git a/ospf6d/ospf6_gr.h b/ospf6d/ospf6_gr.h index 378b7193cd..6406e8efee 100644 --- a/ospf6d/ospf6_gr.h +++ b/ospf6d/ospf6_gr.h @@ -32,6 +32,10 @@ #define OSPF6_MAX_GRACE_INTERVAL 1800 #define OSPF6_MIN_GRACE_INTERVAL 1 +#define OSPF6_DFLT_GRACE_INTERVAL 120 + +/* Forward declaration(s). */ +struct ospf6_neighbor; /* Debug option */ extern unsigned char conf_debug_ospf6_gr; @@ -67,7 +71,8 @@ enum ospf6_gr_helper_rejected_reason { OSPF6_HELPER_NOT_A_VALID_NEIGHBOUR, OSPF6_HELPER_PLANNED_ONLY_RESTART, OSPF6_HELPER_TOPO_CHANGE_RTXMT_LIST, - OSPF6_HELPER_LSA_AGE_MORE + OSPF6_HELPER_LSA_AGE_MORE, + OSPF6_HELPER_RESTARTING, }; #ifdef roundup @@ -119,6 +124,11 @@ struct grace_tlv_restart_reason { #define OSPF6_GRACE_LSA_MIN_SIZE \ GRACE_PERIOD_TLV_SIZE + GRACE_RESTART_REASON_TLV_SIZE +struct ospf6_grace_lsa { + struct grace_tlv_graceperiod tlv_period; + struct grace_tlv_restart_reason tlv_reason; +}; + struct advRtr { in_addr_t advRtrAddr; }; @@ -156,6 +166,13 @@ extern void ospf6_process_maxage_grace_lsa(struct ospf6 *ospf, struct ospf6_neighbor *nbr); extern void ospf6_helper_handle_topo_chg(struct ospf6 *ospf6, struct ospf6_lsa *lsa); +extern int config_write_ospf6_gr(struct vty *vty, struct ospf6 *ospf6); extern int config_write_ospf6_gr_helper(struct vty *vty, struct ospf6 *ospf6); extern int config_write_ospf6_debug_gr_helper(struct vty *vty); + +extern void ospf6_gr_check_lsdb_consistency(struct ospf6 *ospf, + struct ospf6_area *area); +extern void ospf6_gr_nvm_read(struct ospf6 *ospf); +extern void ospf6_gr_init(void); + #endif /* OSPF6_GR_H */ diff --git a/ospf6d/ospf6_gr_helper.c b/ospf6d/ospf6_gr_helper.c index 76496c050f..4522bd2619 100644 --- a/ospf6d/ospf6_gr_helper.c +++ b/ospf6d/ospf6_gr_helper.c @@ -360,6 +360,16 @@ int ospf6_process_grace_lsa(struct ospf6 *ospf6, struct ospf6_lsa *lsa, return OSPF6_GR_NOT_HELPER; } + if (ospf6->gr_info.restart_in_progress) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "%s: router is in the process of graceful restart", + __func__); + restarter->gr_helper_info.rejected_reason = + OSPF6_HELPER_RESTARTING; + return OSPF6_GR_NOT_HELPER; + } + /* check supported grace period configured * if configured, use this to start the grace * timer otherwise use the interval received @@ -1155,7 +1165,10 @@ DEFPY(show_ipv6_ospf6_gr_helper, bool detail = false; ospf6 = ospf6_lookup_by_vrf_name(VRF_DEFAULT_NAME); - OSPF6_CMD_CHECK_RUNNING(); + if (ospf6 == NULL) { + vty_out(vty, "OSPFv3 is not configured\n"); + return CMD_SUCCESS; + } if (argv_find(argv, argc, "detail", &idx)) detail = true; diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c index b427a0c9bd..b63a3c02db 100644 --- a/ospf6d/ospf6_interface.c +++ b/ospf6d/ospf6_interface.c @@ -1330,7 +1330,6 @@ DEFUN(show_ipv6_ospf6_interface, show_ipv6_ospf6_interface_ifname_cmd, bool all_vrf = false; int idx_vrf = 0; - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) { idx_ifname += 2; @@ -1547,7 +1546,6 @@ DEFUN(show_ipv6_ospf6_interface_traffic, show_ipv6_ospf6_interface_traffic_cmd, bool all_vrf = false; int idx_vrf = 0; - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) { @@ -1590,7 +1588,6 @@ DEFUN(show_ipv6_ospf6_interface_ifname_prefix, bool all_vrf = false; int idx_vrf = 0; - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) { idx_ifname += 2; @@ -1651,7 +1648,6 @@ DEFUN(show_ipv6_ospf6_interface_prefix, show_ipv6_ospf6_interface_prefix_cmd, bool all_vrf = false; int idx_vrf = 0; - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) idx_prefix += 2; diff --git a/ospf6d/ospf6_interface.h b/ospf6d/ospf6_interface.h index ccdf8b1c8f..ee24b989bd 100644 --- a/ospf6d/ospf6_interface.h +++ b/ospf6d/ospf6_interface.h @@ -193,23 +193,23 @@ extern void ospf6_interface_stop(struct ospf6_interface *oi); extern struct ospf6_interface * ospf6_interface_lookup_by_ifindex(ifindex_t, vrf_id_t vrf_id); -extern struct ospf6_interface *ospf6_interface_create(struct interface *); -extern void ospf6_interface_delete(struct ospf6_interface *); +extern struct ospf6_interface *ospf6_interface_create(struct interface *ifp); +extern void ospf6_interface_delete(struct ospf6_interface *oi); -extern void ospf6_interface_enable(struct ospf6_interface *); -extern void ospf6_interface_disable(struct ospf6_interface *); +extern void ospf6_interface_enable(struct ospf6_interface *oi); +extern void ospf6_interface_disable(struct ospf6_interface *oi); -extern void ospf6_interface_state_update(struct interface *); -extern void ospf6_interface_connected_route_update(struct interface *); +extern void ospf6_interface_state_update(struct interface *ifp); +extern void ospf6_interface_connected_route_update(struct interface *ifp); extern struct in6_addr * ospf6_interface_get_global_address(struct interface *ifp); /* interface event */ -extern int interface_up(struct thread *); -extern int interface_down(struct thread *); -extern int wait_timer(struct thread *); -extern int backup_seen(struct thread *); -extern int neighbor_change(struct thread *); +extern int interface_up(struct thread *thread); +extern int interface_down(struct thread *thread); +extern int wait_timer(struct thread *thread); +extern int backup_seen(struct thread *thread); +extern int neighbor_change(struct thread *thread); extern void ospf6_interface_init(void); extern void ospf6_interface_clear(struct interface *ifp); diff --git a/ospf6d/ospf6_intra.c b/ospf6d/ospf6_intra.c index e4db8f3a02..85a2713c0a 100644 --- a/ospf6d/ospf6_intra.c +++ b/ospf6d/ospf6_intra.c @@ -47,6 +47,7 @@ #include "ospf6_flood.h" #include "ospf6d.h" #include "ospf6_spf.h" +#include "ospf6_gr.h" unsigned char conf_debug_ospf6_brouter = 0; uint32_t conf_debug_ospf6_brouter_specific_router_id; @@ -249,6 +250,13 @@ int ospf6_router_lsa_originate(struct thread *thread) oa = (struct ospf6_area *)THREAD_ARG(thread); oa->thread_router_lsa = NULL; + if (oa->ospf6->gr_info.restart_in_progress) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "Graceful Restart in progress, don't originate LSA"); + return 0; + } + if (IS_OSPF6_DEBUG_ORIGINATE(ROUTER)) zlog_debug("Originate Router-LSA for Area %s", oa->name); @@ -532,6 +540,13 @@ int ospf6_network_lsa_originate(struct thread *thread) by ospf6_lsa_refresh (), and does not come here. */ assert(oi->area); + if (oi->area->ospf6->gr_info.restart_in_progress) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "Graceful Restart in progress, don't originate LSA"); + return 0; + } + old = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_NETWORK), htonl(oi->interface->ifindex), oi->area->ospf6->router_id, oi->area->lsdb); @@ -773,6 +788,14 @@ int ospf6_link_lsa_originate(struct thread *thread) assert(oi->area); + if (oi->area->ospf6->gr_info.restart_in_progress) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "Graceful Restart in progress, don't originate LSA"); + return 0; + } + + /* find previous LSA */ old = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_LINK), htonl(oi->interface->ifindex), @@ -1009,6 +1032,13 @@ int ospf6_intra_prefix_lsa_originate_stub(struct thread *thread) oa = (struct ospf6_area *)THREAD_ARG(thread); oa->thread_intra_prefix_lsa = NULL; + if (oa->ospf6->gr_info.restart_in_progress) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "Graceful Restart in progress, don't originate LSA"); + return 0; + } + /* find previous LSA */ old = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_INTRA_PREFIX), htonl(0), oa->ospf6->router_id, oa->lsdb); @@ -1243,6 +1273,13 @@ int ospf6_intra_prefix_lsa_originate_transit(struct thread *thread) assert(oi->area); + if (oi->area->ospf6->gr_info.restart_in_progress) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "Graceful Restart in progress, don't originate LSA"); + return 0; + } + /* find previous LSA */ old = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_INTRA_PREFIX), htonl(oi->interface->ifindex), @@ -1470,8 +1507,14 @@ void ospf6_intra_prefix_route_ecmp_path(struct ospf6_area *oa, for (old_route = old; old_route; old_route = old_route->next) { bool route_updated = false; - if (!ospf6_route_is_same(old_route, route) || - (old_route->path.type != route->path.type)) + /* The route linked-list is grouped in batches of prefix. + * If the new prefix is not the same as the one of interest + * then we have walked over the end of the batch and so we + * should break rather than continuing unnecessarily. + */ + if (!ospf6_route_is_same(old_route, route)) + break; + if (old_route->path.type != route->path.type) continue; /* Current and New route has same origin, @@ -1569,8 +1612,14 @@ void ospf6_intra_prefix_route_ecmp_path(struct ospf6_area *oa, for (old_route = old; old_route; old_route = old_route->next) { - if (!ospf6_route_is_same(old_route, route) || - (old_route->path.type != route->path.type)) + /* The route linked-list is grouped in batches of prefix. + * If the new prefix is not the same as the one of interest + * then we have walked over the end of the batch and so we + * should break rather than continuing unnecessarily. + */ + if (!ospf6_route_is_same(old_route, route)) + break; + if (old_route->path.type != route->path.type) continue; /* Old Route and New Route have Equal Cost, Merge NHs */ @@ -2304,7 +2353,7 @@ void ospf6_intra_brouter_calculation(struct ospf6_area *oa) * the table. For an example, ospf6_abr_examin_summary, * removes brouters which are marked for remove. */ - oa->intra_brouter_calc = 1; + oa->intra_brouter_calc = true; ospf6_route_remove(brouter, oa->ospf6->brouter_table); brouter = NULL; } else if (CHECK_FLAG(brouter->flag, OSPF6_ROUTE_ADD) @@ -2337,7 +2386,7 @@ void ospf6_intra_brouter_calculation(struct ospf6_area *oa) UNSET_FLAG(brouter->flag, OSPF6_ROUTE_CHANGE); } /* Reset for nbrouter */ - oa->intra_brouter_calc = 0; + oa->intra_brouter_calc = false; } if (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID(oa->area_id) || diff --git a/ospf6d/ospf6_intra.h b/ospf6d/ospf6_intra.h index 9c29681dee..f15bf0b9b4 100644 --- a/ospf6d/ospf6_intra.h +++ b/ospf6d/ospf6_intra.h @@ -192,12 +192,26 @@ struct ospf6_intra_prefix_lsa { oi, 0, &(oi)->thread_as_extern_lsa); \ } while (0) +#define OSPF6_ROUTER_LSA_EXECUTE(oa) \ + do { \ + if (CHECK_FLAG((oa)->flag, OSPF6_AREA_ENABLE)) \ + thread_execute(master, ospf6_router_lsa_originate, oa, \ + 0); \ + } while (0) + #define OSPF6_NETWORK_LSA_EXECUTE(oi) \ do { \ THREAD_OFF((oi)->thread_network_lsa); \ thread_execute(master, ospf6_network_lsa_originate, oi, 0); \ } while (0) +#define OSPF6_LINK_LSA_EXECUTE(oi) \ + do { \ + if (!CHECK_FLAG((oi)->flag, OSPF6_INTERFACE_DISABLE)) \ + thread_execute(master, ospf6_link_lsa_originate, oi, \ + 0); \ + } while (0) + #define OSPF6_INTRA_PREFIX_LSA_EXECUTE_TRANSIT(oi) \ do { \ THREAD_OFF((oi)->thread_intra_prefix_lsa); \ @@ -221,11 +235,11 @@ extern char *ospf6_network_lsdesc_lookup(uint32_t router_id, struct ospf6_lsa *lsa); extern int ospf6_router_is_stub_router(struct ospf6_lsa *lsa); -extern int ospf6_router_lsa_originate(struct thread *); -extern int ospf6_network_lsa_originate(struct thread *); -extern int ospf6_link_lsa_originate(struct thread *); -extern int ospf6_intra_prefix_lsa_originate_transit(struct thread *); -extern int ospf6_intra_prefix_lsa_originate_stub(struct thread *); +extern int ospf6_router_lsa_originate(struct thread *thread); +extern int ospf6_network_lsa_originate(struct thread *thread); +extern int ospf6_link_lsa_originate(struct thread *thread); +extern int ospf6_intra_prefix_lsa_originate_transit(struct thread *thread); +extern int ospf6_intra_prefix_lsa_originate_stub(struct thread *thread); extern void ospf6_intra_prefix_lsa_add(struct ospf6_lsa *lsa); extern void ospf6_intra_prefix_lsa_remove(struct ospf6_lsa *lsa); extern int ospf6_orig_as_external_lsa(struct thread *thread); diff --git a/ospf6d/ospf6_lsa.c b/ospf6d/ospf6_lsa.c index 1bc1ce9cdf..ac07704d2c 100644 --- a/ospf6d/ospf6_lsa.c +++ b/ospf6d/ospf6_lsa.c @@ -1021,6 +1021,30 @@ static char *ospf6_lsa_handler_name(const struct ospf6_lsa_handler *h) return buf; } +DEFPY (debug_ospf6_lsa_all, + debug_ospf6_lsa_all_cmd, + "[no$no] debug ospf6 lsa all", + NO_STR + DEBUG_STR + OSPF6_STR + "Debug Link State Advertisements (LSAs)\n" + "Display for all types of LSAs\n") +{ + unsigned int i; + struct ospf6_lsa_handler *handler = NULL; + + for (i = 0; i < vector_active(ospf6_lsa_handler_vector); i++) { + handler = vector_slot(ospf6_lsa_handler_vector, i); + if (handler == NULL) + continue; + if (!no) + SET_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG_ALL); + else + UNSET_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG_ALL); + } + return CMD_SUCCESS; +} + DEFPY (debug_ospf6_lsa_aggregation, debug_ospf6_lsa_aggregation_cmd, "[no] debug ospf6 lsa aggregation", @@ -1152,6 +1176,8 @@ DEFUN (no_debug_ospf6_lsa_type, void install_element_ospf6_debug_lsa(void) { + install_element(ENABLE_NODE, &debug_ospf6_lsa_all_cmd); + install_element(CONFIG_NODE, &debug_ospf6_lsa_all_cmd); install_element(ENABLE_NODE, &debug_ospf6_lsa_hex_cmd); install_element(ENABLE_NODE, &no_debug_ospf6_lsa_hex_cmd); install_element(CONFIG_NODE, &debug_ospf6_lsa_hex_cmd); @@ -1165,6 +1191,23 @@ int config_write_ospf6_debug_lsa(struct vty *vty) { unsigned int i; const struct ospf6_lsa_handler *handler; + bool debug_all = true; + + for (i = 0; i < vector_active(ospf6_lsa_handler_vector); i++) { + handler = vector_slot(ospf6_lsa_handler_vector, i); + if (handler == NULL) + continue; + if (CHECK_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG_ALL) + < OSPF6_LSA_DEBUG_ALL) { + debug_all = false; + break; + } + } + + if (debug_all) { + vty_out(vty, "debug ospf6 lsa all\n"); + return 0; + } for (i = 0; i < vector_active(ospf6_lsa_handler_vector); i++) { handler = vector_slot(ospf6_lsa_handler_vector, i); diff --git a/ospf6d/ospf6_lsa.h b/ospf6d/ospf6_lsa.h index a8ed9132dd..2316040694 100644 --- a/ospf6d/ospf6_lsa.h +++ b/ospf6d/ospf6_lsa.h @@ -28,6 +28,9 @@ #define OSPF6_LSA_DEBUG_ORIGINATE 0x02 #define OSPF6_LSA_DEBUG_EXAMIN 0x04 #define OSPF6_LSA_DEBUG_FLOOD 0x08 +#define OSPF6_LSA_DEBUG_ALL \ + (OSPF6_LSA_DEBUG | OSPF6_LSA_DEBUG_ORIGINATE | OSPF6_LSA_DEBUG_EXAMIN \ + | OSPF6_LSA_DEBUG_FLOOD) #define OSPF6_LSA_DEBUG_AGGR 0x10 /* OSPF LSA Default metric values */ @@ -231,10 +234,11 @@ extern int metric_type(struct ospf6 *ospf6, int type, uint8_t instance); extern int metric_value(struct ospf6 *ospf6, int type, uint8_t instance); extern int ospf6_lsa_is_differ(struct ospf6_lsa *lsa1, struct ospf6_lsa *lsa2); extern int ospf6_lsa_is_changed(struct ospf6_lsa *lsa1, struct ospf6_lsa *lsa2); -extern uint16_t ospf6_lsa_age_current(struct ospf6_lsa *); -extern void ospf6_lsa_age_update_to_send(struct ospf6_lsa *, uint32_t); -extern void ospf6_lsa_premature_aging(struct ospf6_lsa *); -extern int ospf6_lsa_compare(struct ospf6_lsa *, struct ospf6_lsa *); +extern uint16_t ospf6_lsa_age_current(struct ospf6_lsa *lsa); +extern void ospf6_lsa_age_update_to_send(struct ospf6_lsa *lsa, + uint32_t transdelay); +extern void ospf6_lsa_premature_aging(struct ospf6_lsa *lsa); +extern int ospf6_lsa_compare(struct ospf6_lsa *lsa1, struct ospf6_lsa *lsa2); extern char *ospf6_lsa_printbuf(struct ospf6_lsa *lsa, char *buf, int size); extern void ospf6_lsa_header_print_raw(struct ospf6_lsa_header *header); @@ -254,16 +258,16 @@ extern struct ospf6_lsa *ospf6_lsa_create(struct ospf6_lsa_header *header); extern struct ospf6_lsa * ospf6_lsa_create_headeronly(struct ospf6_lsa_header *header); extern void ospf6_lsa_delete(struct ospf6_lsa *lsa); -extern struct ospf6_lsa *ospf6_lsa_copy(struct ospf6_lsa *); +extern struct ospf6_lsa *ospf6_lsa_copy(struct ospf6_lsa *lsa); extern struct ospf6_lsa *ospf6_lsa_lock(struct ospf6_lsa *lsa); extern struct ospf6_lsa *ospf6_lsa_unlock(struct ospf6_lsa *lsa); -extern int ospf6_lsa_expire(struct thread *); -extern int ospf6_lsa_refresh(struct thread *); +extern int ospf6_lsa_expire(struct thread *thread); +extern int ospf6_lsa_refresh(struct thread *thread); -extern unsigned short ospf6_lsa_checksum(struct ospf6_lsa_header *); -extern int ospf6_lsa_checksum_valid(struct ospf6_lsa_header *); +extern unsigned short ospf6_lsa_checksum(struct ospf6_lsa_header *lsah); +extern int ospf6_lsa_checksum_valid(struct ospf6_lsa_header *lsah); extern int ospf6_lsa_prohibited_duration(uint16_t type, uint32_t id, uint32_t adv_router, void *scope); diff --git a/ospf6d/ospf6_message.c b/ospf6d/ospf6_message.c index 3dcc74589a..53f3c3468a 100644 --- a/ospf6d/ospf6_message.c +++ b/ospf6d/ospf6_message.c @@ -553,6 +553,21 @@ static void ospf6_hello_recv(struct in6_addr *src, struct in6_addr *dst, return; } + /* + * RFC 3623 - Section 2: + * "If the restarting router determines that it was the Designated + * Router on a given segment prior to the restart, it elects + * itself as the Designated Router again. The restarting router + * knows that it was the Designated Router if, while the + * associated interface is in Waiting state, a Hello packet is + * received from a neighbor listing the router as the Designated + * Router". + */ + if (oi->area->ospf6->gr_info.restart_in_progress + && oi->state == OSPF6_INTERFACE_WAITING + && hello->drouter == oi->area->ospf6->router_id) + oi->drouter = hello->drouter; + /* Schedule interface events */ if (backupseen) thread_add_event(master, backup_seen, oi, 0, NULL); diff --git a/ospf6d/ospf6_neighbor.c b/ospf6d/ospf6_neighbor.c index 4ea615f32b..35fbd3991c 100644 --- a/ospf6d/ospf6_neighbor.c +++ b/ospf6d/ospf6_neighbor.c @@ -90,6 +90,22 @@ struct ospf6_neighbor *ospf6_neighbor_lookup(uint32_t router_id, return (struct ospf6_neighbor *)NULL; } +struct ospf6_neighbor *ospf6_area_neighbor_lookup(struct ospf6_area *area, + uint32_t router_id) +{ + struct ospf6_interface *oi; + struct ospf6_neighbor *nbr; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(area->if_list, node, oi)) { + nbr = ospf6_neighbor_lookup(router_id, oi); + if (nbr) + return nbr; + } + + return NULL; +} + /* create ospf6_neighbor */ struct ospf6_neighbor *ospf6_neighbor_create(uint32_t router_id, struct ospf6_interface *oi) @@ -1081,7 +1097,6 @@ DEFUN(show_ipv6_ospf6_neighbor, show_ipv6_ospf6_neighbor_cmd, bool detail = false; bool drchoice = false; - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (argv_find(argv, argc, "detail", &idx_type)) @@ -1156,7 +1171,6 @@ DEFUN(show_ipv6_ospf6_neighbor_one, show_ipv6_ospf6_neighbor_one_cmd, bool all_vrf = false; int idx_vrf = 0; - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) idx_ipv4 += 2; diff --git a/ospf6d/ospf6_neighbor.h b/ospf6d/ospf6_neighbor.h index a229897226..f7735b87b9 100644 --- a/ospf6d/ospf6_neighbor.h +++ b/ospf6d/ospf6_neighbor.h @@ -23,6 +23,9 @@ #include "hook.h" +/* Forward declaration(s). */ +struct ospf6_area; + /* Debug option */ extern unsigned char conf_debug_ospf6_neighbor; #define OSPF6_DEBUG_NEIGHBOR_STATE 0x01 @@ -183,24 +186,26 @@ extern const char *const ospf6_neighbor_state_str[]; int ospf6_neighbor_cmp(void *va, void *vb); void ospf6_neighbor_dbex_init(struct ospf6_neighbor *on); -struct ospf6_neighbor *ospf6_neighbor_lookup(uint32_t, - struct ospf6_interface *); -struct ospf6_neighbor *ospf6_neighbor_create(uint32_t, - struct ospf6_interface *); -void ospf6_neighbor_delete(struct ospf6_neighbor *); +struct ospf6_neighbor *ospf6_neighbor_lookup(uint32_t router_id, + struct ospf6_interface *oi); +struct ospf6_neighbor *ospf6_area_neighbor_lookup(struct ospf6_area *area, + uint32_t router_id); +struct ospf6_neighbor *ospf6_neighbor_create(uint32_t router_id, + struct ospf6_interface *oi); +void ospf6_neighbor_delete(struct ospf6_neighbor *on); /* Neighbor event */ -extern int hello_received(struct thread *); -extern int twoway_received(struct thread *); -extern int negotiation_done(struct thread *); -extern int exchange_done(struct thread *); -extern int loading_done(struct thread *); -extern int adj_ok(struct thread *); -extern int seqnumber_mismatch(struct thread *); -extern int bad_lsreq(struct thread *); -extern int oneway_received(struct thread *); -extern int inactivity_timer(struct thread *); -extern void ospf6_check_nbr_loading(struct ospf6_neighbor *); +extern int hello_received(struct thread *thread); +extern int twoway_received(struct thread *thread); +extern int negotiation_done(struct thread *thread); +extern int exchange_done(struct thread *thread); +extern int loading_done(struct thread *thread); +extern int adj_ok(struct thread *thread); +extern int seqnumber_mismatch(struct thread *thread); +extern int bad_lsreq(struct thread *thread); +extern int oneway_received(struct thread *thread); +extern int inactivity_timer(struct thread *thread); +extern void ospf6_check_nbr_loading(struct ospf6_neighbor *on); extern void ospf6_neighbor_init(void); extern int config_write_ospf6_debug_neighbor(struct vty *vty); diff --git a/ospf6d/ospf6_nssa.h b/ospf6d/ospf6_nssa.h index 454bdd7fe2..631503edc6 100644 --- a/ospf6d/ospf6_nssa.h +++ b/ospf6d/ospf6_nssa.h @@ -52,11 +52,11 @@ int ospf6_area_nssa_unset(struct ospf6 *ospf6, struct ospf6_area *area); int ospf6_area_nssa_set(struct ospf6 *ospf6, struct ospf6_area *area); extern void ospf6_nssa_lsa_flush(struct ospf6 *ospf6, struct prefix_ipv6 *p); -extern struct ospf6_lsa *ospf6_translated_nssa_refresh(struct ospf6_area *, - struct ospf6_lsa *, - struct ospf6_lsa *); -extern struct ospf6_lsa *ospf6_translated_nssa_originate(struct ospf6_area *, - struct ospf6_lsa *); +extern struct ospf6_lsa *ospf6_translated_nssa_refresh(struct ospf6_area *oa, + struct ospf6_lsa *type7, + struct ospf6_lsa *type5); +extern struct ospf6_lsa * +ospf6_translated_nssa_originate(struct ospf6_area *oa, struct ospf6_lsa *type7); extern void ospf6_asbr_nssa_redist_task(struct ospf6 *ospf6); diff --git a/ospf6d/ospf6_route.c b/ospf6d/ospf6_route.c index 13003b4151..8bfd3b7124 100644 --- a/ospf6d/ospf6_route.c +++ b/ospf6d/ospf6_route.c @@ -37,6 +37,9 @@ #include "ospf6_interface.h" #include "ospf6d.h" #include "ospf6_zebra.h" +#ifndef VTYSH_EXTRACT_PL +#include "ospf6d/ospf6_route_clippy.c" +#endif DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_ROUTE, "OSPF6 route"); DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_ROUTE_TABLE, "OSPF6 route table"); @@ -1117,11 +1120,6 @@ void ospf6_route_show(struct vty *vty, struct ospf6_route *route, json_object *json_array_next_hops = NULL; json_object *json_next_hop; - if (om6->ospf6 == NULL) { - vty_out(vty, "OSPFv3 is not running\n"); - return; - } - monotime(&now); timersub(&now, &route->changed, &res); timerstring(&res, duration, sizeof(duration)); @@ -1205,11 +1203,6 @@ void ospf6_route_show_detail(struct vty *vty, struct ospf6_route *route, json_object *json_array_next_hops = NULL; json_object *json_next_hop; - if (om6->ospf6 == NULL) { - vty_out(vty, "OSPFv3 is not running\n"); - return; - } - monotime(&now); /* destination */ @@ -1837,49 +1830,27 @@ void ospf6_brouter_show(struct vty *vty, struct ospf6_route *route) OSPF6_PATH_TYPE_NAME(route->path.type), area); } -DEFUN (debug_ospf6_route, - debug_ospf6_route_cmd, - "debug ospf6 route <table|intra-area|inter-area|memory>", - DEBUG_STR - OSPF6_STR - "Debug routes\n" - "Debug route table calculation\n" - "Debug intra-area route calculation\n" - "Debug inter-area route calculation\n" - "Debug route memory use\n" - ) +DEFPY(debug_ospf6_route, + debug_ospf6_route_cmd, + "[no$no] debug ospf6 route <all|table|intra-area|inter-area|memory>", + NO_STR + DEBUG_STR + OSPF6_STR + "Debug routes\n" + "Debug for all types of route calculation\n" + "Debug route table calculation\n" + "Debug intra-area route calculation\n" + "Debug inter-area route calculation\n" + "Debug route memory use\n") { - int idx_type = 3; + int idx_type; unsigned char level = 0; - if (!strcmp(argv[idx_type]->text, "table")) - level = OSPF6_DEBUG_ROUTE_TABLE; - else if (!strcmp(argv[idx_type]->text, "intra-area")) - level = OSPF6_DEBUG_ROUTE_INTRA; - else if (!strcmp(argv[idx_type]->text, "inter-area")) - level = OSPF6_DEBUG_ROUTE_INTER; - else if (!strcmp(argv[idx_type]->text, "memory")) - level = OSPF6_DEBUG_ROUTE_MEMORY; - OSPF6_DEBUG_ROUTE_ON(level); - return CMD_SUCCESS; -} - -DEFUN (no_debug_ospf6_route, - no_debug_ospf6_route_cmd, - "no debug ospf6 route <table|intra-area|inter-area|memory>", - NO_STR - DEBUG_STR - OSPF6_STR - "Debug routes\n" - "Debug route table calculation\n" - "Debug intra-area route calculation\n" - "Debug inter-area route calculation\n" - "Debug route memory use\n") -{ - int idx_type = 4; - unsigned char level = 0; + idx_type = ((no) ? 4 : 3); - if (!strcmp(argv[idx_type]->text, "table")) + if (!strcmp(argv[idx_type]->text, "all")) + level = OSPF6_DEBUG_ROUTE_ALL; + else if (!strcmp(argv[idx_type]->text, "table")) level = OSPF6_DEBUG_ROUTE_TABLE; else if (!strcmp(argv[idx_type]->text, "intra-area")) level = OSPF6_DEBUG_ROUTE_INTRA; @@ -1887,12 +1858,20 @@ DEFUN (no_debug_ospf6_route, level = OSPF6_DEBUG_ROUTE_INTER; else if (!strcmp(argv[idx_type]->text, "memory")) level = OSPF6_DEBUG_ROUTE_MEMORY; - OSPF6_DEBUG_ROUTE_OFF(level); + + if (no) + OSPF6_DEBUG_ROUTE_OFF(level); + else + OSPF6_DEBUG_ROUTE_ON(level); return CMD_SUCCESS; } int config_write_ospf6_debug_route(struct vty *vty) { + if (IS_OSPF6_DEBUG_ROUTE(ALL) == OSPF6_DEBUG_ROUTE_ALL) { + vty_out(vty, "debug ospf6 route all\n"); + return 0; + } if (IS_OSPF6_DEBUG_ROUTE(TABLE)) vty_out(vty, "debug ospf6 route table\n"); if (IS_OSPF6_DEBUG_ROUTE(INTRA)) @@ -1908,7 +1887,5 @@ int config_write_ospf6_debug_route(struct vty *vty) void install_element_ospf6_debug_route(void) { install_element(ENABLE_NODE, &debug_ospf6_route_cmd); - install_element(ENABLE_NODE, &no_debug_ospf6_route_cmd); install_element(CONFIG_NODE, &debug_ospf6_route_cmd); - install_element(CONFIG_NODE, &no_debug_ospf6_route_cmd); } diff --git a/ospf6d/ospf6_route.h b/ospf6d/ospf6_route.h index 991720ec2e..e29439b95e 100644 --- a/ospf6d/ospf6_route.h +++ b/ospf6d/ospf6_route.h @@ -33,7 +33,10 @@ extern unsigned char conf_debug_ospf6_route; #define OSPF6_DEBUG_ROUTE_TABLE 0x01 #define OSPF6_DEBUG_ROUTE_INTRA 0x02 #define OSPF6_DEBUG_ROUTE_INTER 0x04 -#define OSPF6_DEBUG_ROUTE_MEMORY 0x80 +#define OSPF6_DEBUG_ROUTE_MEMORY 0x08 +#define OSPF6_DEBUG_ROUTE_ALL \ + (OSPF6_DEBUG_ROUTE_TABLE | OSPF6_DEBUG_ROUTE_INTRA \ + | OSPF6_DEBUG_ROUTE_INTER | OSPF6_DEBUG_ROUTE_MEMORY) #define OSPF6_DEBUG_ROUTE_ON(level) (conf_debug_ospf6_route |= (level)) #define OSPF6_DEBUG_ROUTE_OFF(level) (conf_debug_ospf6_route &= ~(level)) #define IS_OSPF6_DEBUG_ROUTE(e) (conf_debug_ospf6_route & OSPF6_DEBUG_ROUTE_##e) @@ -343,7 +346,7 @@ extern int ospf6_route_get_first_nh_index(struct ospf6_route *route); ospf6_add_nexthop(route->nh_list, ifindex, addr) extern struct ospf6_route *ospf6_route_create(struct ospf6 *ospf6); -extern void ospf6_route_delete(struct ospf6_route *); +extern void ospf6_route_delete(struct ospf6_route *route); extern struct ospf6_route *ospf6_route_copy(struct ospf6_route *route); extern int ospf6_route_cmp(struct ospf6_route *ra, struct ospf6_route *rb); @@ -384,8 +387,10 @@ extern void ospf6_route_show_detail(struct vty *vty, struct ospf6_route *route, json_object *json, bool use_json); -extern int ospf6_route_table_show(struct vty *, int, int, struct cmd_token **, - struct ospf6_route_table *, bool use_json); +extern int ospf6_route_table_show(struct vty *vty, int argc_start, int argc, + struct cmd_token **argv, + struct ospf6_route_table *table, + bool use_json); extern int ospf6_linkstate_table_show(struct vty *vty, int idx_ipv4, int argc, struct cmd_token **argv, struct ospf6_route_table *table); diff --git a/ospf6d/ospf6_spf.c b/ospf6d/ospf6_spf.c index 1412298802..e4de6ccf91 100644 --- a/ospf6d/ospf6_spf.c +++ b/ospf6d/ospf6_spf.c @@ -44,6 +44,7 @@ #include "ospf6d.h" #include "ospf6_abr.h" #include "ospf6_nssa.h" +#include "ospf6_zebra.h" DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_VERTEX, "OSPF6 vertex"); @@ -438,8 +439,8 @@ void ospf6_spf_table_finish(struct ospf6_route_table *result_table) } } -static const char *const ospf6_spf_reason_str[] = {"R+", "R-", "N+", "N-", "L+", - "L-", "R*", "N*", "C"}; +static const char *const ospf6_spf_reason_str[] = { + "R+", "R-", "N+", "N-", "L+", "L-", "R*", "N*", "C", "A", "GR"}; void ospf6_spf_reason_string(unsigned int reason, char *buf, int size) { @@ -1255,6 +1256,17 @@ static int ospf6_ase_calculate_timer(struct thread *t) ospf6_ase_calculate_route(ospf6, lsa, area); } } + + if (ospf6->gr_info.finishing_restart) { + /* + * The routing table computation is complete. Uninstall remnant + * routes that were installed before the restart, but that are + * no longer valid. + */ + ospf6_zebra_gr_disable(ospf6); + ospf6->gr_info.finishing_restart = false; + } + return 0; } diff --git a/ospf6d/ospf6_spf.h b/ospf6d/ospf6_spf.h index d6fbc5c13b..cc52d16861 100644 --- a/ospf6d/ospf6_spf.h +++ b/ospf6d/ospf6_spf.h @@ -93,6 +93,7 @@ struct ospf6_vertex { #define OSPF6_SPF_FLAGS_NETWORK_LSA_ORIGINATED (1 << 7) #define OSPF6_SPF_FLAGS_CONFIG_CHANGE (1 << 8) #define OSPF6_SPF_FLAGS_ASBR_STATUS_CHANGE (1 << 9) +#define OSPF6_SPF_FLAGS_GR_FINISH (1 << 10) static inline void ospf6_set_spf_reason(struct ospf6 *ospf, unsigned int reason) { diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c index 6ff3789a80..3122d616cb 100644 --- a/ospf6d/ospf6_top.c +++ b/ospf6d/ospf6_top.c @@ -472,6 +472,12 @@ struct ospf6 *ospf6_instance_create(const char *name) if (ospf6->fd < 0) return ospf6; + /* + * Read from non-volatile memory whether this instance is performing a + * graceful restart or not. + */ + ospf6_gr_nvm_read(ospf6); + thread_add_read(master, ospf6_receive, ospf6, ospf6->fd, &ospf6->t_ospf6_receive); @@ -488,7 +494,8 @@ void ospf6_delete(struct ospf6 *o) QOBJ_UNREG(o); ospf6_gr_helper_deinit(o); - ospf6_flush_self_originated_lsas_now(o); + if (!o->gr_info.prepare_in_progress) + ospf6_flush_self_originated_lsas_now(o); ospf6_disable(o); ospf6_del(o); @@ -555,6 +562,7 @@ static void ospf6_disable(struct ospf6 *o) THREAD_OFF(o->t_distribute_update); THREAD_OFF(o->t_ospf6_receive); THREAD_OFF(o->t_external_aggr); + THREAD_OFF(o->gr_info.t_grace_period); } } @@ -1519,7 +1527,6 @@ DEFUN(show_ipv6_ospf6, show_ipv6_ospf6_cmd, bool uj = use_json(argc, argv); json_object *json = NULL; - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) { @@ -1560,7 +1567,6 @@ DEFUN(show_ipv6_ospf6_route, show_ipv6_ospf6_route_cmd, int idx_arg_start = 4; bool uj = use_json(argc, argv); - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) idx_arg_start += 2; @@ -1594,7 +1600,6 @@ DEFUN(show_ipv6_ospf6_route_match, show_ipv6_ospf6_route_match_cmd, int idx_start_arg = 4; bool uj = use_json(argc, argv); - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) idx_start_arg += 2; @@ -1629,7 +1634,6 @@ DEFUN(show_ipv6_ospf6_route_match_detail, int idx_start_arg = 4; bool uj = use_json(argc, argv); - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) idx_start_arg += 2; @@ -1665,7 +1669,6 @@ DEFUN(show_ipv6_ospf6_route_type_detail, show_ipv6_ospf6_route_type_detail_cmd, int idx_start_arg = 4; bool uj = use_json(argc, argv); - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) idx_start_arg += 2; @@ -2078,7 +2081,6 @@ DEFPY (show_ipv6_ospf6_external_aggregator, if (uj) json = json_object_new_object(); - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) { @@ -2236,6 +2238,7 @@ static int config_write_ospf6(struct vty *vty) ospf6_distance_config_write(vty, ospf6); ospf6_distribute_config_write(vty, ospf6); ospf6_asbr_summary_config_write(vty, ospf6); + config_write_ospf6_gr(vty, ospf6); config_write_ospf6_gr_helper(vty, ospf6); vty_out(vty, "exit\n"); diff --git a/ospf6d/ospf6_top.h b/ospf6d/ospf6_top.h index 58ecf08495..3188b1f58f 100644 --- a/ospf6d/ospf6_top.h +++ b/ospf6d/ospf6_top.h @@ -60,6 +60,15 @@ struct ospf6_redist { #define ROUTEMAP(R) (R->route_map.map) }; +struct ospf6_gr_info { + bool restart_support; + bool restart_in_progress; + bool prepare_in_progress; + bool finishing_restart; + uint32_t grace_period; + struct thread *t_grace_period; +}; + struct ospf6_gr_helper { /* Gracefull restart Helper supported configs*/ /* Supported grace interval*/ @@ -192,6 +201,9 @@ struct ospf6 { */ uint16_t max_multipath; + /* OSPF Graceful Restart info (restarting mode) */ + struct ospf6_gr_info gr_info; + /*ospf6 Graceful restart helper info */ struct ospf6_gr_helper ospf6_helper_cfg; diff --git a/ospf6d/ospf6_zebra.c b/ospf6d/ospf6_zebra.c index 5403e643dc..c2e91d09bb 100644 --- a/ospf6d/ospf6_zebra.c +++ b/ospf6d/ospf6_zebra.c @@ -40,6 +40,7 @@ #include "ospf6_zebra.h" #include "ospf6d.h" #include "ospf6_area.h" +#include "ospf6_gr.h" #include "lib/json.h" DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_DISTANCE, "OSPF6 distance"); @@ -173,6 +174,36 @@ static int ospf6_zebra_if_address_update_delete(ZAPI_CALLBACK_ARGS) return 0; } +static int ospf6_zebra_gr_update(struct ospf6 *ospf6, int command, + uint32_t stale_time) +{ + struct zapi_cap api; + + if (!zclient || zclient->sock < 0 || !ospf6) + return 1; + + memset(&api, 0, sizeof(struct zapi_cap)); + api.cap = command; + api.stale_removal_time = stale_time; + api.vrf_id = ospf6->vrf_id; + + (void)zclient_capabilities_send(ZEBRA_CLIENT_CAPABILITIES, zclient, + &api); + + return 0; +} + +int ospf6_zebra_gr_enable(struct ospf6 *ospf6, uint32_t stale_time) +{ + return ospf6_zebra_gr_update(ospf6, ZEBRA_CLIENT_GR_CAPABILITIES, + stale_time); +} + +int ospf6_zebra_gr_disable(struct ospf6 *ospf6) +{ + return ospf6_zebra_gr_update(ospf6, ZEBRA_CLIENT_GR_DISABLE, 0); +} + static int ospf6_zebra_read_route(ZAPI_CALLBACK_ARGS) { struct zapi_route api; @@ -384,12 +415,30 @@ static void ospf6_zebra_route_update(int type, struct ospf6_route *request, void ospf6_zebra_route_update_add(struct ospf6_route *request, struct ospf6 *ospf6) { + if (ospf6->gr_info.restart_in_progress + || ospf6->gr_info.prepare_in_progress) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "Zebra: Graceful Restart in progress -- not installing %pFX", + &request->prefix); + return; + } + ospf6_zebra_route_update(ADD, request, ospf6); } void ospf6_zebra_route_update_remove(struct ospf6_route *request, struct ospf6 *ospf6) { + if (ospf6->gr_info.restart_in_progress + || ospf6->gr_info.prepare_in_progress) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "Zebra: Graceful Restart in progress -- not uninstalling %pFX", + &request->prefix); + return; + } + ospf6_zebra_route_update(REM, request, ospf6); } @@ -398,6 +447,15 @@ void ospf6_zebra_add_discard(struct ospf6_route *request, struct ospf6 *ospf6) struct zapi_route api; struct prefix *dest = &request->prefix; + if (ospf6->gr_info.restart_in_progress + || ospf6->gr_info.prepare_in_progress) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "Zebra: Graceful Restart in progress -- not installing %pFX", + &request->prefix); + return; + } + if (!CHECK_FLAG(request->flag, OSPF6_ROUTE_BLACKHOLE_ADDED)) { memset(&api, 0, sizeof(api)); api.vrf_id = ospf6->vrf_id; @@ -426,6 +484,15 @@ void ospf6_zebra_delete_discard(struct ospf6_route *request, struct zapi_route api; struct prefix *dest = &request->prefix; + if (ospf6->gr_info.restart_in_progress + || ospf6->gr_info.prepare_in_progress) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "Zebra: Graceful Restart in progress -- not uninstalling %pFX", + &request->prefix); + return; + } + if (CHECK_FLAG(request->flag, OSPF6_ROUTE_BLACKHOLE_ADDED)) { memset(&api, 0, sizeof(api)); api.vrf_id = ospf6->vrf_id; diff --git a/ospf6d/ospf6_zebra.h b/ospf6d/ospf6_zebra.h index a3ccc3d38d..77e48673c8 100644 --- a/ospf6d/ospf6_zebra.h +++ b/ospf6d/ospf6_zebra.h @@ -54,20 +54,25 @@ extern void ospf6_zebra_redistribute(int, vrf_id_t vrf_id); extern void ospf6_zebra_no_redistribute(int, vrf_id_t vrf_id); #define ospf6_zebra_is_redistribute(type, vrf_id) \ vrf_bitmap_check(zclient->redist[AFI_IP6][type], vrf_id) -extern void ospf6_zebra_init(struct thread_master *); +extern void ospf6_zebra_init(struct thread_master *tm); extern void ospf6_zebra_add_discard(struct ospf6_route *request, struct ospf6 *ospf6); extern void ospf6_zebra_delete_discard(struct ospf6_route *request, struct ospf6 *ospf6); -extern void ospf6_distance_reset(struct ospf6 *); -extern uint8_t ospf6_distance_apply(struct prefix_ipv6 *, struct ospf6_route *, - struct ospf6 *); +extern void ospf6_distance_reset(struct ospf6 *ospf6); +extern uint8_t ospf6_distance_apply(struct prefix_ipv6 *p, + struct ospf6_route * or, + struct ospf6 *ospf6); -extern int ospf6_distance_set(struct vty *, struct ospf6 *, const char *, - const char *, const char *); -extern int ospf6_distance_unset(struct vty *, struct ospf6 *, const char *, - const char *, const char *); +extern int ospf6_zebra_gr_enable(struct ospf6 *ospf6, uint32_t stale_time); +extern int ospf6_zebra_gr_disable(struct ospf6 *ospf6); +extern int ospf6_distance_set(struct vty *vty, struct ospf6 *ospf6, + const char *distance_str, const char *ip_str, + const char *access_list_str); +extern int ospf6_distance_unset(struct vty *vty, struct ospf6 *ospf6, + const char *distance_str, const char *ip_str, + const char *access_list_str); extern int config_write_ospf6_debug_zebra(struct vty *vty); extern void install_element_ospf6_debug_zebra(void); diff --git a/ospf6d/ospf6d.c b/ospf6d/ospf6d.c index 0e8185cfeb..5e6dcde991 100644 --- a/ospf6d/ospf6d.c +++ b/ospf6d/ospf6d.c @@ -413,7 +413,6 @@ DEFUN(show_ipv6_ospf6_database, show_ipv6_ospf6_database_cmd, int idx_vrf = 0; bool uj = use_json(argc, argv); - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) idx_level += 2; @@ -460,7 +459,6 @@ DEFUN(show_ipv6_ospf6_database_type, show_ipv6_ospf6_database_type_cmd, bool all_vrf = false; int idx_vrf = 0; - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) { idx_lsa += 2; @@ -505,7 +503,6 @@ DEFUN(show_ipv6_ospf6_database_id, show_ipv6_ospf6_database_id_cmd, bool all_vrf = false; int idx_vrf = 0; - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (argv[idx_ipv4]->type == IPV4_TKN) inet_pton(AF_INET, argv[idx_ipv4]->arg, &id); @@ -548,7 +545,6 @@ DEFUN(show_ipv6_ospf6_database_router, show_ipv6_ospf6_database_router_cmd, int idx_vrf = 0; bool uj = use_json(argc, argv); - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) { idx_ipv4 += 2; @@ -619,7 +615,6 @@ DEFUN_HIDDEN( bool all_vrf = false; int idx_vrf = 0; - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) idx_ipv4 += 2; @@ -672,7 +667,6 @@ DEFUN(show_ipv6_ospf6_database_type_id, show_ipv6_ospf6_database_type_id_cmd, bool all_vrf = false; int idx_vrf = 0; - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) { idx_lsa += 2; @@ -731,7 +725,6 @@ DEFUN(show_ipv6_ospf6_database_type_router, bool all_vrf = false; int idx_vrf = 0; - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) { idx_lsa += 2; @@ -782,7 +775,6 @@ DEFUN(show_ipv6_ospf6_database_id_router, bool all_vrf = false; int idx_vrf = 0; - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) { idx_ls_id += 2; @@ -833,7 +825,6 @@ DEFUN(show_ipv6_ospf6_database_adv_router_linkstate_id, bool all_vrf = false; int idx_vrf = 0; - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) { idx_adv_rtr += 2; @@ -891,7 +882,6 @@ DEFUN(show_ipv6_ospf6_database_type_id_router, bool all_vrf = false; int idx_vrf = 0; - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) { idx_lsa += 2; @@ -960,7 +950,6 @@ DEFUN (show_ipv6_ospf6_database_type_adv_router_linkstate_id, bool all_vrf = false; int idx_vrf = 0; - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) { idx_lsa += 2; @@ -1008,7 +997,6 @@ DEFUN(show_ipv6_ospf6_database_self_originated, uint32_t adv_router = 0; bool uj = use_json(argc, argv); - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) idx_level += 2; @@ -1063,7 +1051,6 @@ DEFUN(show_ipv6_ospf6_database_type_self_originated, bool all_vrf = false; int idx_vrf = 0; - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) { idx_lsa += 2; @@ -1123,7 +1110,6 @@ DEFUN(show_ipv6_ospf6_database_type_self_originated_linkstate_id, bool all_vrf = false; int idx_vrf = 0; - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) { idx_lsa += 2; @@ -1185,7 +1171,6 @@ DEFUN(show_ipv6_ospf6_database_type_id_self_originated, bool all_vrf = false; int idx_vrf = 0; - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) { idx_lsa += 2; @@ -1268,7 +1253,6 @@ DEFUN(show_ipv6_ospf6_border_routers, show_ipv6_ospf6_border_routers_cmd, int idx_vrf = 0; int idx_argc = 5; - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) { idx_argc += 2; @@ -1308,7 +1292,6 @@ DEFUN(show_ipv6_ospf6_linkstate, show_ipv6_ospf6_linkstate_cmd, bool all_vrf = false; int idx_vrf = 0; - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) idx_ipv4 += 2; @@ -1348,8 +1331,6 @@ DEFUN(show_ipv6_ospf6_linkstate_detail, show_ipv6_ospf6_linkstate_detail_cmd, bool all_vrf = false; int idx_vrf = 0; - - OSPF6_CMD_CHECK_RUNNING(); OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (idx_vrf > 0) idx_detail += 2; @@ -1374,20 +1355,6 @@ DEFUN(show_ipv6_ospf6_linkstate_detail, show_ipv6_ospf6_linkstate_detail_cmd, return CMD_SUCCESS; } -static void ospf6_plist_add(struct prefix_list *plist) -{ - if (prefix_list_afi(plist) != AFI_IP6) - return; - ospf6_area_plist_update(plist, 1); -} - -static void ospf6_plist_del(struct prefix_list *plist) -{ - if (prefix_list_afi(plist) != AFI_IP6) - return; - ospf6_area_plist_update(plist, 0); -} - /* Install ospf related commands. */ void ospf6_init(struct thread_master *master) { @@ -1402,11 +1369,12 @@ void ospf6_init(struct thread_master *master) ospf6_intra_init(); ospf6_asbr_init(); ospf6_abr_init(); + ospf6_gr_init(); ospf6_gr_helper_config_init(); /* initialize hooks for modifying filter rules */ - prefix_list_add_hook(ospf6_plist_add); - prefix_list_delete_hook(ospf6_plist_del); + prefix_list_add_hook(ospf6_plist_update); + prefix_list_delete_hook(ospf6_plist_update); access_list_add_hook(ospf6_filter_update); access_list_delete_hook(ospf6_filter_update); diff --git a/ospf6d/ospf6d.h b/ospf6d/ospf6d.h index d5170be7cc..041a9b1df9 100644 --- a/ospf6d/ospf6d.h +++ b/ospf6d/ospf6d.h @@ -93,12 +93,6 @@ extern struct thread_master *master; #define OSPF6_ROUTER_ID_STR "Specify Router-ID\n" #define OSPF6_LS_ID_STR "Specify Link State ID\n" -#define OSPF6_CMD_CHECK_RUNNING() \ - if (om6->ospf6 == NULL) { \ - vty_out(vty, "OSPFv3 is not running\n"); \ - return CMD_SUCCESS; \ - } - #define IS_OSPF6_ASBR(O) ((O)->flag & OSPF6_FLAG_ASBR) #define OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf) \ if (argv_find(argv, argc, "vrf", &idx_vrf)) { \ diff --git a/ospf6d/subdir.am b/ospf6d/subdir.am index ac99e90b26..be626646a0 100644 --- a/ospf6d/subdir.am +++ b/ospf6d/subdir.am @@ -12,6 +12,7 @@ vtysh_scan += \ ospf6d/ospf6_area.c \ ospf6d/ospf6_bfd.c \ ospf6d/ospf6_flood.c \ + ospf6d/ospf6_gr.c \ ospf6d/ospf6_gr_helper.c \ ospf6d/ospf6_interface.c \ ospf6d/ospf6_intra.c \ @@ -40,6 +41,7 @@ ospf6d_libospf6_a_SOURCES = \ ospf6d/ospf6_routemap_nb_config.c \ ospf6d/ospf6_bfd.c \ ospf6d/ospf6_flood.c \ + ospf6d/ospf6_gr.c \ ospf6d/ospf6_gr_helper.c \ ospf6d/ospf6_interface.c \ ospf6d/ospf6_intra.c \ @@ -92,9 +94,12 @@ ospf6d_ospf6d_snmp_la_LIBADD = lib/libfrrsnmp.la clippy_scan += \ ospf6d/ospf6_top.c \ + ospf6d/ospf6_area.c \ ospf6d/ospf6_asbr.c \ ospf6d/ospf6_lsa.c \ ospf6d/ospf6_gr_helper.c \ + ospf6d/ospf6_gr.c \ + ospf6d/ospf6_route.c \ # end nodist_ospf6d_ospf6d_SOURCES = \ diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c index d209ae053c..cc1b2919c0 100644 --- a/ospfd/ospf_lsa.c +++ b/ospfd/ospf_lsa.c @@ -2954,6 +2954,32 @@ static int ospf_maxage_lsa_remover(struct thread *thread) return 0; } +/* This function checks whether an LSA with initial sequence number should be + * originated after a wrap in sequence number + */ +void ospf_check_and_gen_init_seq_lsa(struct ospf_interface *oi, + struct ospf_lsa *recv_lsa) +{ + struct ospf_lsa *lsa = NULL; + struct ospf *ospf = oi->ospf; + + lsa = ospf_lsa_lookup_by_header(oi->area, recv_lsa->data); + + if ((lsa == NULL) || (!CHECK_FLAG(lsa->flags, OSPF_LSA_PREMATURE_AGE)) + || (lsa->retransmit_counter != 0)) { + if (IS_DEBUG_OSPF(lsa, LSA)) + zlog_debug( + "Do not generate LSA with initial seqence number."); + return; + } + + ospf_lsa_maxage_delete(ospf, lsa); + + lsa->data->ls_seqnum = lsa_seqnum_increment(lsa); + + ospf_lsa_refresh(ospf, lsa); +} + void ospf_lsa_maxage_delete(struct ospf *ospf, struct ospf_lsa *lsa) { struct route_node *rn; diff --git a/ospfd/ospf_lsa.h b/ospfd/ospf_lsa.h index d01dc720ba..5dcd072774 100644 --- a/ospfd/ospf_lsa.h +++ b/ospfd/ospf_lsa.h @@ -218,6 +218,8 @@ struct as_external_lsa { #define LS_AGE(x) (OSPF_LSA_MAXAGE < get_age(x) ? OSPF_LSA_MAXAGE : get_age(x)) #define IS_LSA_SELF(L) (CHECK_FLAG ((L)->flags, OSPF_LSA_SELF)) #define IS_LSA_MAXAGE(L) (LS_AGE ((L)) == OSPF_LSA_MAXAGE) +#define IS_LSA_MAX_SEQ(L) \ + ((L)->data->ls_seqnum == htonl(OSPF_MAX_SEQUENCE_NUMBER)) #define OSPF_LSA_UPDATE_DELAY 2 @@ -347,6 +349,8 @@ extern struct ospf_lsa *ospf_translated_nssa_refresh(struct ospf *ospf, extern struct ospf_lsa *ospf_translated_nssa_originate(struct ospf *ospf, struct ospf_lsa *type7, struct ospf_lsa *type5); +extern void ospf_check_and_gen_init_seq_lsa(struct ospf_interface *oi, + struct ospf_lsa *lsa); extern void ospf_flush_lsa_from_area(struct ospf *ospf, struct in_addr area_id, int type); #endif /* _ZEBRA_OSPF_LSA_H */ diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c index 9930b0bd49..1efdfee3b4 100644 --- a/ospfd/ospf_packet.c +++ b/ospfd/ospf_packet.c @@ -1081,6 +1081,25 @@ static void ospf_hello(struct ip *iph, struct ospf_header *ospfh, return; } + if (OSPF_GR_IS_ACTIVE_HELPER(nbr)) { + /* As per the GR Conformance Test Case 7.2. Section 3 + * "Also, if X was the Designated Router on network segment S + * when the helping relationship began, Y maintains X as the + * Designated Router until the helping relationship is + * terminated." + * When I am helper for this neighbor, I should not trigger the + * ISM Events. Also Intentionally not setting the priority and + * other fields so that when the neighbor exits the Grace + * period, it can handle if there is any change before GR and + * after GR. */ + if (IS_DEBUG_OSPF_GR) + zlog_debug( + "%s, Neighbor is under GR Restart, hence ignoring the ISM Events", + __PRETTY_FUNCTION__); + + return; + } + /* If neighbor itself declares DR and no BDR exists, cause event BackupSeen */ if (IPV4_ADDR_SAME(&nbr->address.u.prefix4, &hello->d_router)) @@ -2089,11 +2108,10 @@ static void ospf_ls_upd(struct ospf *ospf, struct ip *iph, if (current == NULL || (ret = ospf_lsa_more_recent(current, lsa)) < 0) { /* CVE-2017-3224 */ - if (current && (lsa->data->ls_seqnum == - htonl(OSPF_MAX_SEQUENCE_NUMBER) - && !IS_LSA_MAXAGE(lsa))) { + if (current && (IS_LSA_MAX_SEQ(current)) + && (IS_LSA_MAX_SEQ(lsa)) && !IS_LSA_MAXAGE(lsa)) { zlog_debug( - "Link State Update[%s]: has Max Seq but not MaxAge. Dropping it", + "Link State Update[%s]: has Max Seq and higher checksum but not MaxAge. Dropping it", dump_lsa_key(lsa)); DISCARD_LSA(lsa, 4); @@ -2271,8 +2289,10 @@ static void ospf_ls_ack(struct ip *iph, struct ospf_header *ospfh, lsr = ospf_ls_retransmit_lookup(nbr, lsa); - if (lsr != NULL && ospf_lsa_more_recent(lsr, lsa) == 0) + if (lsr != NULL && ospf_lsa_more_recent(lsr, lsa) == 0) { ospf_ls_retransmit_delete(nbr, lsr); + ospf_check_and_gen_init_seq_lsa(oi, lsa); + } lsa->data = NULL; ospf_lsa_discard(lsa); diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index 1d4aa65355..3ae9707f5f 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -10607,11 +10607,9 @@ static void show_ip_ospf_route_network(struct vty *vty, struct ospf *ospf, prefix2str(&rn->p, buf1, sizeof(buf1)); - json_route = json_object_new_object(); if (json) { + json_route = json_object_new_object(); json_object_object_add(json, buf1, json_route); - json_object_to_json_string_ext( - json, JSON_C_TO_STRING_NOSLASHESCAPE); } switch (or->path_type) { @@ -10733,8 +10731,6 @@ static void show_ip_ospf_route_network(struct vty *vty, struct ospf *ospf, } } } - if (!json) - json_object_free(json_route); } if (!json) vty_out(vty, "\n"); @@ -10762,8 +10758,8 @@ static void show_ip_ospf_route_router(struct vty *vty, struct ospf *ospf, continue; int flag = 0; - json_route = json_object_new_object(); if (json) { + json_route = json_object_new_object(); json_object_object_add( json, inet_ntop(AF_INET, &rn->p.u.prefix4, buf, sizeof(buf)), @@ -10878,8 +10874,6 @@ static void show_ip_ospf_route_router(struct vty *vty, struct ospf *ospf, } } } - if (!json) - json_object_free(json_route); } if (!json) vty_out(vty, "\n"); @@ -10908,11 +10902,9 @@ static void show_ip_ospf_route_external(struct vty *vty, struct ospf *ospf, char buf1[19]; snprintfrr(buf1, sizeof(buf1), "%pFX", &rn->p); - json_route = json_object_new_object(); if (json) { + json_route = json_object_new_object(); json_object_object_add(json, buf1, json_route); - json_object_to_json_string_ext( - json, JSON_C_TO_STRING_NOSLASHESCAPE); } switch (er->path_type) { @@ -11010,8 +11002,6 @@ static void show_ip_ospf_route_external(struct vty *vty, struct ospf *ospf, } } } - if (!json) - json_object_free(json_route); } if (!json) vty_out(vty, "\n"); @@ -11224,7 +11214,9 @@ DEFUN (show_ip_ospf_route, if (uj) { /* Keep Non-pretty format */ vty_out(vty, "%s\n", - json_object_to_json_string(json)); + json_object_to_json_string_ext( + json, + JSON_C_TO_STRING_NOSLASHESCAPE)); json_object_free(json); } else if (!ospf_output) vty_out(vty, "%% OSPF instance not found\n"); @@ -11236,7 +11228,9 @@ DEFUN (show_ip_ospf_route, if (uj) { vty_out(vty, "%s\n", json_object_to_json_string_ext( - json, JSON_C_TO_STRING_PRETTY)); + json, + JSON_C_TO_STRING_PRETTY + | JSON_C_TO_STRING_NOSLASHESCAPE)); json_object_free(json); } else vty_out(vty, "%% OSPF instance not found\n"); @@ -11250,7 +11244,9 @@ DEFUN (show_ip_ospf_route, if (uj) { vty_out(vty, "%s\n", json_object_to_json_string_ext( - json, JSON_C_TO_STRING_PRETTY)); + json, + JSON_C_TO_STRING_PRETTY + | JSON_C_TO_STRING_NOSLASHESCAPE)); json_object_free(json); } else vty_out(vty, "%% OSPF instance not found\n"); @@ -11263,7 +11259,9 @@ DEFUN (show_ip_ospf_route, ret = show_ip_ospf_route_common(vty, ospf, json, use_vrf); /* Keep Non-pretty format */ if (uj) - vty_out(vty, "%s\n", json_object_to_json_string(json)); + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + json, JSON_C_TO_STRING_NOSLASHESCAPE)); } if (uj) diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c index 9a421de017..766be60778 100644 --- a/ospfd/ospfd.c +++ b/ospfd/ospfd.c @@ -222,6 +222,9 @@ void ospf_process_refresh_data(struct ospf *ospf, bool reset) ospf_lsdb_delete_all(ospf->lsdb); } + /* Since the LSAs are deleted, need reset the aggr flag */ + ospf_unset_all_aggr_flag(ospf); + /* Delete the LSDB */ for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area)) ospf_area_lsdb_discard_delete(area); diff --git a/pathd/path_cli.c b/pathd/path_cli.c index bd629a2b70..46242fd05a 100644 --- a/pathd/path_cli.c +++ b/pathd/path_cli.c @@ -352,7 +352,16 @@ static int segment_list_has_src_dst( nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, "ipv6_adjacency"); node_src_id = adj_src_ipv6_str; + } else { + /* + * This is just to make the compiler happy about + * node_src_id not being initialized. This + * should never happen unless we change the cli + * function. + */ + assert(!"We must have a adj_src_ipv4_str or a adj_src_ipv6_str"); } + /* addresses */ snprintf(xpath, XPATH_MAXLEN, "./segment[index='%s']/nai/local-address", index_str); diff --git a/pimd/pim_assert.h b/pimd/pim_assert.h index 63fda3fe34..c07cbeb013 100644 --- a/pimd/pim_assert.h +++ b/pimd/pim_assert.h @@ -24,8 +24,22 @@ #include "if.h" -#include "pim_neighbor.h" -#include "pim_ifchannel.h" +struct pim_ifchannel; +struct pim_neighbor; + +enum pim_ifassert_state { + PIM_IFASSERT_NOINFO, + PIM_IFASSERT_I_AM_WINNER, + PIM_IFASSERT_I_AM_LOSER +}; + +struct pim_assert_metric { + uint32_t rpt_bit_flag; + uint32_t metric_preference; + uint32_t route_metric; + struct in_addr ip_address; /* neighbor router that sourced the Assert + message */ +}; /* RFC 4601: 4.11. Timer Values diff --git a/pimd/pim_bfd.c b/pimd/pim_bfd.c index dfe2d5f2fa..c7fcbba71e 100644 --- a/pimd/pim_bfd.c +++ b/pimd/pim_bfd.c @@ -28,6 +28,7 @@ #include "zclient.h" #include "pim_instance.h" +#include "pim_neighbor.h" #include "pim_cmd.h" #include "pim_vty.h" #include "pim_iface.h" diff --git a/pimd/pim_bsm.c b/pimd/pim_bsm.c index f2845ee6e1..a3a3426f39 100644 --- a/pimd/pim_bsm.c +++ b/pimd/pim_bsm.c @@ -28,6 +28,7 @@ #include "pimd.h" #include "pim_iface.h" #include "pim_instance.h" +#include "pim_neighbor.h" #include "pim_rpf.h" #include "pim_hello.h" #include "pim_pim.h" diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index 1238e03a5b..14aa710524 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -3430,112 +3430,87 @@ static void igmp_show_groups(struct pim_instance *pim, struct vty *vty, bool uj) pim->igmp_watermark_limit ? "Set" : "Not Set", pim->igmp_watermark_limit); vty_out(vty, - "Interface Address Group Mode Timer Srcs V Uptime \n"); + "Interface Group Mode Timer Srcs V Uptime \n"); } /* scan interfaces */ FOR_ALL_INTERFACES (pim->vrf, ifp) { struct pim_interface *pim_ifp = ifp->info; - struct listnode *sock_node; - struct igmp_sock *igmp; + struct listnode *grpnode; + struct igmp_group *grp; if (!pim_ifp) continue; - /* scan igmp sockets */ - for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, - igmp)) { - char ifaddr_str[INET_ADDRSTRLEN]; - struct listnode *grpnode; - struct igmp_group *grp; - - pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, - sizeof(ifaddr_str)); - - /* scan igmp groups */ - for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, - grpnode, grp)) { - char group_str[INET_ADDRSTRLEN]; - char hhmmss[10]; - char uptime[10]; + /* scan igmp groups */ + for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_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); + 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 (uj) { + json_object_object_get_ex(json, ifp->name, + &json_iface); - json_group = json_object_new_object(); - json_object_string_add(json_group, - "source", - ifaddr_str); - 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 + 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); + } + + 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 %-15s %4s %8s %4d %d %8s\n", - ifp->name, ifaddr_str, - group_str, - grp->igmp_version == 3 + 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") + ? "EXCL" + : "INCL") : "----", - hhmmss, - grp->group_source_list - ? listcount( - grp->group_source_list) - : 0, - grp->igmp_version, uptime); - } - } /* scan igmp groups */ - } /* scan igmp sockets */ - } /* scan interfaces */ + hhmmss, + grp->group_source_list ? listcount( + grp->group_source_list) + : 0, + grp->igmp_version, uptime); + } + } /* scan igmp groups */ + } /* scan interfaces */ if (uj) { vty_out(vty, "%s\n", json_object_to_json_string_ext( @@ -3550,63 +3525,49 @@ static void igmp_show_group_retransmission(struct pim_instance *pim, struct interface *ifp; vty_out(vty, - "Interface Address Group RetTimer Counter RetSrcs\n"); + "Interface Group RetTimer Counter RetSrcs\n"); /* scan interfaces */ FOR_ALL_INTERFACES (pim->vrf, ifp) { struct pim_interface *pim_ifp = ifp->info; - struct listnode *sock_node; - struct igmp_sock *igmp; + struct listnode *grpnode; + struct igmp_group *grp; if (!pim_ifp) continue; - /* scan igmp sockets */ - for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, - igmp)) { - char ifaddr_str[INET_ADDRSTRLEN]; - struct listnode *grpnode; - struct igmp_group *grp; - - pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, - sizeof(ifaddr_str)); - - /* scan igmp groups */ - for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, - grpnode, grp)) { - char group_str[INET_ADDRSTRLEN]; - char grp_retr_mmss[10]; - struct listnode *src_node; - struct igmp_source *src; - int grp_retr_sources = 0; - - pim_inet4_dump("<group?>", grp->group_addr, - group_str, sizeof(group_str)); - pim_time_timer_to_mmss( - grp_retr_mmss, sizeof(grp_retr_mmss), - grp->t_group_query_retransmit_timer); - - - /* count group sources with retransmission state - */ - for (ALL_LIST_ELEMENTS_RO( - grp->group_source_list, src_node, - src)) { - if (src->source_query_retransmit_count - > 0) { - ++grp_retr_sources; - } + /* scan igmp groups */ + for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_group_list, grpnode, + grp)) { + char group_str[INET_ADDRSTRLEN]; + char grp_retr_mmss[10]; + struct listnode *src_node; + struct igmp_source *src; + int grp_retr_sources = 0; + + pim_inet4_dump("<group?>", grp->group_addr, group_str, + sizeof(group_str)); + pim_time_timer_to_mmss( + grp_retr_mmss, sizeof(grp_retr_mmss), + grp->t_group_query_retransmit_timer); + + + /* count group sources with retransmission state + */ + for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, + src_node, src)) { + if (src->source_query_retransmit_count > 0) { + ++grp_retr_sources; } + } - vty_out(vty, "%-16s %-15s %-15s %-8s %7d %7d\n", - ifp->name, ifaddr_str, group_str, - grp_retr_mmss, - grp->group_specific_query_retransmit_count, - grp_retr_sources); + vty_out(vty, "%-16s %-15s %-8s %7d %7d\n", ifp->name, + group_str, grp_retr_mmss, + grp->group_specific_query_retransmit_count, + grp_retr_sources); - } /* scan igmp groups */ - } /* scan igmp sockets */ - } /* scan interfaces */ + } /* scan igmp groups */ + } /* scan interfaces */ } static void igmp_show_sources(struct pim_instance *pim, struct vty *vty) @@ -3617,71 +3578,54 @@ static void igmp_show_sources(struct pim_instance *pim, struct vty *vty) now = pim_time_monotonic_sec(); vty_out(vty, - "Interface Address Group Source Timer Fwd Uptime \n"); + "Interface Group Source Timer Fwd Uptime \n"); /* scan interfaces */ FOR_ALL_INTERFACES (pim->vrf, ifp) { struct pim_interface *pim_ifp = ifp->info; - struct listnode *sock_node; - struct igmp_sock *igmp; + struct listnode *grpnode; + struct igmp_group *grp; if (!pim_ifp) continue; - /* scan igmp sockets */ - for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, - igmp)) { - char ifaddr_str[INET_ADDRSTRLEN]; - struct listnode *grpnode; - struct igmp_group *grp; + /* scan igmp groups */ + for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_group_list, grpnode, + grp)) { + char group_str[INET_ADDRSTRLEN]; + struct listnode *srcnode; + struct igmp_source *src; + + pim_inet4_dump("<group?>", grp->group_addr, group_str, + sizeof(group_str)); - pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, - sizeof(ifaddr_str)); + /* 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]; - /* scan igmp groups */ - for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, - grpnode, grp)) { - char group_str[INET_ADDRSTRLEN]; - struct listnode *srcnode; - struct igmp_source *src; + pim_inet4_dump("<source?>", src->source_addr, + source_str, sizeof(source_str)); - pim_inet4_dump("<group?>", grp->group_addr, - group_str, sizeof(group_str)); + pim_time_timer_to_mmss(mmss, sizeof(mmss), + src->t_source_timer); - /* 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), + pim_time_uptime(uptime, sizeof(uptime), now - src->source_creation); - vty_out(vty, - "%-16s %-15s %-15s %-15s %5s %3s %8s\n", - ifp->name, ifaddr_str, - group_str, source_str, mmss, - IGMP_SOURCE_TEST_FORWARDING( - src->source_flags) + 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); + uptime); - } /* scan group sources */ - } /* scan igmp groups */ - } /* scan igmp sockets */ - } /* scan interfaces */ + } /* scan group sources */ + } /* scan igmp groups */ + } /* scan interfaces */ } static void igmp_show_source_retransmission(struct pim_instance *pim, @@ -3690,57 +3634,42 @@ static void igmp_show_source_retransmission(struct pim_instance *pim, struct interface *ifp; vty_out(vty, - "Interface Address Group Source Counter\n"); + "Interface Group Source Counter\n"); /* scan interfaces */ FOR_ALL_INTERFACES (pim->vrf, ifp) { struct pim_interface *pim_ifp = ifp->info; - struct listnode *sock_node; - struct igmp_sock *igmp; + struct listnode *grpnode; + struct igmp_group *grp; if (!pim_ifp) continue; - /* scan igmp sockets */ - for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, - igmp)) { - char ifaddr_str[INET_ADDRSTRLEN]; - struct listnode *grpnode; - struct igmp_group *grp; - - pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, - sizeof(ifaddr_str)); + /* scan igmp groups */ + for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_group_list, grpnode, + grp)) { + char group_str[INET_ADDRSTRLEN]; + struct listnode *srcnode; + struct igmp_source *src; - /* scan igmp groups */ - for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, - grpnode, grp)) { - char group_str[INET_ADDRSTRLEN]; - struct listnode *srcnode; - struct igmp_source *src; + pim_inet4_dump("<group?>", grp->group_addr, group_str, + sizeof(group_str)); - 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)) { + char source_str[INET_ADDRSTRLEN]; - /* scan group sources */ - 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)); - pim_inet4_dump( - "<source?>", src->source_addr, - source_str, sizeof(source_str)); + vty_out(vty, "%-16s %-15s %-15s %7d\n", + ifp->name, group_str, source_str, + src->source_query_retransmit_count); - vty_out(vty, - "%-16s %-15s %-15s %-15s %7d\n", - ifp->name, ifaddr_str, - group_str, source_str, - src->source_query_retransmit_count); - - } /* scan group sources */ - } /* scan igmp groups */ - } /* scan igmp sockets */ - } /* scan interfaces */ + } /* scan group sources */ + } /* scan igmp groups */ + } /* scan interfaces */ } static void pim_show_bsr(struct pim_instance *pim, @@ -3993,8 +3922,7 @@ static void clear_mroute(struct pim_instance *pim) /* scan interfaces */ FOR_ALL_INTERFACES (pim->vrf, ifp) { struct pim_interface *pim_ifp = ifp->info; - struct listnode *sock_node; - struct igmp_sock *igmp; + struct igmp_group *grp; struct pim_ifchannel *ch; if (!pim_ifp) @@ -4008,20 +3936,12 @@ static void clear_mroute(struct pim_instance *pim) } /* clean up all igmp groups */ - /* scan igmp sockets */ - for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, - igmp)) { - - struct igmp_group *grp; - if (igmp->igmp_group_list) { - while (igmp->igmp_group_list->count) { - grp = listnode_head( - igmp->igmp_group_list); - igmp_group_delete(grp); - } + if (pim_ifp->igmp_group_list) { + while (pim_ifp->igmp_group_list->count) { + grp = listnode_head(pim_ifp->igmp_group_list); + igmp_group_delete(grp); } - } } diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c index 0b28a3e84c..eb19cf4ddf 100644 --- a/pimd/pim_iface.c +++ b/pimd/pim_iface.c @@ -156,14 +156,12 @@ struct pim_interface *pim_if_new(struct interface *ifp, bool igmp, bool pim, PIM_IF_DO_IGMP_LISTEN_ALLROUTERS(pim_ifp->options); pim_ifp->igmp_join_list = NULL; - pim_ifp->igmp_socket_list = NULL; pim_ifp->pim_neighbor_list = NULL; pim_ifp->upstream_switch_list = NULL; pim_ifp->pim_generation_id = 0; /* list of struct igmp_sock */ - pim_ifp->igmp_socket_list = list_new(); - pim_ifp->igmp_socket_list->del = (void (*)(void *))igmp_sock_free; + pim_igmp_if_init(pim_ifp, ifp); /* list of struct pim_neighbor */ pim_ifp->pim_neighbor_list = list_new(); @@ -214,7 +212,8 @@ void pim_if_delete(struct interface *ifp) pim_if_del_vif(ifp); pim_ifp->pim->mcast_if_count--; - list_delete(&pim_ifp->igmp_socket_list); + pim_igmp_if_fini(pim_ifp); + list_delete(&pim_ifp->pim_neighbor_list); list_delete(&pim_ifp->upstream_switch_list); list_delete(&pim_ifp->sec_addr_list); diff --git a/pimd/pim_iface.h b/pimd/pim_iface.h index 92784103fe..55c278d6e2 100644 --- a/pimd/pim_iface.h +++ b/pimd/pim_iface.h @@ -30,6 +30,7 @@ #include "pim_igmp.h" #include "pim_upstream.h" +#include "pim_instance.h" #include "bfd.h" #define PIM_IF_MASK_PIM (1 << 0) @@ -102,6 +103,8 @@ struct pim_interface { int igmp_last_member_query_count; /* IGMP last member query count */ struct list *igmp_socket_list; /* list of struct igmp_sock */ struct list *igmp_join_list; /* list of struct igmp_join */ + struct list *igmp_group_list; /* list of struct igmp_group */ + struct hash *igmp_group_hash; int pim_sock_fd; /* PIM socket file descriptor */ struct thread *t_pim_sock_read; /* thread for reading PIM socket */ diff --git a/pimd/pim_ifchannel.h b/pimd/pim_ifchannel.h index 7ec8191e56..52f02a660b 100644 --- a/pimd/pim_ifchannel.h +++ b/pimd/pim_ifchannel.h @@ -25,6 +25,8 @@ #include "if.h" #include "prefix.h" +#include "pim_assert.h" + struct pim_ifchannel; #include "pim_upstream.h" @@ -39,20 +41,6 @@ enum pim_ifjoin_state { PIM_IFJOIN_PRUNE_PENDING_TMP, }; -enum pim_ifassert_state { - PIM_IFASSERT_NOINFO, - PIM_IFASSERT_I_AM_WINNER, - PIM_IFASSERT_I_AM_LOSER -}; - -struct pim_assert_metric { - uint32_t rpt_bit_flag; - uint32_t metric_preference; - uint32_t route_metric; - struct in_addr ip_address; /* neighbor router that sourced the Assert - message */ -}; - /* Flag to detect change in CouldAssert(S,G,I) */ diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c index 71b2d9187a..50de7124d2 100644 --- a/pimd/pim_igmp.c +++ b/pimd/pim_igmp.c @@ -810,13 +810,8 @@ static void igmp_group_free(struct igmp_group *group) XFREE(MTYPE_PIM_IGMP_GROUP, group); } -static void igmp_group_count_incr(struct igmp_sock *igmp) +static void igmp_group_count_incr(struct pim_interface *pim_ifp) { - struct pim_interface *pim_ifp = igmp->interface->info; - - if (!pim_ifp) - return; - ++pim_ifp->pim->igmp_group_count; if (pim_ifp->pim->igmp_group_count == pim_ifp->pim->igmp_watermark_limit) { @@ -827,13 +822,8 @@ static void igmp_group_count_incr(struct igmp_sock *igmp) } } -static void igmp_group_count_decr(struct igmp_sock *igmp) +static void igmp_group_count_decr(struct pim_interface *pim_ifp) { - struct pim_interface *pim_ifp = igmp->interface->info; - - if (!pim_ifp) - return; - if (pim_ifp->pim->igmp_group_count == 0) { zlog_warn("Cannot decrement igmp group count below 0(vrf: %s)", VRF_LOGNAME(pim_ifp->pim->vrf)); @@ -848,14 +838,14 @@ void igmp_group_delete(struct igmp_group *group) struct listnode *src_node; struct listnode *src_nextnode; struct igmp_source *src; + struct pim_interface *pim_ifp = group->interface->info; if (PIM_DEBUG_IGMP_TRACE) { char group_str[INET_ADDRSTRLEN]; pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str)); - zlog_debug("Deleting IGMP group %s from socket %d interface %s", - group_str, group->group_igmp_sock->fd, - group->group_igmp_sock->interface->name); + zlog_debug("Deleting IGMP group %s from interface %s", + group_str, group->interface->name); } for (ALL_LIST_ELEMENTS(group->group_source_list, src_node, src_nextnode, @@ -866,9 +856,9 @@ void igmp_group_delete(struct igmp_group *group) THREAD_OFF(group->t_group_query_retransmit_timer); group_timer_off(group); - igmp_group_count_decr(group->group_igmp_sock); - listnode_delete(group->group_igmp_sock->igmp_group_list, group); - hash_release(group->group_igmp_sock->igmp_group_hash, group); + igmp_group_count_decr(pim_ifp); + listnode_delete(pim_ifp->igmp_group_list, group); + hash_release(pim_ifp->igmp_group_hash, group); igmp_group_free(group); } @@ -886,11 +876,6 @@ void igmp_sock_free(struct igmp_sock *igmp) assert(!igmp->t_igmp_read); assert(!igmp->t_igmp_query_timer); assert(!igmp->t_other_querier_timer); - assert(igmp->igmp_group_list); - assert(!listcount(igmp->igmp_group_list)); - - list_delete(&igmp->igmp_group_list); - hash_free(igmp->igmp_group_hash); XFREE(MTYPE_PIM_IGMP_SOCKET, igmp); } @@ -898,14 +883,6 @@ void igmp_sock_free(struct igmp_sock *igmp) void igmp_sock_delete(struct igmp_sock *igmp) { struct pim_interface *pim_ifp; - struct listnode *grp_node; - struct listnode *grp_nextnode; - struct igmp_group *grp; - - for (ALL_LIST_ELEMENTS(igmp->igmp_group_list, grp_node, grp_nextnode, - grp)) { - igmp_group_delete(grp); - } sock_close(igmp); @@ -914,6 +891,9 @@ void igmp_sock_delete(struct igmp_sock *igmp) listnode_delete(pim_ifp->igmp_socket_list, igmp); igmp_sock_free(igmp); + + if (!listcount(pim_ifp->igmp_socket_list)) + pim_igmp_if_reset(pim_ifp); } void igmp_sock_delete_all(struct interface *ifp) @@ -948,12 +928,50 @@ static bool igmp_group_hash_equal(const void *arg1, const void *arg2) return false; } +void pim_igmp_if_init(struct pim_interface *pim_ifp, struct interface *ifp) +{ + char hash_name[64]; + + pim_ifp->igmp_socket_list = list_new(); + pim_ifp->igmp_socket_list->del = (void (*)(void *))igmp_sock_free; + + pim_ifp->igmp_group_list = list_new(); + pim_ifp->igmp_group_list->del = (void (*)(void *))igmp_group_free; + + snprintf(hash_name, sizeof(hash_name), "IGMP %s hash", ifp->name); + pim_ifp->igmp_group_hash = hash_create( + igmp_group_hash_key, igmp_group_hash_equal, hash_name); +} + +void pim_igmp_if_reset(struct pim_interface *pim_ifp) +{ + struct listnode *grp_node, *grp_nextnode; + struct igmp_group *grp; + + for (ALL_LIST_ELEMENTS(pim_ifp->igmp_group_list, grp_node, grp_nextnode, + grp)) { + igmp_group_delete(grp); + } +} + +void pim_igmp_if_fini(struct pim_interface *pim_ifp) +{ + pim_igmp_if_reset(pim_ifp); + + assert(pim_ifp->igmp_group_list); + assert(!listcount(pim_ifp->igmp_group_list)); + + list_delete(&pim_ifp->igmp_group_list); + hash_free(pim_ifp->igmp_group_hash); + + list_delete(&pim_ifp->igmp_socket_list); +} + static struct igmp_sock *igmp_sock_new(int fd, struct in_addr ifaddr, struct interface *ifp, int mtrace_only) { struct pim_interface *pim_ifp; struct igmp_sock *igmp; - char hash_name[64]; pim_ifp = ifp->info; @@ -965,13 +983,6 @@ static struct igmp_sock *igmp_sock_new(int fd, struct in_addr ifaddr, igmp = XCALLOC(MTYPE_PIM_IGMP_SOCKET, sizeof(*igmp)); - igmp->igmp_group_list = list_new(); - igmp->igmp_group_list->del = (void (*)(void *))igmp_group_free; - - snprintf(hash_name, sizeof(hash_name), "IGMP %s hash", ifp->name); - igmp->igmp_group_hash = hash_create(igmp_group_hash_key, - igmp_group_hash_equal, hash_name); - igmp->fd = fd; igmp->interface = ifp; igmp->ifaddr = ifaddr; @@ -1114,7 +1125,7 @@ static int igmp_group_timer(struct thread *t) pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str)); zlog_debug("%s: Timer for group %s on interface %s", __func__, - group_str, group->group_igmp_sock->interface->name); + group_str, group->interface->name); } assert(group->group_filtermode_isexcl); @@ -1151,7 +1162,7 @@ static void group_timer_off(struct igmp_group *group) pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str)); zlog_debug("Cancelling TIMER event for group %s on %s", - group_str, group->group_igmp_sock->interface->name); + group_str, group->interface->name); } THREAD_OFF(group->t_group_timer); } @@ -1188,16 +1199,18 @@ struct igmp_group *find_group_by_addr(struct igmp_sock *igmp, struct in_addr group_addr) { struct igmp_group lookup; + struct pim_interface *pim_ifp = igmp->interface->info; lookup.group_addr.s_addr = group_addr.s_addr; - return hash_lookup(igmp->igmp_group_hash, &lookup); + return hash_lookup(pim_ifp->igmp_group_hash, &lookup); } struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp, struct in_addr group_addr) { struct igmp_group *group; + struct pim_interface *pim_ifp = igmp->interface->info; group = find_group_by_addr(igmp, group_addr); if (group) { @@ -1239,7 +1252,7 @@ struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp, group->t_group_query_retransmit_timer = NULL; group->group_specific_query_retransmit_count = 0; group->group_addr = group_addr; - group->group_igmp_sock = igmp; + group->interface = igmp->interface; group->last_igmp_v1_report_dsec = -1; group->last_igmp_v2_report_dsec = -1; group->group_creation = pim_time_monotonic_sec(); @@ -1248,8 +1261,8 @@ struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp, /* initialize new group as INCLUDE {empty} */ group->group_filtermode_isexcl = 0; /* 0=INCLUDE, 1=EXCLUDE */ - listnode_add(igmp->igmp_group_list, group); - group = hash_get(igmp->igmp_group_hash, group, hash_alloc_intern); + listnode_add(pim_ifp->igmp_group_list, group); + group = hash_get(pim_ifp->igmp_group_hash, group, hash_alloc_intern); if (PIM_DEBUG_IGMP_TRACE) { char group_str[INET_ADDRSTRLEN]; @@ -1260,7 +1273,7 @@ struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp, group_str, igmp->fd, igmp->interface->name); } - igmp_group_count_incr(igmp); + igmp_group_count_incr(pim_ifp); /* RFC 3376: 6.2.2. Definition of Group Timers diff --git a/pimd/pim_igmp.h b/pimd/pim_igmp.h index abb8af836b..dfe986e8f5 100644 --- a/pimd/pim_igmp.h +++ b/pimd/pim_igmp.h @@ -99,12 +99,15 @@ struct igmp_sock { bool mtrace_only; - struct list *igmp_group_list; /* list of struct igmp_group */ - struct hash *igmp_group_hash; - struct igmp_stats rx_stats; }; +struct pim_interface; + +void pim_igmp_if_init(struct pim_interface *pim_ifp, struct interface *ifp); +void pim_igmp_if_reset(struct pim_interface *pim_ifp); +void pim_igmp_if_fini(struct pim_interface *pim_ifp); + struct igmp_sock *pim_igmp_sock_lookup_ifaddr(struct list *igmp_sock_list, struct in_addr ifaddr); struct igmp_sock *igmp_sock_lookup_by_fd(struct list *igmp_sock_list, int fd); @@ -178,7 +181,7 @@ struct igmp_group { int group_filtermode_isexcl; /* 0=INCLUDE, 1=EXCLUDE */ struct list *group_source_list; /* list of struct igmp_source */ time_t group_creation; - struct igmp_sock *group_igmp_sock; /* back pointer */ + struct interface *interface; int64_t last_igmp_v1_report_dsec; int64_t last_igmp_v2_report_dsec; }; @@ -188,6 +191,10 @@ struct igmp_group *find_group_by_addr(struct igmp_sock *igmp, struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp, struct in_addr group_addr); +struct igmp_source *igmp_get_source_by_addr(struct igmp_group *group, + struct in_addr src_addr, + bool *created); + void igmp_group_delete_empty_include(struct igmp_group *group); void igmp_startup_mode_on(struct igmp_sock *igmp); @@ -195,9 +202,6 @@ void igmp_startup_mode_on(struct igmp_sock *igmp); void igmp_group_timer_on(struct igmp_group *group, long interval_msec, const char *ifname); -struct igmp_source *source_new(struct igmp_group *group, - struct in_addr src_addr); - void igmp_send_query(int igmp_version, struct igmp_group *group, int fd, const char *ifname, char *query_buf, int query_buf_size, int num_sources, struct in_addr dst_addr, diff --git a/pimd/pim_igmpv3.c b/pimd/pim_igmpv3.c index bc67a1dd1d..13db11fa80 100644 --- a/pimd/pim_igmpv3.c +++ b/pimd/pim_igmpv3.c @@ -57,16 +57,28 @@ static void on_trace(const char *label, struct interface *ifp, } } +static inline long igmp_gmi_msec(struct igmp_group *group) +{ + struct pim_interface *pim_ifp = group->interface->info; + struct igmp_sock *igmp; + struct listnode *sock_node; + + long qrv = 0, qqi = 0; + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) { + qrv = MAX(qrv, igmp->querier_robustness_variable); + qqi = MAX(qqi, igmp->querier_query_interval); + } + return PIM_IGMP_GMI_MSEC(qrv, qqi, + pim_ifp->igmp_query_max_response_time_dsec); +} + void igmp_group_reset_gmi(struct igmp_group *group) { long group_membership_interval_msec; - struct pim_interface *pim_ifp; - struct igmp_sock *igmp; struct interface *ifp; - igmp = group->group_igmp_sock; - ifp = igmp->interface; - pim_ifp = ifp->info; + ifp = group->interface; /* RFC 3376: 8.4. Group Membership Interval @@ -82,9 +94,7 @@ void igmp_group_reset_gmi(struct igmp_group *group) (1000 * querier_query_interval) + 100 * query_response_interval_dsec; */ - group_membership_interval_msec = PIM_IGMP_GMI_MSEC( - igmp->querier_robustness_variable, igmp->querier_query_interval, - pim_ifp->igmp_query_max_response_time_dsec); + group_membership_interval_msec = igmp_gmi_msec(group); if (PIM_DEBUG_IGMP_TRACE) { char group_str[INET_ADDRSTRLEN]; @@ -127,7 +137,7 @@ static int igmp_source_timer(struct thread *t) zlog_debug( "%s: Source timer expired for group %s source %s on %s", __func__, group_str, source_str, - group->group_igmp_sock->interface->name); + group->interface->name); } /* @@ -188,8 +198,7 @@ static void source_timer_off(struct igmp_group *group, sizeof(source_str)); zlog_debug( "Cancelling TIMER event for group %s source %s on %s", - group_str, source_str, - group->group_igmp_sock->interface->name); + group_str, source_str, group->interface->name); } THREAD_OFF(source->t_source_timer); @@ -199,7 +208,7 @@ static void igmp_source_timer_on(struct igmp_group *group, struct igmp_source *source, long interval_msec) { source_timer_off(group, source); - struct pim_interface *pim_ifp = group->group_igmp_sock->interface->info; + struct pim_interface *pim_ifp = group->interface->info; if (PIM_DEBUG_IGMP_EVENTS) { char group_str[INET_ADDRSTRLEN]; @@ -211,7 +220,7 @@ static void igmp_source_timer_on(struct igmp_group *group, zlog_debug( "Scheduling %ld.%03ld sec TIMER event for group %s source %s on %s", interval_msec / 1000, interval_msec % 1000, group_str, - source_str, group->group_igmp_sock->interface->name); + source_str, group->interface->name); } thread_add_timer_msec(router->master, igmp_source_timer, source, @@ -225,19 +234,14 @@ static void igmp_source_timer_on(struct igmp_group *group, igmp_source_forward_start(pim_ifp->pim, source); } -void igmp_source_reset_gmi(struct igmp_sock *igmp, struct igmp_group *group, - struct igmp_source *source) +void igmp_source_reset_gmi(struct igmp_group *group, struct igmp_source *source) { long group_membership_interval_msec; - struct pim_interface *pim_ifp; struct interface *ifp; - ifp = igmp->interface; - pim_ifp = ifp->info; + ifp = group->interface; - group_membership_interval_msec = PIM_IGMP_GMI_MSEC( - igmp->querier_robustness_variable, igmp->querier_query_interval, - pim_ifp->igmp_query_max_response_time_dsec); + group_membership_interval_msec = igmp_gmi_msec(group); if (PIM_DEBUG_IGMP_TRACE) { char group_str[INET_ADDRSTRLEN]; @@ -312,7 +316,7 @@ static void source_clear_send_flag(struct list *source_list) */ static void group_exclude_fwd_anysrc_ifempty(struct igmp_group *group) { - struct pim_interface *pim_ifp = group->group_igmp_sock->interface->info; + struct pim_interface *pim_ifp = group->interface->info; assert(group->group_filtermode_isexcl); @@ -356,9 +360,8 @@ void igmp_source_delete(struct igmp_source *source) pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str)); zlog_debug( - "Deleting IGMP source %s for group %s from socket %d interface %s c_oil ref_count %d", - source_str, group_str, group->group_igmp_sock->fd, - group->group_igmp_sock->interface->name, + "Deleting IGMP source %s for group %s from interface %s c_oil ref_count %d", + source_str, group_str, group->interface->name, source->source_channel_oil ? source->source_channel_oil->oil_ref_count : 0); @@ -376,10 +379,9 @@ void igmp_source_delete(struct igmp_source *source) pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str)); zlog_warn( - "%s: forwarding=ON(!) IGMP source %s for group %s from socket %d interface %s", + "%s: forwarding=ON(!) IGMP source %s for group %s from interface %s", __func__, source_str, group_str, - group->group_igmp_sock->fd, - group->group_igmp_sock->interface->name); + group->interface->name); /* warning only */ } @@ -439,11 +441,18 @@ struct igmp_source *igmp_find_source_by_addr(struct igmp_group *group, return 0; } -struct igmp_source *source_new(struct igmp_group *group, - struct in_addr src_addr) +struct igmp_source *igmp_get_source_by_addr(struct igmp_group *group, + struct in_addr src_addr, bool *new) { struct igmp_source *src; + if (new) + *new = false; + + src = igmp_find_source_by_addr(group, src_addr); + if (src) + return src; + if (PIM_DEBUG_IGMP_TRACE) { char group_str[INET_ADDRSTRLEN]; char source_str[INET_ADDRSTRLEN]; @@ -452,9 +461,8 @@ struct igmp_source *source_new(struct igmp_group *group, pim_inet4_dump("<source?>", src_addr, source_str, sizeof(source_str)); zlog_debug( - "Creating new IGMP source %s for group %s on socket %d interface %s", - source_str, group_str, group->group_igmp_sock->fd, - group->group_igmp_sock->interface->name); + "Creating new IGMP source %s for group %s on interface %s", + source_str, group_str, group->interface->name); } src = XCALLOC(MTYPE_PIM_IGMP_GROUP_SOURCE, sizeof(*src)); @@ -471,23 +479,6 @@ struct igmp_source *source_new(struct igmp_group *group, /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */ igmp_anysource_forward_stop(group); - - return src; -} - -static struct igmp_source *add_source_by_addr(struct igmp_sock *igmp, - struct igmp_group *group, - struct in_addr src_addr) -{ - struct igmp_source *src; - - src = igmp_find_source_by_addr(group, src_addr); - if (src) { - return src; - } - - src = source_new(group, src_addr); - return src; } @@ -518,8 +509,7 @@ static void allow(struct igmp_sock *igmp, struct in_addr from, source = igmp_find_source_by_addr(group, star); if (source) - igmp_source_reset_gmi(igmp, group, - source); + igmp_source_reset_gmi(group, source); } } else { igmp_group_delete(group); @@ -540,10 +530,9 @@ static void allow(struct igmp_sock *igmp, struct in_addr from, src_addr = sources + i; - source = add_source_by_addr(igmp, group, *src_addr); - if (!source) { + source = igmp_get_source_by_addr(group, *src_addr, NULL); + if (!source) continue; - } /* RFC 3376: 6.4.1. Reception of Current-State Records @@ -555,7 +544,7 @@ static void allow(struct igmp_sock *igmp, struct in_addr from, igmp_source_reset_gmi() below, resetting the source timers to GMI, accomplishes this. */ - igmp_source_reset_gmi(igmp, group, source); + igmp_source_reset_gmi(group, source); } /* scan received sources */ } @@ -585,21 +574,23 @@ static void isex_excl(struct igmp_group *group, int num_sources, /* scan received sources (A) */ for (i = 0; i < num_sources; ++i) { struct in_addr *src_addr; + bool new; src_addr = sources + i; /* E.2: lookup reported source from (A) in (X,Y) */ - source = igmp_find_source_by_addr(group, *src_addr); - if (source) { + source = igmp_get_source_by_addr(group, *src_addr, &new); + if (!source) + continue; + + if (!new) { /* E.3: if found, clear deletion flag: (X*A) or (Y*A) */ IGMP_SOURCE_DONT_DELETE(source->source_flags); } else { /* E.4: if not found, create source with timer=GMI: * (A-X-Y) */ - source = source_new(group, *src_addr); assert(!source->t_source_timer); /* timer == 0 */ - igmp_source_reset_gmi(group->group_igmp_sock, group, - source); + igmp_source_reset_gmi(group, source); assert(source->t_source_timer); /* (A-X-Y) timer > 0 */ } @@ -615,8 +606,7 @@ static void isex_excl(struct igmp_group *group, int num_sources, source = igmp_find_source_by_addr(group, star); if (source) { IGMP_SOURCE_DONT_DELETE(source->source_flags); - igmp_source_reset_gmi(group->group_igmp_sock, group, - source); + igmp_source_reset_gmi(group, source); } } @@ -639,18 +629,21 @@ static void isex_incl(struct igmp_group *group, int num_sources, for (i = 0; i < num_sources; ++i) { struct igmp_source *source; struct in_addr *src_addr; + bool new; src_addr = sources + i; /* I.2: lookup reported source (B) */ - source = igmp_find_source_by_addr(group, *src_addr); - if (source) { + source = igmp_get_source_by_addr(group, *src_addr, &new); + if (!source) + continue; + + if (!new) { /* I.3: if found, clear deletion flag (A*B) */ IGMP_SOURCE_DONT_DELETE(source->source_flags); } else { /* I.4: if not found, create source with timer=0 (B-A) */ - source = source_new(group, *src_addr); assert(!source->t_source_timer); /* (B-A) timer=0 */ } @@ -706,7 +699,6 @@ void igmpv3_report_isex(struct igmp_sock *igmp, struct in_addr from, static void toin_incl(struct igmp_group *group, int num_sources, struct in_addr *sources) { - struct igmp_sock *igmp = group->group_igmp_sock; int num_sources_tosend = listcount(group->group_source_list); int i; @@ -717,22 +709,23 @@ static void toin_incl(struct igmp_group *group, int num_sources, for (i = 0; i < num_sources; ++i) { struct igmp_source *source; struct in_addr *src_addr; + bool new; src_addr = sources + i; /* Lookup reported source (B) */ - source = igmp_find_source_by_addr(group, *src_addr); - if (source) { + source = igmp_get_source_by_addr(group, *src_addr, &new); + if (!source) + continue; + + if (!new) { /* If found, clear SEND flag (A*B) */ IGMP_SOURCE_DONT_SEND(source->source_flags); --num_sources_tosend; - } else { - /* If not found, create new source */ - source = source_new(group, *src_addr); } /* (B)=GMI */ - igmp_source_reset_gmi(igmp, group, source); + igmp_source_reset_gmi(group, source); } /* Send sources marked with SEND flag: Q(G,A-B) */ @@ -744,7 +737,6 @@ static void toin_incl(struct igmp_group *group, int num_sources, static void toin_excl(struct igmp_group *group, int num_sources, struct in_addr *sources) { - struct igmp_sock *igmp = group->group_igmp_sock; int num_sources_tosend; int i; @@ -755,25 +747,24 @@ static void toin_excl(struct igmp_group *group, int num_sources, for (i = 0; i < num_sources; ++i) { struct igmp_source *source; struct in_addr *src_addr; + bool new; src_addr = sources + i; /* Lookup reported source (A) */ - source = igmp_find_source_by_addr(group, *src_addr); - if (source) { - if (source->t_source_timer) { - /* If found and timer running, clear SEND flag - * (X*A) */ - IGMP_SOURCE_DONT_SEND(source->source_flags); - --num_sources_tosend; - } - } else { - /* If not found, create new source */ - source = source_new(group, *src_addr); + source = igmp_get_source_by_addr(group, *src_addr, &new); + if (!source) + continue; + + if (source->t_source_timer) { + /* If found and timer running, clear SEND flag + * (X*A) */ + IGMP_SOURCE_DONT_SEND(source->source_flags); + --num_sources_tosend; } /* (A)=GMI */ - igmp_source_reset_gmi(igmp, group, source); + igmp_source_reset_gmi(group, source); } /* Send sources marked with SEND flag: Q(G,X-A) */ @@ -839,22 +830,18 @@ static void toex_incl(struct igmp_group *group, int num_sources, for (i = 0; i < num_sources; ++i) { struct igmp_source *source; struct in_addr *src_addr; + bool new; src_addr = sources + i; /* Lookup reported source (B) */ - source = igmp_find_source_by_addr(group, *src_addr); - if (source) { + source = igmp_get_source_by_addr(group, *src_addr, &new); + if (!new) { /* If found, clear deletion flag: (A*B) */ IGMP_SOURCE_DONT_DELETE(source->source_flags); /* and set SEND flag (A*B) */ IGMP_SOURCE_DO_SEND(source->source_flags); ++num_sources_tosend; - } else { - /* If source not found, create source with timer=0: - * (B-A)=0 */ - source = source_new(group, *src_addr); - assert(!source->t_source_timer); /* (B-A) timer=0 */ } } /* Scan received sources (B) */ @@ -899,12 +886,16 @@ static void toex_excl(struct igmp_group *group, int num_sources, for (i = 0; i < num_sources; ++i) { struct igmp_source *source; struct in_addr *src_addr; + bool new; src_addr = sources + i; /* lookup reported source (A) in known sources (X,Y) */ - source = igmp_find_source_by_addr(group, *src_addr); - if (source) { + source = igmp_get_source_by_addr(group, *src_addr, &new); + if (!source) + continue; + + if (!new) { /* if found, clear off DELETE flag from reported source * (A) */ IGMP_SOURCE_DONT_DELETE(source->source_flags); @@ -912,7 +903,6 @@ static void toex_excl(struct igmp_group *group, int num_sources, /* if not found, create source with Group Timer: * (A-X-Y)=Group Timer */ long group_timer_msec; - source = source_new(group, *src_addr); assert(!source->t_source_timer); /* timer == 0 */ group_timer_msec = igmp_group_timer_remain_msec(group); @@ -986,6 +976,26 @@ void igmpv3_report_allow(struct igmp_sock *igmp, struct in_addr from, allow(igmp, from, group_addr, num_sources, sources); } +static void igmp_send_query_group(struct igmp_group *group, char *query_buf, + size_t query_buf_size, int num_sources, + int s_flag) +{ + struct interface *ifp = group->interface; + struct pim_interface *pim_ifp = ifp->info; + struct igmp_sock *igmp; + struct listnode *sock_node; + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) { + igmp_send_query( + pim_ifp->igmp_version, group, igmp->fd, ifp->name, + query_buf, query_buf_size, num_sources, + group->group_addr, group->group_addr, + pim_ifp->igmp_specific_query_max_response_time_dsec, + s_flag, igmp->querier_robustness_variable, + igmp->querier_query_interval); + } +} + /* RFC3376: 6.6.3.1. Building and Sending Group Specific Queries @@ -995,7 +1005,6 @@ void igmpv3_report_allow(struct igmp_sock *igmp, struct in_addr from, */ static void group_retransmit_group(struct igmp_group *group) { - struct igmp_sock *igmp; struct pim_interface *pim_ifp; long lmqc; /* Last Member Query Count */ long lmqi_msec; /* Last Member Query Interval */ @@ -1003,8 +1012,7 @@ static void group_retransmit_group(struct igmp_group *group) int s_flag; int query_buf_size; - igmp = group->group_igmp_sock; - pim_ifp = igmp->interface->info; + pim_ifp = group->interface->info; if (pim_ifp->igmp_version == 3) { query_buf_size = PIM_IGMP_BUFSIZE_WRITE; @@ -1033,7 +1041,7 @@ static void group_retransmit_group(struct igmp_group *group) sizeof(group_str)); zlog_debug( "retransmit_group_specific_query: group %s on %s: s_flag=%d count=%d", - group_str, igmp->interface->name, s_flag, + group_str, group->interface->name, s_flag, group->group_specific_query_retransmit_count); } @@ -1045,14 +1053,7 @@ static void group_retransmit_group(struct igmp_group *group) interest. */ - igmp_send_query(pim_ifp->igmp_version, group, igmp->fd, - igmp->interface->name, query_buf, sizeof(query_buf), - 0 /* num_sources_tosend */, - group->group_addr /* dst_addr */, - group->group_addr /* group_addr */, - pim_ifp->igmp_specific_query_max_response_time_dsec, - s_flag, igmp->querier_robustness_variable, - igmp->querier_query_interval); + igmp_send_query_group(group, query_buf, sizeof(query_buf), 0, s_flag); } /* @@ -1070,7 +1071,6 @@ static void group_retransmit_group(struct igmp_group *group) static int group_retransmit_sources(struct igmp_group *group, int send_with_sflag_set) { - struct igmp_sock *igmp; struct pim_interface *pim_ifp; long lmqc; /* Last Member Query Count */ long lmqi_msec; /* Last Member Query Interval */ @@ -1090,8 +1090,7 @@ static int group_retransmit_sources(struct igmp_group *group, source_addr1 = (struct in_addr *)(query_buf1 + IGMP_V3_SOURCES_OFFSET); source_addr2 = (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET); - igmp = group->group_igmp_sock; - pim_ifp = igmp->interface->info; + pim_ifp = group->interface->info; lmqc = pim_ifp->igmp_last_member_query_count; lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec; @@ -1131,7 +1130,7 @@ static int group_retransmit_sources(struct igmp_group *group, sizeof(group_str)); zlog_debug( "retransmit_grp&src_specific_query: group %s on %s: srcs_with_sflag=%d srcs_wo_sflag=%d will_send_sflag=%d retransmit_src_left=%d", - group_str, igmp->interface->name, num_sources_tosend1, + group_str, group->interface->name, num_sources_tosend1, num_sources_tosend2, send_with_sflag_set, num_retransmit_sources_left); } @@ -1154,7 +1153,7 @@ static int group_retransmit_sources(struct igmp_group *group, zlog_warn( "%s: group %s on %s: s_flag=1 unable to fit %d sources into buf_size=%zu (max_sources=%d)", __func__, group_str, - igmp->interface->name, + group->interface->name, num_sources_tosend1, sizeof(query_buf1), query_buf1_max_sources); } else { @@ -1169,15 +1168,9 @@ static int group_retransmit_sources(struct igmp_group *group, interest. */ - igmp_send_query( - pim_ifp->igmp_version, group, igmp->fd, - igmp->interface->name, query_buf1, - sizeof(query_buf1), num_sources_tosend1, - group->group_addr, group->group_addr, - pim_ifp->igmp_specific_query_max_response_time_dsec, - 1 /* s_flag */, - igmp->querier_robustness_variable, - igmp->querier_query_interval); + igmp_send_query_group( + group, query_buf1, sizeof(query_buf1), + num_sources_tosend1, 1 /* s_flag */); } } /* send_with_sflag_set */ @@ -1197,7 +1190,7 @@ static int group_retransmit_sources(struct igmp_group *group, sizeof(group_str)); zlog_warn( "%s: group %s on %s: s_flag=0 unable to fit %d sources into buf_size=%zu (max_sources=%d)", - __func__, group_str, igmp->interface->name, + __func__, group_str, group->interface->name, num_sources_tosend2, sizeof(query_buf2), query_buf2_max_sources); } else { @@ -1211,15 +1204,9 @@ static int group_retransmit_sources(struct igmp_group *group, interest. */ - igmp_send_query( - pim_ifp->igmp_version, group, igmp->fd, - igmp->interface->name, query_buf2, - sizeof(query_buf2), num_sources_tosend2, - group->group_addr, group->group_addr, - pim_ifp->igmp_specific_query_max_response_time_dsec, - 0 /* s_flag */, - igmp->querier_robustness_variable, - igmp->querier_query_interval); + igmp_send_query_group( + group, query_buf2, sizeof(query_buf2), + num_sources_tosend2, 0 /* s_flag */); } } @@ -1239,7 +1226,7 @@ static int igmp_group_retransmit(struct thread *t) pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str)); zlog_debug("group_retransmit_timer: group %s on %s", group_str, - group->group_igmp_sock->interface->name); + group->interface->name); } /* Retransmit group-specific queries? (RFC3376: 6.6.3.1) */ @@ -1287,7 +1274,6 @@ static int igmp_group_retransmit(struct thread *t) */ static void group_retransmit_timer_on(struct igmp_group *group) { - struct igmp_sock *igmp; struct pim_interface *pim_ifp; long lmqi_msec; /* Last Member Query Interval */ @@ -1296,8 +1282,7 @@ static void group_retransmit_timer_on(struct igmp_group *group) return; } - igmp = group->group_igmp_sock; - pim_ifp = igmp->interface->info; + pim_ifp = group->interface->info; lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec; @@ -1308,7 +1293,7 @@ static void group_retransmit_timer_on(struct igmp_group *group) zlog_debug( "Scheduling %ld.%03ld sec retransmit timer for group %s on %s", lmqi_msec / 1000, lmqi_msec % 1000, group_str, - igmp->interface->name); + group->interface->name); } thread_add_timer_msec(router->master, igmp_group_retransmit, group, @@ -1332,11 +1317,9 @@ static long igmp_source_timer_remain_msec(struct igmp_source *source) static void group_query_send(struct igmp_group *group) { struct pim_interface *pim_ifp; - struct igmp_sock *igmp; long lmqc; /* Last Member Query Count */ - igmp = group->group_igmp_sock; - pim_ifp = igmp->interface->info; + pim_ifp = group->interface->info; lmqc = pim_ifp->igmp_last_member_query_count; /* lower group timer to lmqt */ @@ -1359,7 +1342,6 @@ static void group_query_send(struct igmp_group *group) static void source_query_send_by_flag(struct igmp_group *group, int num_sources_tosend) { - struct igmp_sock *igmp; struct pim_interface *pim_ifp; struct listnode *src_node; struct igmp_source *src; @@ -1369,8 +1351,7 @@ static void source_query_send_by_flag(struct igmp_group *group, assert(num_sources_tosend > 0); - igmp = group->group_igmp_sock; - pim_ifp = igmp->interface->info; + pim_ifp = group->interface->info; lmqc = pim_ifp->igmp_last_member_query_count; lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec; @@ -1417,16 +1398,19 @@ static void block_excl(struct igmp_group *group, int num_sources, for (i = 0; i < num_sources; ++i) { struct igmp_source *source; struct in_addr *src_addr; + bool new; src_addr = sources + i; /* lookup reported source (A) in known sources (X,Y) */ - source = igmp_find_source_by_addr(group, *src_addr); - if (!source) { + source = igmp_get_source_by_addr(group, *src_addr, &new); + if (!source) + continue; + + if (new) { /* 3: if not found, create source with Group Timer: * (A-X-Y)=Group Timer */ long group_timer_msec; - source = source_new(group, *src_addr); assert(!source->t_source_timer); /* timer == 0 */ group_timer_msec = igmp_group_timer_remain_msec(group); @@ -1504,7 +1488,6 @@ void igmpv3_report_block(struct igmp_sock *igmp, struct in_addr from, void igmp_group_timer_lower_to_lmqt(struct igmp_group *group) { - struct igmp_sock *igmp; struct interface *ifp; struct pim_interface *pim_ifp; char *ifname; @@ -1523,8 +1506,7 @@ void igmp_group_timer_lower_to_lmqt(struct igmp_group *group) return; } - igmp = group->group_igmp_sock; - ifp = igmp->interface; + ifp = group->interface; pim_ifp = ifp->info; ifname = ifp->name; @@ -1551,7 +1533,6 @@ void igmp_group_timer_lower_to_lmqt(struct igmp_group *group) void igmp_source_timer_lower_to_lmqt(struct igmp_source *source) { struct igmp_group *group; - struct igmp_sock *igmp; struct interface *ifp; struct pim_interface *pim_ifp; char *ifname; @@ -1560,8 +1541,7 @@ void igmp_source_timer_lower_to_lmqt(struct igmp_source *source) int lmqt_msec; /* Last Member Query Time */ group = source->source_group; - igmp = group->group_igmp_sock; - ifp = igmp->interface; + ifp = group->interface; pim_ifp = ifp->info; ifname = ifp->name; diff --git a/pimd/pim_igmpv3.h b/pimd/pim_igmpv3.h index 6abaef6e26..273f944b3c 100644 --- a/pimd/pim_igmpv3.h +++ b/pimd/pim_igmpv3.h @@ -23,6 +23,8 @@ #include <zebra.h> #include "if.h" +#include "pim_igmp.h" + #define IGMP_V3_CHECKSUM_OFFSET (2) #define IGMP_V3_REPORT_NUMGROUPS_OFFSET (6) #define IGMP_V3_REPORT_GROUPPRECORD_OFFSET (8) @@ -52,7 +54,7 @@ #define PIM_IGMP_OHPI_DSEC(qrv,qqi,qri_dsec) ((qrv) * (10 * (qqi)) + (qri_dsec)) void igmp_group_reset_gmi(struct igmp_group *group); -void igmp_source_reset_gmi(struct igmp_sock *igmp, struct igmp_group *group, +void igmp_source_reset_gmi(struct igmp_group *group, struct igmp_source *source); void igmp_source_free(struct igmp_source *source); diff --git a/pimd/pim_instance.h b/pimd/pim_instance.h index 52ded08ae3..68c5b9167b 100644 --- a/pimd/pim_instance.h +++ b/pimd/pim_instance.h @@ -210,6 +210,8 @@ struct pim_instance { void pim_vrf_init(void); void pim_vrf_terminate(void); +extern struct pim_router *router; + struct pim_instance *pim_get_pim_instance(vrf_id_t vrf_id); #endif diff --git a/pimd/pim_mroute.h b/pimd/pim_mroute.h index 2d8e1b01fb..4cd6b9f0ac 100644 --- a/pimd/pim_mroute.h +++ b/pimd/pim_mroute.h @@ -167,6 +167,8 @@ struct igmpmsg { Above: from <linux/mroute.h> */ +struct channel_oil; + int pim_mroute_socket_enable(struct pim_instance *pim); int pim_mroute_socket_disable(struct pim_instance *pim); diff --git a/pimd/pim_nb_config.c b/pimd/pim_nb_config.c index 4b4c1ec7db..f4627cbcc2 100644 --- a/pimd/pim_nb_config.c +++ b/pimd/pim_nb_config.c @@ -23,6 +23,7 @@ #include "pim_nb.h" #include "lib/northbound_cli.h" #include "pim_igmpv3.h" +#include "pim_neighbor.h" #include "pim_pim.h" #include "pim_mlag.h" #include "pim_bfd.h" @@ -60,8 +61,9 @@ static void pim_if_membership_clear(struct interface *ifp) static void pim_if_membership_refresh(struct interface *ifp) { struct pim_interface *pim_ifp; - struct listnode *sock_node; - struct igmp_sock *igmp; + struct listnode *grpnode; + struct igmp_group *grp; + pim_ifp = ifp->info; assert(pim_ifp); @@ -83,36 +85,27 @@ static void pim_if_membership_refresh(struct interface *ifp) * the interface */ - /* scan igmp sockets */ - for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) { - struct listnode *grpnode; - struct igmp_group *grp; - - /* scan igmp groups */ - for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, grpnode, - grp)) { - struct listnode *srcnode; - struct igmp_source *src; - - /* scan group sources */ - for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, - srcnode, src)) { - - if (IGMP_SOURCE_TEST_FORWARDING( - src->source_flags)) { - struct prefix_sg sg; - - memset(&sg, 0, - sizeof(struct prefix_sg)); - sg.src = src->source_addr; - sg.grp = grp->group_addr; - pim_ifchannel_local_membership_add( - ifp, &sg, false /*is_vxlan*/); - } - - } /* scan group sources */ - } /* scan igmp groups */ - } /* scan igmp sockets */ + /* scan igmp groups */ + for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_group_list, grpnode, grp)) { + struct listnode *srcnode; + struct igmp_source *src; + + /* scan group sources */ + for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, srcnode, + src)) { + + if (IGMP_SOURCE_TEST_FORWARDING(src->source_flags)) { + struct prefix_sg sg; + + memset(&sg, 0, sizeof(struct prefix_sg)); + sg.src = src->source_addr; + sg.grp = grp->group_addr; + pim_ifchannel_local_membership_add( + ifp, &sg, false /*is_vxlan*/); + } + + } /* scan group sources */ + } /* scan igmp groups */ /* * Finally delete every PIM (S,G) entry lacking all state info @@ -458,6 +451,8 @@ static void change_query_max_response_time(struct pim_interface *pim_ifp, { struct listnode *sock_node; struct igmp_sock *igmp; + struct listnode *grp_node; + struct igmp_group *grp; if (pim_ifp->igmp_query_max_response_time_dsec == query_max_response_time_dsec) @@ -474,32 +469,28 @@ static void change_query_max_response_time(struct pim_interface *pim_ifp, /* scan all sockets */ for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) { - struct listnode *grp_node; - struct igmp_group *grp; - /* reschedule socket general query */ igmp_sock_query_reschedule(igmp); + } - /* scan socket groups */ - for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, grp_node, - grp)) { - struct listnode *src_node; - struct igmp_source *src; - - /* reset group timers for groups in EXCLUDE mode */ - if (grp->group_filtermode_isexcl) - igmp_group_reset_gmi(grp); - - /* scan group sources */ - for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, - src_node, src)) { - - /* reset source timers for sources with running - * timers - */ - if (src->t_source_timer) - igmp_source_reset_gmi(igmp, grp, src); - } + /* scan socket groups */ + for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_group_list, grp_node, grp)) { + struct listnode *src_node; + struct igmp_source *src; + + /* reset group timers for groups in EXCLUDE mode */ + if (grp->group_filtermode_isexcl) + igmp_group_reset_gmi(grp); + + /* scan group sources */ + for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, src_node, + src)) { + + /* reset source timers for sources with running + * timers + */ + if (src->t_source_timer) + igmp_source_reset_gmi(grp, src); } } } diff --git a/pimd/pim_neighbor.h b/pimd/pim_neighbor.h index b461098a60..d71b2b87c3 100644 --- a/pimd/pim_neighbor.h +++ b/pimd/pim_neighbor.h @@ -27,6 +27,7 @@ #include "prefix.h" #include "pim_tlv.h" +#include "pim_iface.h" struct pim_neighbor { int64_t creation; /* timestamp of creation */ diff --git a/pimd/pim_oil.h b/pimd/pim_oil.h index b0aa2b17c5..af8ac84594 100644 --- a/pimd/pim_oil.h +++ b/pimd/pim_oil.h @@ -20,8 +20,9 @@ #ifndef PIM_OIL_H #define PIM_OIL_H +struct pim_interface; + #include "pim_mroute.h" -#include "pim_iface.h" /* * Where did we get this (S,G) from? diff --git a/pimd/pim_rp.c b/pimd/pim_rp.c index 3e3b6dddb5..f2a969e04a 100644 --- a/pimd/pim_rp.c +++ b/pimd/pim_rp.c @@ -42,7 +42,7 @@ #include "pim_rpf.h" #include "pim_sock.h" #include "pim_memory.h" -#include "pim_iface.h" +#include "pim_neighbor.h" #include "pim_msdp.h" #include "pim_nht.h" #include "pim_mroute.h" diff --git a/pimd/pim_rp.h b/pimd/pim_rp.h index dd7cd5d75e..595025e5c9 100644 --- a/pimd/pim_rp.h +++ b/pimd/pim_rp.h @@ -24,9 +24,10 @@ #include "prefix.h" #include "vty.h" #include "plist.h" -#include "pim_iface.h" #include "pim_rpf.h" +struct pim_interface; + enum rp_source { RP_SRC_NONE = 0, RP_SRC_STATIC, diff --git a/pimd/pim_rpf.c b/pimd/pim_rpf.c index 66c6df65ad..b93f85e48c 100644 --- a/pimd/pim_rpf.c +++ b/pimd/pim_rpf.c @@ -31,6 +31,7 @@ #include "pim_pim.h" #include "pim_str.h" #include "pim_iface.h" +#include "pim_neighbor.h" #include "pim_zlookup.h" #include "pim_ifchannel.h" #include "pim_time.h" diff --git a/pimd/pim_rpf.h b/pimd/pim_rpf.h index f006519b71..006aa1b636 100644 --- a/pimd/pim_rpf.h +++ b/pimd/pim_rpf.h @@ -22,9 +22,6 @@ #include <zebra.h> -#include "pim_upstream.h" -#include "pim_neighbor.h" - /* RFC 4601: diff --git a/pimd/pim_upstream.h b/pimd/pim_upstream.h index 56039d5605..ea3b564f8a 100644 --- a/pimd/pim_upstream.h +++ b/pimd/pim_upstream.h @@ -24,7 +24,7 @@ #include <prefix.h> #include "plist.h" -#include <pimd/pim_rpf.h> +#include "pim_rpf.h" #include "pim_str.h" #include "pim_ifchannel.h" diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c index 6f933e9e72..aa041df857 100644 --- a/pimd/pim_zebra.c +++ b/pimd/pim_zebra.c @@ -474,7 +474,7 @@ void igmp_anysource_forward_start(struct pim_instance *pim, assert(group->group_filtermode_isexcl); assert(listcount(group->group_source_list) < 1); - source = source_new(group, src_addr); + source = igmp_get_source_by_addr(group, src_addr, NULL); if (!source) { zlog_warn("%s: Failure to create * source", __func__); return; @@ -508,7 +508,7 @@ static void igmp_source_forward_reevaluate_one(struct pim_instance *pim, sg.src = source->source_addr; sg.grp = group->group_addr; - ch = pim_ifchannel_find(group->group_igmp_sock->interface, &sg); + ch = pim_ifchannel_find(group->interface, &sg); if (pim_is_grp_ssm(pim, group->group_addr)) { /* If SSM group withdraw local membership */ if (ch @@ -517,8 +517,8 @@ static void igmp_source_forward_reevaluate_one(struct pim_instance *pim, zlog_debug( "local membership del for %s as G is now SSM", pim_str_sg_dump(&sg)); - pim_ifchannel_local_membership_del( - group->group_igmp_sock->interface, &sg); + pim_ifchannel_local_membership_del(group->interface, + &sg); } } else { /* If ASM group add local membership */ @@ -529,8 +529,7 @@ static void igmp_source_forward_reevaluate_one(struct pim_instance *pim, "local membership add for %s as G is now ASM", pim_str_sg_dump(&sg)); pim_ifchannel_local_membership_add( - group->group_igmp_sock->interface, &sg, - false /*is_vxlan*/); + group->interface, &sg, false /*is_vxlan*/); } } } @@ -541,33 +540,24 @@ void igmp_source_forward_reevaluate_all(struct pim_instance *pim) FOR_ALL_INTERFACES (pim->vrf, ifp) { struct pim_interface *pim_ifp = ifp->info; - struct listnode *sock_node; - struct igmp_sock *igmp; + struct listnode *grpnode; + struct igmp_group *grp; if (!pim_ifp) continue; - /* scan igmp sockets */ - for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, - igmp)) { - struct listnode *grpnode; - struct igmp_group *grp; - - /* scan igmp groups */ - for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, - grpnode, grp)) { - struct listnode *srcnode; - struct igmp_source *src; - - /* scan group sources */ - for (ALL_LIST_ELEMENTS_RO( - grp->group_source_list, srcnode, - src)) { - igmp_source_forward_reevaluate_one(pim, - src); - } /* scan group sources */ - } /* scan igmp groups */ - } /* scan igmp sockets */ + /* scan igmp groups */ + for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_group_list, grpnode, + grp)) { + struct listnode *srcnode; + struct igmp_source *src; + + /* scan group sources */ + for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, + srcnode, src)) { + igmp_source_forward_reevaluate_one(pim, src); + } /* scan group sources */ + } /* scan igmp groups */ } /* scan interfaces */ } @@ -585,12 +575,10 @@ void igmp_source_forward_start(struct pim_instance *pim, sg.grp = source->source_group->group_addr; if (PIM_DEBUG_IGMP_TRACE) { - zlog_debug( - "%s: (S,G)=%s igmp_sock=%d oif=%s fwd=%d", __func__, - pim_str_sg_dump(&sg), - source->source_group->group_igmp_sock->fd, - source->source_group->group_igmp_sock->interface->name, - IGMP_SOURCE_TEST_FORWARDING(source->source_flags)); + zlog_debug("%s: (S,G)=%s oif=%s fwd=%d", __func__, + pim_str_sg_dump(&sg), + source->source_group->interface->name, + IGMP_SOURCE_TEST_FORWARDING(source->source_flags)); } /* Prevent IGMP interface from installing multicast route multiple @@ -600,13 +588,12 @@ void igmp_source_forward_start(struct pim_instance *pim, } group = source->source_group; - pim_oif = group->group_igmp_sock->interface->info; + pim_oif = group->interface->info; if (!pim_oif) { if (PIM_DEBUG_IGMP_TRACE) { zlog_debug("%s: multicast not enabled on oif=%s ?", __func__, - source->source_group->group_igmp_sock - ->interface->name); + source->source_group->interface->name); } return; } @@ -688,14 +675,10 @@ void igmp_source_forward_start(struct pim_instance *pim, */ if (PIM_DEBUG_IGMP_TRACE) { zlog_debug( - "%s: ignoring request for looped MFC entry (S,G)=%s: igmp_sock=%d oif=%s vif_index=%d", + "%s: ignoring request for looped MFC entry (S,G)=%s: oif=%s vif_index=%d", __func__, pim_str_sg_dump(&sg), source->source_group - ->group_igmp_sock - ->fd, - source->source_group - ->group_igmp_sock ->interface->name, input_iface_vif_index); } @@ -719,7 +702,7 @@ void igmp_source_forward_start(struct pim_instance *pim, if (PIM_I_am_DR(pim_oif) || PIM_I_am_DualActive(pim_oif)) { result = pim_channel_add_oif(source->source_channel_oil, - group->group_igmp_sock->interface, + group->interface, PIM_OIF_FLAG_PROTO_IGMP, __func__); if (result) { if (PIM_DEBUG_MROUTE) { @@ -733,7 +716,7 @@ void igmp_source_forward_start(struct pim_instance *pim, zlog_debug( "%s: %s was received on %s interface but we are not DR for that interface", __func__, pim_str_sg_dump(&sg), - group->group_igmp_sock->interface->name); + group->interface->name); return; } @@ -741,16 +724,15 @@ void igmp_source_forward_start(struct pim_instance *pim, Feed IGMPv3-gathered local membership information into PIM per-interface (S,G) state. */ - if (!pim_ifchannel_local_membership_add( - group->group_igmp_sock->interface, &sg, + if (!pim_ifchannel_local_membership_add(group->interface, &sg, false /*is_vxlan*/)) { if (PIM_DEBUG_MROUTE) zlog_warn("%s: Failure to add local membership for %s", __func__, pim_str_sg_dump(&sg)); pim_channel_del_oif(source->source_channel_oil, - group->group_igmp_sock->interface, - PIM_OIF_FLAG_PROTO_IGMP, __func__); + group->interface, PIM_OIF_FLAG_PROTO_IGMP, + __func__); return; } @@ -772,12 +754,10 @@ void igmp_source_forward_stop(struct igmp_source *source) sg.grp = source->source_group->group_addr; if (PIM_DEBUG_IGMP_TRACE) { - zlog_debug( - "%s: (S,G)=%s igmp_sock=%d oif=%s fwd=%d", __func__, - pim_str_sg_dump(&sg), - source->source_group->group_igmp_sock->fd, - source->source_group->group_igmp_sock->interface->name, - IGMP_SOURCE_TEST_FORWARDING(source->source_flags)); + zlog_debug("%s: (S,G)=%s oif=%s fwd=%d", __func__, + pim_str_sg_dump(&sg), + source->source_group->interface->name, + IGMP_SOURCE_TEST_FORWARDING(source->source_flags)); } /* Prevent IGMP interface from removing multicast route multiple @@ -800,9 +780,8 @@ void igmp_source_forward_stop(struct igmp_source *source) pim_forward_stop below. */ result = pim_channel_del_oif(source->source_channel_oil, - group->group_igmp_sock->interface, - PIM_OIF_FLAG_PROTO_IGMP, - __func__); + group->interface, PIM_OIF_FLAG_PROTO_IGMP, + __func__); if (result) { if (PIM_DEBUG_IGMP_TRACE) zlog_debug( @@ -815,8 +794,7 @@ void igmp_source_forward_stop(struct igmp_source *source) Feed IGMPv3-gathered local membership information into PIM per-interface (S,G) state. */ - pim_ifchannel_local_membership_del(group->group_igmp_sock->interface, - &sg); + pim_ifchannel_local_membership_del(group->interface, &sg); IGMP_SOURCE_DONT_FORWARDING(source->source_flags); } diff --git a/pimd/pim_zlookup.c b/pimd/pim_zlookup.c index dce936b8a9..abf9577bd5 100644 --- a/pimd/pim_zlookup.c +++ b/pimd/pim_zlookup.c @@ -31,6 +31,7 @@ #include "pimd.h" #include "pim_iface.h" +#include "pim_neighbor.h" #include "pim_pim.h" #include "pim_str.h" #include "pim_oil.h" diff --git a/pimd/pimd.h b/pimd/pimd.h index 88e692b50d..4cb860a6b7 100644 --- a/pimd/pimd.h +++ b/pimd/pimd.h @@ -136,7 +136,6 @@ extern const char *const PIM_ALL_ROUTERS; extern const char *const PIM_ALL_PIM_ROUTERS; extern const char *const PIM_ALL_IGMP_ROUTERS; -extern struct pim_router *router; extern struct zebra_privs_t pimd_privs; extern struct in_addr qpim_all_pim_routers_addr; extern uint8_t qpim_ecmp_enable; diff --git a/ripd/ripd.c b/ripd/ripd.c index 37f4b57431..84fb67956e 100644 --- a/ripd/ripd.c +++ b/ripd/ripd.c @@ -99,7 +99,7 @@ RB_GENERATE(rip_instance_head, rip, entry, rip_instance_compare) struct rip_instance_head rip_instances = RB_INITIALIZER(&rip_instances); -/* Utility function to set boradcast option to the socket. */ +/* Utility function to set broadcast option to the socket. */ static int sockopt_broadcast(int sock) { int ret; @@ -480,7 +480,7 @@ static void rip_rte_process(struct rte *rte, struct sockaddr_in *from, } /* Once the entry has been validated, update the metric by - adding the cost of the network on wich the message + adding the cost of the network on which the message arrived. If the result is greater than infinity, use infinity (RFC2453 Sec. 3.9.2) */ /* Zebra ripd can handle offset-list in. */ diff --git a/staticd/static_nb.c b/staticd/static_nb.c index c1a6253a1d..5935364d5a 100644 --- a/staticd/static_nb.c +++ b/staticd/static_nb.c @@ -21,7 +21,7 @@ #include "northbound.h" #include "libfrr.h" #include "static_nb.h" - +#include "static_vty.h" /* clang-format off */ @@ -29,10 +29,18 @@ const struct frr_yang_module_info frr_staticd_info = { .name = "frr-staticd", .nodes = { { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd", + .cbs = { + .cli_show = static_cli_show, + .cli_show_end = static_cli_show_end, + } + }, + { .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list", .cbs = { .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_create, .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_destroy, + .cli_cmp = static_route_list_cli_cmp, } }, { @@ -40,6 +48,7 @@ const struct frr_yang_module_info frr_staticd_info = { .cbs = { .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_create, .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_destroy, + .cli_cmp = static_path_list_cli_cmp, } }, { @@ -55,6 +64,8 @@ const struct frr_yang_module_info frr_staticd_info = { .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_create, .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_destroy, .pre_validate = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_pre_validate, + .cli_show = static_nexthop_cli_show, + .cli_cmp = static_nexthop_cli_cmp, } }, { @@ -110,6 +121,7 @@ const struct frr_yang_module_info frr_staticd_info = { .cbs = { .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_create, .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_destroy, + .cli_cmp = static_src_list_cli_cmp, } }, { @@ -117,6 +129,7 @@ const struct frr_yang_module_info frr_staticd_info = { .cbs = { .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_create, .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_destroy, + .cli_cmp = static_path_list_cli_cmp, } }, { @@ -132,6 +145,8 @@ const struct frr_yang_module_info frr_staticd_info = { .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_create, .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_destroy, .pre_validate = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_pre_validate, + .cli_show = static_src_nexthop_cli_show, + .cli_cmp = static_nexthop_cli_cmp, } }, { diff --git a/staticd/static_nb_config.c b/staticd/static_nb_config.c index deeca97b0e..470c7bdad5 100644 --- a/staticd/static_nb_config.c +++ b/staticd/static_nb_config.c @@ -122,7 +122,7 @@ struct nexthop_iter { static int nexthop_iter_cb(const struct lyd_node *dnode, void *arg) { struct nexthop_iter *iter = arg; - int nh_type; + enum static_nh_type nh_type; nh_type = yang_dnode_get_enum(dnode, "./nh-type"); @@ -141,7 +141,7 @@ static bool static_nexthop_create(struct nb_cb_create_args *args) struct static_path *pn; struct ipaddr ipaddr; struct static_nexthop *nh; - int nh_type; + enum static_nh_type nh_type; const char *ifname; const char *nh_vrf; @@ -304,7 +304,7 @@ static int static_nexthop_mpls_label_modify(struct nb_cb_modify_args *args) static int static_nexthop_onlink_modify(struct nb_cb_modify_args *args) { struct static_nexthop *nh; - static_types nh_type; + enum static_nh_type nh_type; switch (args->event) { case NB_EV_VALIDATE: @@ -352,7 +352,7 @@ static int static_nexthop_color_destroy(struct nb_cb_destroy_args *args) static int static_nexthop_bh_type_modify(struct nb_cb_modify_args *args) { struct static_nexthop *nh; - static_types nh_type; + enum static_nh_type nh_type; switch (args->event) { case NB_EV_VALIDATE: diff --git a/staticd/static_routes.c b/staticd/static_routes.c index 77a10092f8..60f384e517 100644 --- a/staticd/static_routes.c +++ b/staticd/static_routes.c @@ -186,7 +186,8 @@ void static_del_route(struct route_node *rn) route_unlock_node(rn); } -bool static_add_nexthop_validate(const char *nh_vrf_name, static_types type, +bool static_add_nexthop_validate(const char *nh_vrf_name, + enum static_nh_type type, struct ipaddr *ipaddr) { struct vrf *vrf; @@ -257,7 +258,7 @@ void static_del_path(struct static_path *pn) } struct static_nexthop *static_add_nexthop(struct static_path *pn, - static_types type, + enum static_nh_type type, struct ipaddr *ipaddr, const char *ifname, const char *nh_vrf, uint32_t color) @@ -772,7 +773,7 @@ void static_ifindex_update(struct interface *ifp, bool up) static_ifindex_update_af(ifp, up, AFI_IP6, SAFI_MULTICAST); } -void static_get_nh_type(static_types stype, char *type, size_t size) +void static_get_nh_type(enum static_nh_type stype, char *type, size_t size) { switch (stype) { case STATIC_IFNAME: diff --git a/staticd/static_routes.h b/staticd/static_routes.h index 2211384916..c901a8926a 100644 --- a/staticd/static_routes.h +++ b/staticd/static_routes.h @@ -47,14 +47,14 @@ enum static_blackhole_type { * The order for below macros should be in sync with * yang model typedef nexthop-type */ -typedef enum { +enum static_nh_type { STATIC_IFNAME = 1, STATIC_IPV4_GATEWAY, STATIC_IPV4_GATEWAY_IFNAME, STATIC_IPV6_GATEWAY, STATIC_IPV6_GATEWAY_IFNAME, STATIC_BLACKHOLE, -} static_types; +}; /* * Route Creation gives us: @@ -123,7 +123,7 @@ struct static_nexthop { enum static_install_states state; /* Flag for this static route's type. */ - static_types type; + enum static_nh_type type; /* * Nexthop value. @@ -169,7 +169,7 @@ extern struct zebra_privs_t static_privs; void static_fixup_vrf_ids(struct static_vrf *svrf); extern struct static_nexthop * -static_add_nexthop(struct static_path *pn, static_types type, +static_add_nexthop(struct static_path *pn, enum static_nh_type type, struct ipaddr *ipaddr, const char *ifname, const char *nh_vrf, uint32_t color); extern void static_install_nexthop(struct static_nexthop *nh); @@ -194,9 +194,10 @@ extern struct static_path *static_add_path(struct route_node *rn, uint32_t table_id, uint8_t distance); extern void static_del_path(struct static_path *pn); -extern void static_get_nh_type(static_types stype, char *type, size_t size); +extern void static_get_nh_type(enum static_nh_type stype, char *type, + size_t size); extern bool static_add_nexthop_validate(const char *nh_vrf_name, - static_types type, + enum static_nh_type type, struct ipaddr *ipaddr); extern struct stable_info *static_get_stable_info(struct route_node *rn); diff --git a/staticd/static_vrf.c b/staticd/static_vrf.c index 740d904690..4bea3075c9 100644 --- a/staticd/static_vrf.c +++ b/staticd/static_vrf.c @@ -23,11 +23,11 @@ #include "nexthop.h" #include "table.h" #include "srcdest_table.h" +#include "northbound_cli.h" #include "static_vrf.h" #include "static_routes.h" #include "static_zebra.h" -#include "static_vty.h" DEFINE_MTYPE_STATIC(STATIC, STATIC_RTABLE_INFO, "Static Route Table Info"); @@ -150,24 +150,16 @@ struct static_vrf *static_vrf_lookup_by_name(const char *name) static int static_vrf_config_write(struct vty *vty) { - struct vrf *vrf; - - RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { - if (vrf->vrf_id != VRF_DEFAULT) - vty_frame(vty, "vrf %s\n", vrf->name); + struct lyd_node *dnode; + int written = 0; - static_config(vty, vrf->info, AFI_IP, - SAFI_UNICAST, "ip route"); - static_config(vty, vrf->info, AFI_IP, - SAFI_MULTICAST, "ip mroute"); - static_config(vty, vrf->info, AFI_IP6, - SAFI_UNICAST, "ipv6 route"); - - if (vrf->vrf_id != VRF_DEFAULT) - vty_endframe(vty, "exit-vrf\n!\n"); + dnode = yang_dnode_get(running_config->dnode, "/frr-routing:routing"); + if (dnode) { + nb_cli_show_dnode_cmds(vty, dnode, false); + written = 1; } - return 0; + return written; } void static_vrf_init(void) diff --git a/staticd/static_vty.c b/staticd/static_vty.c index f16b40a23f..751a262775 100644 --- a/staticd/static_vty.c +++ b/staticd/static_vty.c @@ -56,7 +56,7 @@ static int static_route_leak(struct vty *vty, const char *svrf, int ret; struct prefix p, src; struct in_addr mask; - uint8_t type; + enum static_nh_type type; const char *bh_type; char xpath_prefix[XPATH_MAXLEN]; char xpath_nexthop[XPATH_MAXLEN]; @@ -357,129 +357,6 @@ static int static_route(struct vty *vty, afi_t afi, safi_t safi, table_str, false, NULL); } -/* Write static route configuration. */ -int static_config(struct vty *vty, struct static_vrf *svrf, afi_t afi, - safi_t safi, const char *cmd) -{ - char spacing[100]; - struct route_node *rn; - struct static_nexthop *nh; - struct static_path *pn; - struct route_table *stable; - struct static_route_info *si; - char buf[SRCDEST2STR_BUFFER]; - int write = 0; - struct stable_info *info; - - stable = svrf->stable[afi][safi]; - if (stable == NULL) - return write; - - snprintf(spacing, sizeof(spacing), "%s%s", - (svrf->vrf->vrf_id == VRF_DEFAULT) ? "" : " ", cmd); - - for (rn = route_top(stable); rn; rn = srcdest_route_next(rn)) { - si = static_route_info_from_rnode(rn); - if (!si) - continue; - info = static_get_stable_info(rn); - frr_each(static_path_list, &si->path_list, pn) { - frr_each(static_nexthop_list, &pn->nexthop_list, nh) { - vty_out(vty, "%s %s", spacing, - srcdest_rnode2str(rn, buf, - sizeof(buf))); - - switch (nh->type) { - case STATIC_IPV4_GATEWAY: - vty_out(vty, " %pI4", &nh->addr.ipv4); - break; - case STATIC_IPV6_GATEWAY: - vty_out(vty, " %s", - inet_ntop(AF_INET6, - &nh->addr.ipv6, buf, - sizeof(buf))); - break; - case STATIC_IFNAME: - vty_out(vty, " %s", nh->ifname); - break; - case STATIC_BLACKHOLE: - switch (nh->bh_type) { - case STATIC_BLACKHOLE_DROP: - vty_out(vty, " blackhole"); - break; - case STATIC_BLACKHOLE_NULL: - vty_out(vty, " Null0"); - break; - case STATIC_BLACKHOLE_REJECT: - vty_out(vty, " reject"); - break; - } - break; - case STATIC_IPV4_GATEWAY_IFNAME: - vty_out(vty, " %s %s", - inet_ntop(AF_INET, - &nh->addr.ipv4, buf, - sizeof(buf)), - nh->ifname); - break; - case STATIC_IPV6_GATEWAY_IFNAME: - vty_out(vty, " %s %s", - inet_ntop(AF_INET6, - &nh->addr.ipv6, buf, - sizeof(buf)), - nh->ifname); - break; - } - - if (pn->tag) - vty_out(vty, " tag %" ROUTE_TAG_PRI, - pn->tag); - - if (pn->distance - != ZEBRA_STATIC_DISTANCE_DEFAULT) - vty_out(vty, " %u", pn->distance); - - /* Label information */ - if (nh->snh_label.num_labels) - vty_out(vty, " label %s", - mpls_label2str( - nh->snh_label - .num_labels, - nh->snh_label.label, - buf, sizeof(buf), 0)); - - if (!strmatch(nh->nh_vrfname, - info->svrf->vrf->name)) - vty_out(vty, " nexthop-vrf %s", - nh->nh_vrfname); - - /* - * table ID from VRF overrides - * configured - */ - if (pn->table_id - && svrf->vrf->data.l.table_id - == RT_TABLE_MAIN) - vty_out(vty, " table %u", pn->table_id); - - if (nh->onlink) - vty_out(vty, " onlink"); - - /* - * SR-TE color - */ - if (nh->color != 0) - vty_out(vty, " color %u", nh->color); - - vty_out(vty, "\n"); - - write = 1; - } - } - } - return write; -} - /* Static unicast routes for multicast RPF lookup. */ DEFPY_YANG (ip_mroute_dist, ip_mroute_dist_cmd, @@ -1124,6 +1001,278 @@ DEFPY_YANG(ipv6_route_vrf, ifname, flag, tag_str, distance_str, label, table_str, false, color_str); } + +void static_cli_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + const char *vrf; + + vrf = yang_dnode_get_string(dnode, "../vrf"); + if (strcmp(vrf, VRF_DEFAULT_NAME)) + vty_out(vty, "vrf %s\n", vrf); +} + +void static_cli_show_end(struct vty *vty, struct lyd_node *dnode) +{ + const char *vrf; + + vrf = yang_dnode_get_string(dnode, "../vrf"); + if (strcmp(vrf, VRF_DEFAULT_NAME)) + vty_out(vty, "exit-vrf\n"); +} + +struct mpls_label_iter { + struct vty *vty; + bool first; +}; + +static int mpls_label_iter_cb(const struct lyd_node *dnode, void *arg) +{ + struct mpls_label_iter *iter = arg; + + if (yang_dnode_exists(dnode, "./label")) { + if (iter->first) + vty_out(iter->vty, " label %s", + yang_dnode_get_string(dnode, "./label")); + else + vty_out(iter->vty, "/%s", + yang_dnode_get_string(dnode, "./label")); + iter->first = false; + } + + return YANG_ITER_CONTINUE; +} + +static void nexthop_cli_show(struct vty *vty, const struct lyd_node *route, + const struct lyd_node *src, + const struct lyd_node *path, + const struct lyd_node *nexthop, bool show_defaults) +{ + const char *vrf; + const char *afi_safi; + afi_t afi; + safi_t safi; + enum static_nh_type nh_type; + enum static_blackhole_type bh_type; + uint32_t tag; + uint8_t distance; + struct mpls_label_iter iter; + const char *nexthop_vrf; + uint32_t table_id; + bool onlink; + + vrf = yang_dnode_get_string(route, "../../vrf"); + + afi_safi = yang_dnode_get_string(route, "./afi-safi"); + yang_afi_safi_identity2value(afi_safi, &afi, &safi); + + if (afi == AFI_IP) + vty_out(vty, "%sip", + strmatch(vrf, VRF_DEFAULT_NAME) ? "" : " "); + else + vty_out(vty, "%sipv6", + strmatch(vrf, VRF_DEFAULT_NAME) ? "" : " "); + + if (safi == SAFI_UNICAST) + vty_out(vty, " route"); + else + vty_out(vty, " mroute"); + + vty_out(vty, " %s", yang_dnode_get_string(route, "./prefix")); + + if (src) + vty_out(vty, " from %s", + yang_dnode_get_string(src, "./src-prefix")); + + nh_type = yang_dnode_get_enum(nexthop, "./nh-type"); + switch (nh_type) { + case STATIC_IFNAME: + vty_out(vty, " %s", + yang_dnode_get_string(nexthop, "./interface")); + break; + case STATIC_IPV4_GATEWAY: + case STATIC_IPV6_GATEWAY: + vty_out(vty, " %s", + yang_dnode_get_string(nexthop, "./gateway")); + break; + case STATIC_IPV4_GATEWAY_IFNAME: + case STATIC_IPV6_GATEWAY_IFNAME: + vty_out(vty, " %s", + yang_dnode_get_string(nexthop, "./gateway")); + vty_out(vty, " %s", + yang_dnode_get_string(nexthop, "./interface")); + break; + case STATIC_BLACKHOLE: + bh_type = yang_dnode_get_enum(nexthop, "./bh-type"); + switch (bh_type) { + case STATIC_BLACKHOLE_DROP: + vty_out(vty, " blackhole"); + break; + case STATIC_BLACKHOLE_NULL: + vty_out(vty, " Null0"); + break; + case STATIC_BLACKHOLE_REJECT: + vty_out(vty, " reject"); + break; + } + break; + } + + if (yang_dnode_exists(path, "./tag")) { + tag = yang_dnode_get_uint32(path, "./tag"); + if (tag != 0 || show_defaults) + vty_out(vty, " tag %" PRIu32, tag); + } + + distance = yang_dnode_get_uint8(path, "./distance"); + if (distance != ZEBRA_STATIC_DISTANCE_DEFAULT || show_defaults) + vty_out(vty, " %" PRIu8, distance); + + iter.vty = vty; + iter.first = true; + yang_dnode_iterate(mpls_label_iter_cb, &iter, nexthop, + "./mpls-label-stack/entry"); + + nexthop_vrf = yang_dnode_get_string(nexthop, "./vrf"); + if (strcmp(vrf, nexthop_vrf)) + vty_out(vty, " nexthop-vrf %s", nexthop_vrf); + + table_id = yang_dnode_get_uint32(path, "./table-id"); + if (table_id || show_defaults) + vty_out(vty, " table %" PRIu32, table_id); + + if (yang_dnode_exists(nexthop, "./onlink")) { + onlink = yang_dnode_get_bool(nexthop, "./onlink"); + if (onlink) + vty_out(vty, " onlink"); + } + + if (yang_dnode_exists(nexthop, "./srte-color")) + vty_out(vty, " color %s", + yang_dnode_get_string(nexthop, "./srte-color")); + + vty_out(vty, "\n"); +} + +void static_nexthop_cli_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + const struct lyd_node *path = yang_dnode_get_parent(dnode, "path-list"); + const struct lyd_node *route = + yang_dnode_get_parent(path, "route-list"); + + nexthop_cli_show(vty, route, NULL, path, dnode, show_defaults); +} + +void static_src_nexthop_cli_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + const struct lyd_node *path = yang_dnode_get_parent(dnode, "path-list"); + const struct lyd_node *src = yang_dnode_get_parent(path, "src-list"); + const struct lyd_node *route = yang_dnode_get_parent(src, "route-list"); + + nexthop_cli_show(vty, route, src, path, dnode, show_defaults); +} + +int static_nexthop_cli_cmp(struct lyd_node *dnode1, struct lyd_node *dnode2) +{ + enum static_nh_type nh_type1, nh_type2; + struct prefix prefix1, prefix2; + int ret = 0; + + nh_type1 = yang_dnode_get_enum(dnode1, "./nh-type"); + nh_type2 = yang_dnode_get_enum(dnode2, "./nh-type"); + + if (nh_type1 != nh_type2) + return (int)nh_type1 - (int)nh_type2; + + switch (nh_type1) { + case STATIC_IFNAME: + ret = if_cmp_name_func( + yang_dnode_get_string(dnode1, "./interface"), + yang_dnode_get_string(dnode2, "./interface")); + break; + case STATIC_IPV4_GATEWAY: + case STATIC_IPV6_GATEWAY: + yang_dnode_get_prefix(&prefix1, dnode1, "./gateway"); + yang_dnode_get_prefix(&prefix2, dnode2, "./gateway"); + ret = prefix_cmp(&prefix1, &prefix2); + break; + case STATIC_IPV4_GATEWAY_IFNAME: + case STATIC_IPV6_GATEWAY_IFNAME: + yang_dnode_get_prefix(&prefix1, dnode1, "./gateway"); + yang_dnode_get_prefix(&prefix2, dnode2, "./gateway"); + ret = prefix_cmp(&prefix1, &prefix2); + if (!ret) + ret = if_cmp_name_func( + yang_dnode_get_string(dnode1, "./interface"), + yang_dnode_get_string(dnode2, "./interface")); + break; + case STATIC_BLACKHOLE: + /* There's only one blackhole nexthop per route */ + ret = 0; + break; + } + + if (ret) + return ret; + + return if_cmp_name_func(yang_dnode_get_string(dnode1, "./vrf"), + yang_dnode_get_string(dnode2, "./vrf")); +} + +int static_route_list_cli_cmp(struct lyd_node *dnode1, struct lyd_node *dnode2) +{ + const char *afi_safi1, *afi_safi2; + afi_t afi1, afi2; + safi_t safi1, safi2; + struct prefix prefix1, prefix2; + + afi_safi1 = yang_dnode_get_string(dnode1, "./afi-safi"); + yang_afi_safi_identity2value(afi_safi1, &afi1, &safi1); + + afi_safi2 = yang_dnode_get_string(dnode2, "./afi-safi"); + yang_afi_safi_identity2value(afi_safi2, &afi2, &safi2); + + if (afi1 != afi2) + return (int)afi1 - (int)afi2; + + if (safi1 != safi2) + return (int)safi1 - (int)safi2; + + yang_dnode_get_prefix(&prefix1, dnode1, "./prefix"); + yang_dnode_get_prefix(&prefix2, dnode2, "./prefix"); + + return prefix_cmp(&prefix1, &prefix2); +} + +int static_src_list_cli_cmp(struct lyd_node *dnode1, struct lyd_node *dnode2) +{ + struct prefix prefix1, prefix2; + + yang_dnode_get_prefix(&prefix1, dnode1, "./src-prefix"); + yang_dnode_get_prefix(&prefix2, dnode2, "./src-prefix"); + + return prefix_cmp(&prefix1, &prefix2); +} + +int static_path_list_cli_cmp(struct lyd_node *dnode1, struct lyd_node *dnode2) +{ + uint32_t table_id1, table_id2; + uint8_t distance1, distance2; + + table_id1 = yang_dnode_get_uint32(dnode1, "./table-id"); + table_id2 = yang_dnode_get_uint32(dnode2, "./table-id"); + + if (table_id1 != table_id2) + return (int)table_id1 - (int)table_id2; + + distance1 = yang_dnode_get_uint8(dnode1, "./distance"); + distance2 = yang_dnode_get_uint8(dnode2, "./distance"); + + return (int)distance1 - (int)distance2; +} + DEFPY_YANG(debug_staticd, debug_staticd_cmd, "[no] debug static [{events$events|route$route}]", NO_STR DEBUG_STR STATICD_STR diff --git a/staticd/static_vty.h b/staticd/static_vty.h index 01577685e5..8861afa468 100644 --- a/staticd/static_vty.h +++ b/staticd/static_vty.h @@ -23,8 +23,17 @@ extern "C" { #endif -int static_config(struct vty *vty, struct static_vrf *svrf, - afi_t afi, safi_t safi, const char *cmd); +void static_cli_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void static_cli_show_end(struct vty *vty, struct lyd_node *dnode); +void static_nexthop_cli_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void static_src_nexthop_cli_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +int static_nexthop_cli_cmp(struct lyd_node *dnode1, struct lyd_node *dnode2); +int static_route_list_cli_cmp(struct lyd_node *dnode1, struct lyd_node *dnode2); +int static_src_list_cli_cmp(struct lyd_node *dnode1, struct lyd_node *dnode2); +int static_path_list_cli_cmp(struct lyd_node *dnode1, struct lyd_node *dnode2); void static_vty_init(void); diff --git a/tests/bgpd/test_mpath.c b/tests/bgpd/test_mpath.c index 92efd4c3d6..77fd876594 100644 --- a/tests/bgpd/test_mpath.c +++ b/tests/bgpd/test_mpath.c @@ -310,7 +310,7 @@ static int setup_bgp_path_info_mpath_update(testcase_t *t) str2prefix("42.1.1.0/24", &test_rn.p); rt_node = bgp_dest_to_rnode(&test_rn); memcpy((struct route_table *)&rt_node->table, &rt->route_table, - sizeof(struct route_table *)); + sizeof(struct route_table)); setup_bgp_mp_list(t); for (i = 0; i < test_mp_list_info_count; i++) bgp_path_info_add(&test_rn, &test_mp_list_info[i]); diff --git a/tests/lib/cli/test_cli.c b/tests/lib/cli/test_cli.c index 8dba1e29f0..f8d74018dd 100644 --- a/tests/lib/cli/test_cli.c +++ b/tests/lib/cli/test_cli.c @@ -40,6 +40,8 @@ DUMMY_DEFUN(cmd12, "alt a A.B.C.D"); DUMMY_DEFUN(cmd13, "alt a X:X::X:X"); DUMMY_DEFUN(cmd14, "pat g { foo A.B.C.D$foo|foo|bar X:X::X:X$bar| baz } [final]"); +DUMMY_DEFUN(cmd15, "no pat g ![ WORD ]"); +DUMMY_DEFUN(cmd16, "[no] pat h {foo ![A.B.C.D$foo]|bar X:X::X:X$bar} final"); #include "tests/lib/cli/test_cli_clippy.c" @@ -81,5 +83,7 @@ void test_init(int argc, char **argv) install_element(ENABLE_NODE, &cmd13_cmd); } install_element(ENABLE_NODE, &cmd14_cmd); + install_element(ENABLE_NODE, &cmd15_cmd); + install_element(ENABLE_NODE, &cmd16_cmd); install_element(ENABLE_NODE, &magic_test_cmd); } diff --git a/tests/lib/cli/test_cli.in b/tests/lib/cli/test_cli.in index 5c146ef984..bd685a6231 100644 --- a/tests/lib/cli/test_cli.in +++ b/tests/lib/cli/test_cli.in @@ -74,6 +74,23 @@ pat f pat f foo pat f key +no pat g +no pat g test +no pat g test more + +pat h foo ?1.2.3.4 final +no pat h foo ?1.2.3.4 final +pat h foo final +no pat h foo final +pat h bar final +no pat h bar final +pat h bar 1::2 final +no pat h bar 1::2 final +pat h bar 1::2 foo final +no pat h bar 1::2 foo final +pat h bar 1::2 foo 1.2.3.4 final +no pat h bar 1::2 foo 1.2.3.4 final + alt a a?b alt a 1 .2?.3.4 alt a 1 :2? ::?3 diff --git a/tests/lib/cli/test_cli.refout.in b/tests/lib/cli/test_cli.refout.in index 1f38e08b20..84365810d5 100644 --- a/tests/lib/cli/test_cli.refout.in +++ b/tests/lib/cli/test_cli.refout.in @@ -147,7 +147,7 @@ test# papat % Command incomplete.
test# pat
a b c d e f
-g
+g h
test# pat
% Command incomplete.
test#
@@ -263,6 +263,100 @@ cmd10 with 3 args. [01] f@(null): f
[02] key@(null): key
test#
+test# no pat g
+cmd15 with 3 args.
+[00] no@(null): no
+[01] pat@(null): pat
+[02] g@(null): g
+test# no pat g test
+cmd15 with 4 args.
+[00] no@(null): no
+[01] pat@(null): pat
+[02] g@(null): g
+[03] WORD@g: test
+test# no pat g test more
+% [NONE] Unknown command: no pat g test more
+test#
+test# pat h foo
+ A.B.C.D 04
+test# pat h foo 1.2.3.4 final
+cmd16 with 5 args.
+[00] pat@(null): pat
+[01] h@(null): h
+[02] foo@(null): foo
+[03] A.B.C.D@foo: 1.2.3.4
+[04] final@(null): final
+test# no pat h foo
+ A.B.C.D 04
+ bar 05
+ final 07
+test# no pat h foo 1.2.3.4 final
+cmd16 with 6 args.
+[00] no@no: no
+[01] pat@(null): pat
+[02] h@(null): h
+[03] foo@(null): foo
+[04] A.B.C.D@foo: 1.2.3.4
+[05] final@(null): final
+test# pat h foo final
+% [NONE] Unknown command: pat h foo final
+test# no pat h foo final
+cmd16 with 5 args.
+[00] no@no: no
+[01] pat@(null): pat
+[02] h@(null): h
+[03] foo@(null): foo
+[04] final@(null): final
+test# pat h bar final
+% [NONE] Unknown command: pat h bar final
+test# no pat h bar final
+% [NONE] Unknown command: no pat h bar final
+test# pat h bar 1::2 final
+cmd16 with 5 args.
+[00] pat@(null): pat
+[01] h@(null): h
+[02] bar@(null): bar
+[03] X:X::X:X@bar: 1::2
+[04] final@(null): final
+test# no pat h bar 1::2 final
+cmd16 with 6 args.
+[00] no@no: no
+[01] pat@(null): pat
+[02] h@(null): h
+[03] bar@(null): bar
+[04] X:X::X:X@bar: 1::2
+[05] final@(null): final
+test# pat h bar 1::2 foo final
+% [NONE] Unknown command: pat h bar 1::2 foo final
+test# no pat h bar 1::2 foo final
+cmd16 with 7 args.
+[00] no@no: no
+[01] pat@(null): pat
+[02] h@(null): h
+[03] bar@(null): bar
+[04] X:X::X:X@bar: 1::2
+[05] foo@(null): foo
+[06] final@(null): final
+test# pat h bar 1::2 foo 1.2.3.4 final
+cmd16 with 7 args.
+[00] pat@(null): pat
+[01] h@(null): h
+[02] bar@(null): bar
+[03] X:X::X:X@bar: 1::2
+[04] foo@(null): foo
+[05] A.B.C.D@foo: 1.2.3.4
+[06] final@(null): final
+test# no pat h bar 1::2 foo 1.2.3.4 final
+cmd16 with 8 args.
+[00] no@no: no
+[01] pat@(null): pat
+[02] h@(null): h
+[03] bar@(null): bar
+[04] X:X::X:X@bar: 1::2
+[05] foo@(null): foo
+[06] A.B.C.D@foo: 1.2.3.4
+[07] final@(null): final
+test#
test# alt a
test# alt a a
WORD 02
diff --git a/tests/lib/test_nexthop.c b/tests/lib/test_nexthop.c index 659d207b4e..7cf687dffe 100644 --- a/tests/lib/test_nexthop.c +++ b/tests/lib/test_nexthop.c @@ -112,15 +112,15 @@ static void test_run_first(void) nexthop_free(nh2); /* Blackhole */ - nh1 = nexthop_from_blackhole(BLACKHOLE_REJECT); - nh2 = nexthop_from_blackhole(BLACKHOLE_REJECT); + nh1 = nexthop_from_blackhole(BLACKHOLE_REJECT, 0); + nh2 = nexthop_from_blackhole(BLACKHOLE_REJECT, 0); ret = nexthop_cmp_basic(nh1, nh2); assert(ret == 0); nexthop_free(nh2); - nh2 = nexthop_from_blackhole(BLACKHOLE_NULL); + nh2 = nexthop_from_blackhole(BLACKHOLE_NULL, 0); ret = nexthop_cmp_basic(nh1, nh2); assert(ret != 0); diff --git a/tests/lib/test_printfrr.c b/tests/lib/test_printfrr.c index 21b3a916b8..06996a2f13 100644 --- a/tests/lib/test_printfrr.c +++ b/tests/lib/test_printfrr.c @@ -24,6 +24,7 @@ #include "lib/printfrr.h" #include "lib/memory.h" #include "lib/prefix.h" +#include "lib/nexthop.h" static int errors; @@ -253,5 +254,25 @@ int main(int argc, char **argv) printchk("\"\"", "%pSQqn", (char *)NULL); printchk("(null)", "%pSQq", (char *)NULL); + /* + * %pNH<foo> tests + * + * gateway addresses only for now: interfaces require more setup + */ + printchk("(null)", "%pNHcg", NULL); + printchk("(null)", "%pNHci", NULL); + + struct nexthop nh; + + memset(&nh, 0, sizeof(nh)); + + nh.type = NEXTHOP_TYPE_IPV4; + inet_aton("3.2.1.0", &nh.gate.ipv4); + printchk("3.2.1.0", "%pNHcg", &nh); + + nh.type = NEXTHOP_TYPE_IPV6; + inet_pton(AF_INET6, "fe2c::34", &nh.gate.ipv6); + printchk("fe2c::34", "%pNHcg", &nh); + return !!errors; } diff --git a/tests/subdir.am b/tests/subdir.am index b0be63c695..1edfda9bc2 100644 --- a/tests/subdir.am +++ b/tests/subdir.am @@ -183,7 +183,7 @@ TESTS_CXXFLAGS = \ # note no -Werror ALL_TESTS_LDADD = lib/libfrr.la $(LIBCAP) -BGP_TEST_LDADD = bgpd/libbgp.a $(RFPLDADD) $(ALL_TESTS_LDADD) $(LIBYANG_LIBS) -lm +BGP_TEST_LDADD = bgpd/libbgp.a $(RFPLDADD) $(ALL_TESTS_LDADD) $(LIBYANG_LIBS) $(UST_LIBS) -lm ISISD_TEST_LDADD = isisd/libisis.a $(ALL_TESTS_LDADD) if GRPC GRPC_TESTS_LDADD = staticd/libstatic.a grpc/libfrrgrpc_pb.la -lgrpc++ -lprotobuf $(ALL_TESTS_LDADD) $(LIBYANG_LIBS) -lm diff --git a/tests/topotests/all_protocol_startup/test_all_protocol_startup.py b/tests/topotests/all_protocol_startup/test_all_protocol_startup.py index b1203570a1..1b99fcea1f 100644 --- a/tests/topotests/all_protocol_startup/test_all_protocol_startup.py +++ b/tests/topotests/all_protocol_startup/test_all_protocol_startup.py @@ -932,6 +932,9 @@ def test_bgp_summary(): # Remove Unknown Summary (all of it) actual = re.sub(r"Unknown Summary \(VRF default\):", "", actual) actual = re.sub(r"No Unknown neighbor is configured", "", actual) + # Make Connect/Active/Idle the same (change them all to Active) + actual = re.sub(r" Connect ", " Active ", actual) + actual = re.sub(r" Idle ", " Active ", actual) actual = re.sub( r"IPv4 labeled-unicast Summary \(VRF default\):", "", actual @@ -1089,6 +1092,9 @@ def test_bgp_ipv6_summary(): # Remove Unknown Summary (all of it) actual = re.sub(r"Unknown Summary \(VRF default\):", "", actual) actual = re.sub(r"No Unknown neighbor is configured", "", actual) + # Make Connect/Active/Idle the same (change them all to Active) + actual = re.sub(r" Connect ", " Active ", actual) + actual = re.sub(r" Idle ", " Active ", actual) # Remove Labeled Unicast Summary (all of it) actual = re.sub( diff --git a/tests/topotests/bgp_default_route_route_map_match_set/test_bgp_default-originate_route-map_match_set.py b/tests/topotests/bgp_default_route_route_map_match_set/test_bgp_default-originate_route-map_match_set.py index 27451ec7b3..c890b0d7dc 100644 --- a/tests/topotests/bgp_default_route_route_map_match_set/test_bgp_default-originate_route-map_match_set.py +++ b/tests/topotests/bgp_default_route_route_map_match_set/test_bgp_default-originate_route-map_match_set.py @@ -92,7 +92,13 @@ def test_bgp_default_originate_route_map(): def _bgp_default_route_has_metric(router): output = json.loads(router.vtysh_cmd("show ip bgp 0.0.0.0/0 json")) expected = { - "paths": [{"aspath": {"string": "65000 65000 65000 65000"}, "metric": 123}] + "paths": [ + { + "aspath": {"string": "65000 65000 65000 65000"}, + "metric": 123, + "community": None, + } + ] } return topotest.json_cmp(output, expected) diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/host2/zebra.conf b/tests/topotests/bgp_evpn_overlay_index_gateway/host2/zebra.conf index 9135545c58..b9f80f112d 100644 --- a/tests/topotests/bgp_evpn_overlay_index_gateway/host2/zebra.conf +++ b/tests/topotests/bgp_evpn_overlay_index_gateway/host2/zebra.conf @@ -1,4 +1,4 @@ ! -int host1-eth0 +int host2-eth0 ip address 50.0.1.21/24 ipv6 address 50:0:1::21/48 diff --git a/tests/topotests/bgp_prefix_sid2/peer1/exabgp.cfg b/tests/topotests/bgp_prefix_sid2/peer1/exabgp.cfg index ad1b15a26c..3819179570 100644 --- a/tests/topotests/bgp_prefix_sid2/peer1/exabgp.cfg +++ b/tests/topotests/bgp_prefix_sid2/peer1/exabgp.cfg @@ -15,14 +15,14 @@ group controller { next-hop 2001::2; extended-community [ target:2:10 ]; label 3; - attribute [0x28 0xc0 0x0500150020010db800010001000000000000000100ffff00 ]; + attribute [0x28 0xc0 0x050019000100150020010db800010001000000000000000100ffff00 ]; } route 2001:2::/64 { rd 2:10; next-hop 2001::2; extended-community [ target:2:10 ]; label 3; - attribute [0x28 0xc0 0x0500150020010db800010001000000000000000100ffff00 ]; + attribute [0x28 0xc0 0x050019000100150020010db800010001000000000000000100ffff00 ]; } } } diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/test_bgp_snmp_mplsvpn.py b/tests/topotests/bgp_snmp_mplsl3vpn/test_bgp_snmp_mplsvpn.py index 3e6e417211..0d27474cbd 100755 --- a/tests/topotests/bgp_snmp_mplsl3vpn/test_bgp_snmp_mplsvpn.py +++ b/tests/topotests/bgp_snmp_mplsl3vpn/test_bgp_snmp_mplsvpn.py @@ -151,13 +151,6 @@ def setup_module(mod): r1.run("sysctl -w net.mpls.conf.r1-eth0.input=1") r1.run("sysctl -w net.mpls.conf.r1-eth1.input=1") r1.run("sysctl -w net.mpls.conf.r1-eth2.input=1") - r2.run("sysctl -w net.mpls.conf.r1-eth0.input=1") - r2.run("sysctl -w net.mpls.conf.r1-eth1.input=1") - r3.run("sysctl -w net.mpls.conf.r1-eth0.input=1") - r3.run("sysctl -w net.mpls.conf.r1-eth1.input=1") - r3.run("sysctl -w net.mpls.conf.r1-eth2.input=1") - r4.run("sysctl -w net.mpls.conf.r1-eth0.input=1") - r4.run("sysctl -w net.mpls.conf.r1-eth1.input=1") router_list = tgen.routers() diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vpnv6_rib_locator_deleted.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vpnv6_rib_locator_deleted.json new file mode 100644 index 0000000000..f2df9be49d --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vpnv6_rib_locator_deleted.json @@ -0,0 +1,160 @@ +{ + "vrfId": 0, + "vrfName": "default", + "routerId": "1.1.1.1", + "defaultLocPrf": 100, + "localAS": 1, + "routes": { + "routeDistinguishers": { + "1:10": { + "2001:1::/64": [ + { + "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": [ + { + "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": [ + { + "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 + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vpnv6_rib_locator_recreated.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vpnv6_rib_locator_recreated.json new file mode 100644 index 0000000000..0fdd3d6dc0 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vpnv6_rib_locator_recreated.json @@ -0,0 +1,169 @@ +{ + "vrfId": 0, + "vrfName": "default", + "routerId": "1.1.1.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 + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vrf10_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vrf10_rib.json index fa05972a35..141c1cb957 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vrf10_rib.json +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vrf10_rib.json @@ -48,12 +48,9 @@ "interfaceName": "eth0", "vrf": "default", "active": true, - "labels": [ - 3 - ], "weight": 1, "seg6": { - "segs": "2001:db8:2:2::100" + "segs": "2001:db8:2:2:100::" } } ], diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vrf20_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vrf20_rib.json index 0155557242..e20998061f 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vrf20_rib.json +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vrf20_rib.json @@ -22,12 +22,9 @@ "interfaceName": "eth0", "vrf": "default", "active": true, - "labels": [ - 3 - ], "weight": 1, "seg6": { - "segs": "2001:db8:2:2::200" + "segs": "2001:db8:2:2:200::" } } ], @@ -83,12 +80,9 @@ "interfaceName": "eth0", "vrf": "default", "active": true, - "labels": [ - 3 - ], "weight": 1, "seg6": { - "segs": "2001:db8:2:2::200" + "segs": "2001:db8:2:2:200::" } } ], diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/zebra.conf index ec36870369..68b5730a63 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/zebra.conf +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/zebra.conf @@ -34,7 +34,9 @@ segment-routing 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 ! diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vpnv6_rib_locator_deleted.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vpnv6_rib_locator_deleted.json new file mode 100644 index 0000000000..25cdf031c3 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vpnv6_rib_locator_deleted.json @@ -0,0 +1,93 @@ +{ + "vrfId": 0, + "vrfName": "default", + "routerId": "2.2.2.2", + "defaultLocPrf": 100, + "localAS": 2, + "routes": { + "routeDistinguishers": { + "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 + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vpnv6_rib_locator_recreated.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vpnv6_rib_locator_recreated.json new file mode 100644 index 0000000000..03bbcc008d --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vpnv6_rib_locator_recreated.json @@ -0,0 +1,169 @@ +{ + "vrfId": 0, + "vrfName": "default", + "routerId": "2.2.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 + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vrf10_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vrf10_rib.json index 887eb24386..7f8a930d00 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vrf10_rib.json +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vrf10_rib.json @@ -22,12 +22,9 @@ "interfaceName": "eth0", "vrf": "default", "active": true, - "labels": [ - 3 - ], "weight": 1, "seg6": { - "segs": "2001:db8:1:1::100" + "segs": "2001:db8:1:1:100::" } } ], @@ -83,12 +80,9 @@ "interfaceName": "eth0", "vrf": "default", "active": true, - "labels": [ - 3 - ], "weight": 1, "seg6": { - "segs": "2001:db8:1:1::100" + "segs": "2001:db8:1:1:100::" } } ], diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vrf20_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vrf20_rib.json index c118518423..104bdc30d2 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vrf20_rib.json +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vrf20_rib.json @@ -48,12 +48,9 @@ "interfaceName": "eth0", "vrf": "default", "active": true, - "labels": [ - 3 - ], "weight": 1, "seg6": { - "segs": "2001:db8:1:1::200" + "segs": "2001:db8:1:1:200::" } } ], diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/zebra.conf index f3e025d23a..91fd92d422 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/zebra.conf +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/zebra.conf @@ -35,6 +35,8 @@ 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 ! diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/test_bgp_srv6l3vpn_to_bgp_vrf.py b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/test_bgp_srv6l3vpn_to_bgp_vrf.py index 2d544c1ccf..e0cf8c88e6 100755 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/test_bgp_srv6l3vpn_to_bgp_vrf.py +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/test_bgp_srv6l3vpn_to_bgp_vrf.py @@ -129,6 +129,10 @@ def setup_module(mod): tgen.gears["r2"].run("ip link set eth3 master vrf20") 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. + def teardown_module(mod): tgen = get_topogen() @@ -143,7 +147,22 @@ def open_json_file(filename): assert False, "Could not read file {}".format(filename) -def test_rib(): +def check_ping(name, dest_addr, expect_connected): + def _check(name, dest_addr, match): + tgen = get_topogen() + output = tgen.gears[name].run("ping6 {} -c 1 -w 1".format(dest_addr)) + logger.info(output) + assert match in output, "ping fail" + + 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() @@ -152,51 +171,131 @@ def test_rib(): expected = open_json_file("{}/{}".format(CWD, expected_file)) return topotest.json_cmp(output, expected) - def check(name, cmd, expected_file): - 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" - - check("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib.json") - check("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib.json") - check("r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_rib.json") - check("r1", "show ipv6 route vrf vrf20 json", "r1/vrf20_rib.json") - check("r2", "show ipv6 route vrf vrf10 json", "r2/vrf10_rib.json") - check("r2", "show ipv6 route vrf vrf20 json", "r2/vrf20_rib.json") - check("ce1", "show ipv6 route json", "ce1/ipv6_rib.json") - check("ce2", "show ipv6 route json", "ce2/ipv6_rib.json") - check("ce3", "show ipv6 route json", "ce3/ipv6_rib.json") - check("ce4", "show ipv6 route json", "ce4/ipv6_rib.json") - check("ce5", "show ipv6 route json", "ce5/ipv6_rib.json") - check("ce6", "show ipv6 route json", "ce6/ipv6_rib.json") + 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(): - def _check(name, dest_addr, match): - tgen = get_topogen() - output = tgen.gears[name].run("ping6 {} -c 1 -w 1".format(dest_addr)) - logger.info(output) - assert match in output, "ping fail" + 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 check(name, dest_addr, match): - 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" - - check("ce1", "2001:2::2", " 0% packet loss") - check("ce1", "2001:3::2", " 0% packet loss") - check("ce1", "2001:4::2", " 100% packet loss") - check("ce1", "2001:5::2", " 100% packet loss") - check("ce1", "2001:6::2", " 100% packet loss") - check("ce4", "2001:1::2", " 100% packet loss") - check("ce4", "2001:2::2", " 100% packet loss") - check("ce4", "2001:3::2", " 100% packet loss") - check("ce4", "2001:5::2", " 0% packet loss") - check("ce4", "2001:6::2", " 0% packet loss") + +def test_locator_delete(): + check_ping("ce1", "2001:2::2", True) + get_topogen().gears["r1"].vtysh_cmd( + """ + configure terminal + segment-routing + srv6 + locators + no locator loc1 + """ + ) + check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_locator_deleted.json") + check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_locator_deleted.json") + check_ping("ce1", "2001:2::2", False) + + +def test_locator_recreate(): + check_ping("ce1", "2001:2::2", False) + get_topogen().gears["r1"].vtysh_cmd( + """ + configure terminal + segment-routing + srv6 + locators + locator loc1 + prefix 2001:db8:1:1::/64 + """ + ) + check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_locator_recreated.json") + check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_locator_recreated.json") + check_ping("ce1", "2001:2::2", True) + + +def test_bgp_locator_unset(): + check_ping("ce1", "2001:2::2", True) + get_topogen().gears["r1"].vtysh_cmd( + """ + configure terminal + router bgp 1 + segment-routing srv6 + no locator loc1 + """ + ) + check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_locator_deleted.json") + check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_locator_deleted.json") + check_ping("ce1", "2001:2::2", False) + + +def test_bgp_locator_reset(): + check_ping("ce1", "2001:2::2", False) + get_topogen().gears["r1"].vtysh_cmd( + """ + configure terminal + router bgp 1 + segment-routing srv6 + locator loc1 + """ + ) + check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_locator_recreated.json") + check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_locator_recreated.json") + check_ping("ce1", "2001:2::2", True) + + +def test_bgp_srv6_unset(): + check_ping("ce1", "2001:2::2", True) + get_topogen().gears["r1"].vtysh_cmd( + """ + configure terminal + router bgp 1 + no segment-routing srv6 + """ + ) + check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_locator_deleted.json") + check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_locator_deleted.json") + check_ping("ce1", "2001:2::2", False) + + +def test_bgp_srv6_reset(): + check_ping("ce1", "2001:2::2", False) + get_topogen().gears["r1"].vtysh_cmd( + """ + configure terminal + router bgp 1 + segment-routing srv6 + locator loc1 + """ + ) + check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_locator_recreated.json") + check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_locator_recreated.json") + check_ping("ce1", "2001:2::2", True) if __name__ == "__main__": diff --git a/tests/topotests/conftest.py b/tests/topotests/conftest.py index ed55490c09..7fe6a5aea1 100755 --- a/tests/topotests/conftest.py +++ b/tests/topotests/conftest.py @@ -6,13 +6,14 @@ import glob import os import pdb import re +import subprocess import sys import time import pytest import lib.fixtures from lib import topolog -from lib.micronet import Commander +from lib.micronet import Commander, proc_error from lib.micronet_cli import cli from lib.micronet_compat import Mininet, cleanup_current, cleanup_previous from lib.topogen import diagnose_env, get_topogen @@ -256,6 +257,23 @@ def pytest_configure(config): 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 + + def assert_feature_windows(b, feature): + if b and xdist_no_windows: + pytest.exit( + "{} use requires byobu/TMUX/XTerm under dist {}".format( + feature, os.environ["PYTEST_XDIST_MODE"] + ) + ) + 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 # --------------------------------------- @@ -272,6 +290,7 @@ def pytest_configure(config): 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 [] @@ -279,31 +298,40 @@ def pytest_configure(config): 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 - topotest_extra_config["pause"] = config.getoption("--pause") + assert_feature_windows(pause_on_error, "--pause-on-error") + + pause = config.getoption("--pause") + topotest_extra_config["pause"] = pause + assert_feature_windows(pause, "--pause") topotest_extra_config["topology_only"] = config.getoption("--topology-only") @@ -403,20 +431,27 @@ def pytest_runtest_makereport(item, call): error_cmd = os.getenv("SHELL", commander.get_exec_path(["bash"])) if error_cmd: - # Really would like something better than using this global here. - # Not all tests use topogen though so get_topogen() won't work. + is_tmux = bool(os.getenv("TMUX", "")) + is_screen = not is_tmux and bool(os.getenv("STY", "")) + is_xterm = not is_tmux and not is_screen and bool(os.getenv("DISPLAY", "")) + + channel = None win_info = None wait_for_channels = [] + wait_for_procs = [] + # Really would like something better than using this global here. + # Not all tests use topogen though so get_topogen() won't work. for node in Mininet.g_mnet_inst.hosts.values(): pause = True - channel = ( - "{}-{}".format(os.getpid(), Commander.tmux_wait_gen) - if not isatty - else None - ) - Commander.tmux_wait_gen += 1 - wait_for_channels.append(channel) + if is_tmux: + channel = ( + "{}-{}".format(os.getpid(), Commander.tmux_wait_gen) + if not isatty + else None + ) + Commander.tmux_wait_gen += 1 + wait_for_channels.append(channel) pane_info = node.run_in_window( error_cmd, @@ -427,13 +462,22 @@ def pytest_runtest_makereport(item, call): tmux_target=win_info, wait_for=channel, ) - if win_info is None: - win_info = pane_info + if is_tmux: + if win_info is None: + win_info = pane_info + elif is_xterm: + assert isinstance(pane_info, subprocess.Popen) + wait_for_procs.append(pane_info) # Now wait on any channels for channel in wait_for_channels: logger.debug("Waiting on TMUX channel %s", channel) commander.cmd_raises([commander.get_exec_path("tmux"), "wait", channel]) + for p in wait_for_procs: + logger.debug("Waiting on TMUX xterm process %s", p) + o, e = p.communicate() + if p.wait(): + logger.warning("xterm proc failed: %s:", proc_error(p, o, e)) if error and topotest_extra_config["cli_on_error"]: # Really would like something better than using this global here. diff --git a/tests/topotests/example_test/r1/zebra.conf b/tests/topotests/example_test/r1/zebra.conf new file mode 100644 index 0000000000..b733b7b03c --- /dev/null +++ b/tests/topotests/example_test/r1/zebra.conf @@ -0,0 +1,8 @@ +interface r1-eth0 + ip address 192.168.1.1/24 + +interface r1-eth1 + ip address 192.168.2.1/24 + +interface r1-eth2 + ip address 192.168.3.1/24
\ No newline at end of file diff --git a/tests/topotests/example_test/r2/zebra.conf b/tests/topotests/example_test/r2/zebra.conf new file mode 100644 index 0000000000..c0921f54c9 --- /dev/null +++ b/tests/topotests/example_test/r2/zebra.conf @@ -0,0 +1,4 @@ +interface r2-eth0 + ip address 192.168.1.2/24 +interface r2-eth1 + ip address 192.168.3.2/24 diff --git a/tests/topotests/example_test/test_example.py b/tests/topotests/example_test/test_example.py index 72eceee612..30c3d248f7 100755 --- a/tests/topotests/example_test/test_example.py +++ b/tests/topotests/example_test/test_example.py @@ -36,6 +36,7 @@ def test_fail_example(): assert True, "Some Text with explaination in case of failure" +@pytest.mark.xfail def test_ls_exits_zero(): "Tests for ls command on invalid file" diff --git a/tests/topotests/example_test/test_template.py b/tests/topotests/example_test/test_template.py index e94bb905a5..4c073f259c 100644 --- a/tests/topotests/example_test/test_template.py +++ b/tests/topotests/example_test/test_template.py @@ -1,5 +1,5 @@ #!/usr/bin/env python - +# -*- coding: utf-8 eval: (blacken-mode 1) -*- # # <template>.py # Part of NetDEF Topology Tests @@ -29,54 +29,69 @@ import sys import pytest -# Import topogen and topotest helpers -from lib.topogen import Topogen, TopoRouter, get_topogen - +from lib.topogen import Topogen, TopoRouter +from lib.topolog import logger # TODO: select markers based on daemons used during test # pytest module level markers -""" -pytestmark = pytest.mark.bfdd # single marker pytestmark = [ - pytest.mark.bgpd, - pytest.mark.ospfd, - pytest.mark.ospf6d -] # multiple markers -""" - - + # pytest.mark.babeld, + # pytest.mark.bfdd, + # pytest.mark.bgpd, + # pytest.mark.eigrpd, + # pytest.mark.isisd, + # pytest.mark.ldpd, + # pytest.mark.nhrpd, + # pytest.mark.ospf6d, + pytest.mark.ospfd, + # pytest.mark.pathd, + # pytest.mark.pbrd, + # pytest.mark.pimd, + # pytest.mark.ripd, + # pytest.mark.ripngd, + # pytest.mark.sharpd, + # pytest.mark.staticd, + # pytest.mark.vrrpd, +] + +# Function we pass to Topogen to create the topology def build_topo(tgen): "Build function" # Create 2 routers - for routern in range(1, 3): - tgen.add_router("r{}".format(routern)) + r1 = tgen.add_router("r1") + r2 = tgen.add_router("r2") - # Create a switch with just one router connected to it to simulate a - # empty network. + # Create a p2p connection between r1 and r2 + tgen.add_link(r1, r2) + + # Create a switch with one router connected to it to simulate a empty network. switch = tgen.add_switch("s1") - switch.add_link(tgen.gears["r1"]) + switch.add_link(r1) - # Create a connection between r1 and r2 + # Create a p2p connection between r1 and r2 switch = tgen.add_switch("s2") - switch.add_link(tgen.gears["r1"]) - switch.add_link(tgen.gears["r2"]) + switch.add_link(r1) + switch.add_link(r2) -def setup_module(mod): - "Sets up the pytest environment" +# New form of setup/teardown using pytest fixture +@pytest.fixture(scope="module") +def tgen(request): + "Setup/Teardown the environment and provide tgen argument to tests" # This function initiates the topology build with Topogen... - tgen = Topogen(build_topo, mod.__name__) + tgen = Topogen(build_topo, request.module.__name__) - # The basic topology above could also have be more easily specified using a - # dictionary, remove the build_topo function and use the following instead: + # A basic topology similar to the above could also have be more easily specified + # using a # dictionary, remove the build_topo function and use the following + # instead: # # topodef = { # "s1": "r1" # "s2": ("r1", "r2") # } - # tgen = Topogen(topodef, mod.__name__) + # tgen = Topogen(topodef, request.module.__name__) # ... and here it calls initialization functions. tgen.start_topology() @@ -84,42 +99,69 @@ def setup_module(mod): # This is a sample of configuration loading. router_list = tgen.routers() - # For all registred routers, load the zebra configuration file - # CWD = os.path.dirname(os.path.realpath(__file__)) + # For all routers arrange for: + # - starting zebra using config file from <rtrname>/zebra.conf + # - starting ospfd using an empty config file. for rname, router in router_list.items(): - router.load_config( - TopoRouter.RD_ZEBRA, - # Uncomment next line to load configuration from ./router/zebra.conf - # os.path.join(CWD, '{}/zebra.conf'.format(rname)) - ) + router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf") + router.load_config(TopoRouter.RD_OSPF) - # After loading the configurations, this function loads configured daemons. + # Start and configure the router daemons tgen.start_router() + # Provide tgen as argument to each test function + yield tgen -def teardown_module(mod): - "Teardown the pytest environment" - tgen = get_topogen() - - # This function tears down the whole topology. + # Teardown after last test runs tgen.stop_topology() -def test_call_cli(): - "Dummy test that just calls tgen.cli() so we can interact with the build." - tgen = get_topogen() - # Don't run this test if we have any failure. +# Fixture that executes before each test +@pytest.fixture(autouse=True) +def skip_on_failure(tgen): if tgen.routers_have_failure(): - pytest.skip(tgen.errors) + pytest.skip("skipped because of previous test failure") + + +# =================== +# The tests functions +# =================== + - # logger.info("calling CLI") - # tgen.cli() +def test_get_version(tgen): + "Test the logs the FRR version" + + r1 = tgen.gears["r1"] + version = r1.vtysh_cmd("show version") + logger.info("FRR version is: " + version) + + +def test_connectivity(tgen): + "Test the logs the FRR version" + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + output = r1.cmd_raises("ping -c1 192.168.1.2") + output = r2.cmd_raises("ping -c1 192.168.3.1") + + +@pytest.mark.xfail +def test_expect_failure(tgen): + "A test that is current expected to fail but should be fixed" + + assert False, "Example of temporary expected failure that will eventually be fixed" + + +@pytest.mark.skip +def test_will_be_skipped(tgen): + "A test that will be skipped" + assert False # Memory leak test template -def test_memory_leak(): +def test_memory_leak(tgen): "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") diff --git a/tests/topotests/example_test/test_template_json.json b/tests/topotests/example_test/test_template_json.json new file mode 100644 index 0000000000..1ed4a9df6f --- /dev/null +++ b/tests/topotests/example_test/test_template_json.json @@ -0,0 +1,188 @@ + +{ + "address_types": ["ipv4","ipv6"], + "ipv4base":"10.0.0.0", + "ipv4mask":30, + "ipv6base":"fd00::", + "ipv6mask":64, + "link_ip_start":{"ipv4":"10.0.0.0", "v4mask":30, "ipv6":"fd00::", "v6mask":64}, + "lo_prefix":{"ipv4":"1.0.", "v4mask":32, "ipv6":"2001:DB8:F::", "v6mask":128}, + "routers":{ + "r1":{ + "links":{ + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2":{"ipv4":"auto", "ipv6":"auto"}, + "r3":{"ipv4":"auto", "ipv6":"auto"} + }, + "bgp":{ + "local_as":"100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {} + } + }, + "r3": { + "dest_link": { + "r1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {} + } + }, + "r3": { + "dest_link": { + "r1": {} + } + } + } + } + } + } + } + }, + "r2":{ + "links":{ + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1":{"ipv4":"auto", "ipv6":"auto"}, + "r3":{"ipv4":"auto", "ipv6":"auto"} + }, + "bgp":{ + "local_as":"100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r3": { + "dest_link": { + "r2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r3": { + "dest_link": { + "r2": {} + } + } + } + } + } + } + } + }, + "r3":{ + "links":{ + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1":{"ipv4":"auto", "ipv6":"auto"}, + "r2":{"ipv4":"auto", "ipv6":"auto"}, + "r4":{"ipv4":"auto", "ipv6":"auto"} + }, + "bgp":{ + "local_as":"100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {} + } + }, + "r2": { + "dest_link": { + "r3": {} + } + }, + "r4": { + "dest_link": { + "r3": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {} + } + }, + "r2": { + "dest_link": { + "r3": {} + } + }, + "r4": { + "dest_link": { + "r3": {} + } + } + } + } + } + } + } + }, + "r4":{ + "links":{ + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r3":{"ipv4":"auto", "ipv6":"auto"} + }, + "bgp":{ + "local_as":"200", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": {} + } + } + } + } + } + } + } + } + } +} diff --git a/tests/topotests/example_test/test_template_json.py b/tests/topotests/example_test/test_template_json.py new file mode 100644 index 0000000000..42e8bc6e7a --- /dev/null +++ b/tests/topotests/example_test/test_template_json.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python3 +# +# September 5 2021, Christian Hopps <chopps@labn.net> +# +# Copyright (c) 2021, LabN Consulting, L.L.C. +# Copyright (c) 2017 by +# Network Device Education Foundation, Inc. ("NetDEF") +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +<template>.py: Test <template>. +""" + +import pytest + +# Import topogen and topotest helpers +from lib import bgp +from lib import fixtures + + +# TODO: select markers based on daemons used during test +pytestmark = [ + pytest.mark.bgpd, + # pytest.mark.ospfd, + # pytest.mark.ospf6d + # ... +] + +# Use tgen_json fixture (invoked by use test arg of same name) to +# setup/teardown standard JSON topotest +tgen = pytest.fixture(fixtures.tgen_json, scope="module") + + +# tgen is defined above +# topo is a fixture defined in ../conftest.py +def test_bgp_convergence(tgen, topo): + "Test for BGP convergence." + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + bgp_convergence = bgp.verify_bgp_convergence(tgen, topo) + assert bgp_convergence + + +# Memory leak test template +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() diff --git a/tests/topotests/lib/bgp.py b/tests/topotests/lib/bgp.py index d05332388e..556240bfb5 100644 --- a/tests/topotests/lib/bgp.py +++ b/tests/topotests/lib/bgp.py @@ -2672,7 +2672,7 @@ def verify_best_path_as_per_admin_distance( return True -@retry(retry_timeout=10, initial_wait=2) +@retry(retry_timeout=30) def verify_bgp_rib( tgen, addr_type, diff --git a/tests/topotests/lib/common_config.py b/tests/topotests/lib/common_config.py index bfb6cd1ef2..1bce3c6bb2 100644 --- a/tests/topotests/lib/common_config.py +++ b/tests/topotests/lib/common_config.py @@ -44,6 +44,7 @@ from lib.micronet import comm_error from lib.topogen import TopoRouter, get_topogen from lib.topolog import get_logger, logger from lib.topotest import frr_unicode, interface_set_status, version_cmp +from lib import topotest FRRCFG_FILE = "frr_json.conf" FRRCFG_BKUP_FILE = "frr_json_initial.conf" @@ -2971,6 +2972,8 @@ def addKernelRoute( ip, mask = grp_addr.split("/") if mask == "32" or mask == "128": grp_addr = ip + else: + mask = "32" if addr_type == "ipv4" else "128" if not re_search(r"{}".format(grp_addr), result) and mask != "0": errormsg = ( diff --git a/tests/topotests/lib/micronet.py b/tests/topotests/lib/micronet.py index 0416c53c48..8567bd3b4b 100644 --- a/tests/topotests/lib/micronet.py +++ b/tests/topotests/lib/micronet.py @@ -369,13 +369,13 @@ class Commander(object): # pylint: disable=R0205 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) + if title: + cmd.append("-T") + cmd.append(title) cmd.append("-e") cmd.append(sudo_path) cmd.extend(self.pre_cmd) - cmd.append(user_cmd) + cmd.extend(["bash", "-c", user_cmd]) # if channel: # return self.cmd_raises(cmd, skip_pre_cmd=True) # else: @@ -384,13 +384,11 @@ class Commander(object): # pylint: disable=R0205 skip_pre_cmd=True, stdin=None, shell=False, - # stdout=open("/dev/null", "w"), - # stderr=open("/dev/null", "w"), ) time_mod.sleep(2) if p.poll() is not None: self.logger.error("%s: Failed to launch xterm: %s", self, comm_error(p)) - return "" + return p else: self.logger.error( "DISPLAY, STY, and TMUX not in environment, can't open window" diff --git a/tests/topotests/lib/micronet_compat.py b/tests/topotests/lib/micronet_compat.py index 31a76aca55..a3d3f4c685 100644 --- a/tests/topotests/lib/micronet_compat.py +++ b/tests/topotests/lib/micronet_compat.py @@ -33,16 +33,22 @@ def get_pids_with_env(has_var, has_val=None): result = {} for pidenv in glob.iglob("/proc/*/environ"): pid = pidenv.split("/")[2] - 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 + 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 diff --git a/tests/topotests/lib/topotest.py b/tests/topotests/lib/topotest.py index b6f55664a6..b98698185c 100644 --- a/tests/topotests/lib/topotest.py +++ b/tests/topotests/lib/topotest.py @@ -439,6 +439,19 @@ def run_and_expect_type(func, etype, count=20, wait=3, avalue=None): return (False, result) +def router_json_cmp_retry(router, cmd, data, exact=False, retry_timeout=10.0): + """ + Runs `cmd` that returns JSON data (normally the command ends with 'json') + and compare with `data` contents. Retry by default for 10 seconds + """ + + def test_func(): + return router_json_cmp(router, cmd, data, exact) + + ok, _ = run_and_expect(test_func, None, int(retry_timeout), 1) + return ok + + def int2dpid(dpid): "Converting Integer to DPID" @@ -1603,10 +1616,6 @@ class Router(Node): if "all" in shell_routers or self.name in shell_routers: self.run_in_window(os.getenv("SHELL", "bash")) - vtysh_routers = g_extra_config["vtysh"] - if "all" in vtysh_routers or self.name in vtysh_routers: - self.run_in_window("vtysh") - if self.daemons["eigrpd"] == 1: eigrpd_path = os.path.join(self.daemondir, "eigrpd") if not os.path.isfile(eigrpd_path): @@ -1619,7 +1628,13 @@ class Router(Node): logger.info("BFD Test, but no bfdd compiled or installed") return "BFD Test, but no bfdd compiled or installed" - return self.startRouterDaemons(tgen=tgen) + status = self.startRouterDaemons(tgen=tgen) + + vtysh_routers = g_extra_config["vtysh"] + if "all" in vtysh_routers or self.name in vtysh_routers: + self.run_in_window("vtysh") + + return status def getStdErr(self, daemon): return self.getLog("err", daemon) diff --git a/tests/topotests/multicast_pim_bsm_topo1/test_mcast_pim_bsmp_01.py b/tests/topotests/multicast_pim_bsm_topo1/test_mcast_pim_bsmp_01.py index f1b13cbd02..a94dcb505a 100644 --- a/tests/topotests/multicast_pim_bsm_topo1/test_mcast_pim_bsmp_01.py +++ b/tests/topotests/multicast_pim_bsm_topo1/test_mcast_pim_bsmp_01.py @@ -306,12 +306,6 @@ def pre_config_to_bsm(tgen, topo, tc_name, bsr, sender, receiver, fhr, rp, lhr, result = create_static_routes(tgen, input_dict) assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) - # Add kernal route for source - group = topo["routers"][bsr]["bsm"]["bsr_packets"][packet]["pkt_dst"] - bsr_interface = topo["routers"][bsr]["links"][fhr]["interface"] - result = addKernelRoute(tgen, bsr, bsr_interface, group) - assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) - # RP Mapping rp_mapping = topo["routers"][bsr]["bsm"]["bsr_packets"][packet]["rp_mapping"] @@ -325,16 +319,6 @@ def pre_config_to_bsm(tgen, topo, tc_name, bsr, sender, receiver, fhr, rp, lhr, if int(mask) == 32: group = group.split("/")[0] - # Add kernal routes for sender - s_interface = topo["routers"][sender]["links"][fhr]["interface"] - result = addKernelRoute(tgen, sender, s_interface, group) - assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) - - # Add kernal routes for receiver - r_interface = topo["routers"][receiver]["links"][lhr]["interface"] - result = addKernelRoute(tgen, receiver, r_interface, group) - assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) - # Add static routes for RPs in FHR and LHR next_hop_fhr = topo["routers"][rp]["links"][fhr]["ipv4"].split("/")[0] next_hop_lhr = topo["routers"][rp]["links"][lhr]["ipv4"].split("/")[0] diff --git a/tests/topotests/multicast_pim_sm_topo1/test_multicast_pim_sm_topo1.py b/tests/topotests/multicast_pim_sm_topo1/test_multicast_pim_sm_topo1.py index 3e14ab7164..dc14bc6468 100755 --- a/tests/topotests/multicast_pim_sm_topo1/test_multicast_pim_sm_topo1.py +++ b/tests/topotests/multicast_pim_sm_topo1/test_multicast_pim_sm_topo1.py @@ -90,7 +90,6 @@ from lib.pim import ( clear_ip_mroute, clear_ip_pim_interface_traffic, verify_igmp_config, - clear_ip_mroute_verify, McastTesterHelper, ) from lib.topolog import logger @@ -549,17 +548,11 @@ def test_clear_pim_neighbors_and_mroute_p0(request): result = app_helper.run_traffic("i2", IGMP_JOIN_RANGE_1, "f1") assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) - step("Clear the mroute on l1, wait for 5 sec") - result = clear_ip_mroute_verify(tgen, "l1") - assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) - step( - "After clear ip mroute (*,g) entries are re-populated again" - " with same OIL and IIF, verify using 'show ip mroute' and " - " 'show ip pim upstream' " + "Verify clear ip mroute (*,g) entries are populated by using " + "'show ip mroute' cli" ) - source = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0] input_dict = [ {"dut": "l1", "src_address": "*", "iif": "l1-c1-eth0", "oil": "l1-i1-eth1"} ] @@ -570,6 +563,21 @@ def test_clear_pim_neighbors_and_mroute_p0(request): ) assert result is True, "Testcase{} : Failed Error: {}".format(tc_name, result) + step("Clear mroutes on l1") + clear_ip_mroute(tgen, "l1") + + step( + "After clear ip mroute (*,g) entries are re-populated again" + " with same OIL and IIF, verify using 'show ip mroute' and " + " 'show ip pim upstream' " + ) + + for data in input_dict: + result = verify_ip_mroutes( + tgen, data["dut"], data["src_address"], IGMP_JOIN, data["iif"], data["oil"] + ) + assert result is True, "Testcase{} : Failed Error: {}".format(tc_name, result) + step( "Verify 'show ip pim upstream' showing correct OIL and IIF" " on all the nodes" ) diff --git a/tests/topotests/zebra_netlink/r1/sharpd.conf b/tests/topotests/ospf6_gr_topo1/__init__.py index e69de29bb2..e69de29bb2 100644 --- a/tests/topotests/zebra_netlink/r1/sharpd.conf +++ b/tests/topotests/ospf6_gr_topo1/__init__.py diff --git a/tests/topotests/ospf6_gr_topo1/rt1/ospf6d.conf b/tests/topotests/ospf6_gr_topo1/rt1/ospf6d.conf new file mode 100644 index 0000000000..1ee1189766 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt1/ospf6d.conf @@ -0,0 +1,30 @@ +:assword 1 +hostname rt1 +log file ospf6d.log +log commands +! +debug ospf6 lsa router originate +debug ospf6 lsa router flooding +debug ospf6 zebra +debug ospf6 interface +debug ospf6 neighbor +debug ospf6 flooding +debug ospf6 graceful-restart +debug ospf6 spf process +! +interface lo + ipv6 ospf area 1 + ipv6 ospf network point-to-point +! +interface eth-rt2 + ipv6 ospf network point-to-point + ipv6 ospf area 1 + ipv6 ospf hello-interval 3 + ipv6 ospf dead-interval 9 +! +router ospf6 + ospf6 router-id 1.1.1.1 + redistribute connected + graceful-restart grace-period 120 + graceful-restart helper-only +! diff --git a/tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_ospf_database.json b/tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_ospf_database.json new file mode 100644 index 0000000000..58fc114a44 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_ospf_database.json @@ -0,0 +1,95 @@ +{ + "areaScopedLinkStateDb":[ + { + "areaId":"1", + "lsa":[ + { + "type":"Rtr", + "advRouter":"1.1.1.1" + }, + { + "type":"Rtr", + "advRouter":"2.2.2.2" + }, + { + "type":"IAP", + "advRouter":"2.2.2.2", + "payload":"2001:db8:1000::2\/128" + }, + { + "type":"IAP", + "advRouter":"2.2.2.2", + "payload":"2001:db8:1000::3\/128" + }, + { + "type":"IAP", + "advRouter":"2.2.2.2", + "payload":"2001:db8:1000::4\/128" + }, + { + "type":"IAP", + "advRouter":"2.2.2.2", + "payload":"2001:db8:1000::6\/128" + }, + { + "type":"IAP", + "advRouter":"2.2.2.2", + "payload":"2001:db8:1000::5\/128" + }, + { + "type":"IAP", + "advRouter":"2.2.2.2", + "payload":"2001:db8:1000::7\/128" + }, + { + "type":"IAR", + "advRouter":"2.2.2.2", + "payload":"7.7.7.7" + }, + { + "type":"INP", + "advRouter":"1.1.1.1", + "payload":"2001:db8:1000::1\/128" + } + ] + } + ], + "interfaceScopedLinkStateDb":[ + { + "areaId":"1", + "interface":"eth-rt2", + "lsa":[ + { + "type":"Lnk", + "advRouter":"1.1.1.1" + }, + { + "type":"Lnk", + "advRouter":"2.2.2.2" + } + ] + }, + { + "areaId":"1", + "interface":"lo", + "lsa":[ + ] + } + ], + "asScopedLinkStateDb":[ + { + "lsa":[ + { + "type":"ASE", + "advRouter":"1.1.1.1", + "payload":"2001:db8:1000::1\/128" + }, + { + "type":"ASE", + "advRouter":"7.7.7.7", + "payload":"2001:db8:1000::7\/128" + } + ] + } + ] +} diff --git a/tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_ospf_neighbor.json b/tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_ospf_neighbor.json new file mode 100644 index 0000000000..cb88358639 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_ospf_neighbor.json @@ -0,0 +1,12 @@ +{ + "neighbors":[ + { + "neighborId":"2.2.2.2", + "priority":1, + "state":"Full", + "ifState":"PointToPoint", + "interfaceName":"eth-rt2", + "interfaceState":"PointToPoint" + } + ] +} diff --git a/tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_ospf_route.json b/tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_ospf_route.json new file mode 100644 index 0000000000..0c69310eb4 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_ospf_route.json @@ -0,0 +1,74 @@ +{ + "routes":{ + "2001:db8:1000::1\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IA", + "nextHops":[ + { + "interfaceName":"lo" + } + ] + }, + "2001:db8:1000::2\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IE", + "nextHops":[ + { + "interfaceName":"eth-rt2" + } + ] + }, + "2001:db8:1000::3\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IE", + "nextHops":[ + { + "interfaceName":"eth-rt2" + } + ] + }, + "2001:db8:1000::4\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IE", + "nextHops":[ + { + "interfaceName":"eth-rt2" + } + ] + }, + "2001:db8:1000::5\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IE", + "nextHops":[ + { + "interfaceName":"eth-rt2" + } + ] + }, + "2001:db8:1000::6\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IE", + "nextHops":[ + { + "interfaceName":"eth-rt2" + } + ] + }, + "2001:db8:1000::7\/128":{ + "isBestRoute":false, + "destinationType":"N", + "pathType":"E2", + "nextHops":[ + { + "interfaceName":"eth-rt2" + } + ] + } + } +} diff --git a/tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_route.json b/tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_route.json new file mode 100644 index 0000000000..66ee57ce84 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_route.json @@ -0,0 +1,139 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "distance":110, + "metric":10, + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"lo", + "active":true + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":20, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":30, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":40, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":50, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":40, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "2001:db8:1000::7\/128":[ + { + "prefix":"2001:db8:1000::7\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":50, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/ospf6_gr_topo1/rt1/zebra.conf b/tests/topotests/ospf6_gr_topo1/rt1/zebra.conf new file mode 100644 index 0000000000..f29f5b73fb --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt1/zebra.conf @@ -0,0 +1,22 @@ +password 1 +hostname rt1 +log file zebra.log +log commands +! +debug zebra event +debug zebra packet +debug zebra rib +debug zebra kernel +! +interface lo + ip address 1.1.1.1/32 + ipv6 address 2001:db8:1000::1/128 +! +interface stub1 +! +interface eth-rt2 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ospf6_gr_topo1/rt2/ospf6d.conf b/tests/topotests/ospf6_gr_topo1/rt2/ospf6d.conf new file mode 100644 index 0000000000..6cd8d1a8e3 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt2/ospf6d.conf @@ -0,0 +1,35 @@ +password 1 +hostname rt2 +log file ospf6d.log +log commands +! +debug ospf6 lsa router originate +debug ospf6 lsa router flooding +debug ospf6 zebra +debug ospf6 interface +debug ospf6 neighbor +debug ospf6 flooding +debug ospf6 graceful-restart +debug ospf6 spf process +! +interface lo + ipv6 ospf area 0 + ipv6 ospf network point-to-point +! +interface eth-rt1 + ipv6 ospf network point-to-point + ipv6 ospf area 1 + ipv6 ospf hello-interval 3 + ipv6 ospf dead-interval 9 +! +interface eth-rt3 + ipv6 ospf network point-to-point + ipv6 ospf area 0 + ipv6 ospf hello-interval 3 + ipv6 ospf dead-interval 9 +! +router ospf6 + ospf6 router-id 2.2.2.2 + graceful-restart grace-period 120 + graceful-restart helper-only +! diff --git a/tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_ospf_database.json b/tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_ospf_database.json new file mode 100644 index 0000000000..fb16326196 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_ospf_database.json @@ -0,0 +1,183 @@ +{ + "areaScopedLinkStateDb":[ + { + "areaId":"0", + "lsa":[ + { + "type":"Rtr", + "advRouter":"2.2.2.2" + }, + { + "type":"Rtr", + "advRouter":"3.3.3.3" + }, + { + "type":"Rtr", + "advRouter":"3.3.3.3" + }, + { + "type":"Rtr", + "advRouter":"3.3.3.3" + }, + { + "type":"Rtr", + "advRouter":"4.4.4.4" + }, + { + "type":"Rtr", + "advRouter":"6.6.6.6" + }, + { + "type":"IAP", + "advRouter":"2.2.2.2", + "payload":"2001:db8:1000::1\/128" + }, + { + "type":"IAP", + "advRouter":"4.4.4.4", + "payload":"2001:db8:1000::5\/128" + }, + { + "type":"IAP", + "advRouter":"6.6.6.6", + "payload":"2001:db8:1000::7\/128" + }, + { + "type":"IAR", + "advRouter":"2.2.2.2", + "payload":"1.1.1.1" + }, + { + "type":"IAR", + "advRouter":"6.6.6.6", + "payload":"7.7.7.7" + }, + { + "type":"INP", + "advRouter":"2.2.2.2", + "payload":"2001:db8:1000::2\/128" + }, + { + "type":"INP", + "advRouter":"3.3.3.3", + "payload":"2001:db8:1000::3\/128" + }, + { + "type":"INP", + "advRouter":"4.4.4.4", + "payload":"2001:db8:1000::4\/128" + }, + { + "type":"INP", + "advRouter":"6.6.6.6", + "payload":"2001:db8:1000::6\/128" + } + ] + }, + { + "areaId":"1", + "lsa":[ + { + "type":"Rtr", + "advRouter":"1.1.1.1" + }, + { + "type":"Rtr", + "advRouter":"2.2.2.2" + }, + { + "type":"IAP", + "advRouter":"2.2.2.2", + "payload":"2001:db8:1000::2\/128" + }, + { + "type":"IAP", + "advRouter":"2.2.2.2", + "payload":"2001:db8:1000::3\/128" + }, + { + "type":"IAP", + "advRouter":"2.2.2.2", + "payload":"2001:db8:1000::4\/128" + }, + { + "type":"IAP", + "advRouter":"2.2.2.2", + "payload":"2001:db8:1000::6\/128" + }, + { + "type":"IAP", + "advRouter":"2.2.2.2", + "payload":"2001:db8:1000::5\/128" + }, + { + "type":"IAP", + "advRouter":"2.2.2.2", + "payload":"2001:db8:1000::7\/128" + }, + { + "type":"IAR", + "advRouter":"2.2.2.2", + "payload":"7.7.7.7" + }, + { + "type":"INP", + "advRouter":"1.1.1.1", + "payload":"2001:db8:1000::1\/128" + } + ] + } + ], + "interfaceScopedLinkStateDb":[ + { + "areaId":"0", + "interface":"eth-rt3", + "lsa":[ + { + "type":"Lnk", + "advRouter":"2.2.2.2" + }, + { + "type":"Lnk", + "advRouter":"3.3.3.3" + } + ] + }, + { + "areaId":"0", + "interface":"lo", + "lsa":[ + ] + }, + { + "areaId":"1", + "interface":"eth-rt1", + "lsa":[ + { + "type":"Lnk", + "advRouter":"1.1.1.1" + }, + { + "type":"Lnk", + "advRouter":"2.2.2.2" + } + ] + } + ], + "asScopedLinkStateDb":[ + { + "lsa":[ + { + "type":"ASE", + "advRouter":"1.1.1.1", + "payload":"2001:db8:1000::1\/128" + }, + { + "type":"ASE", + "advRouter":"7.7.7.7", + "payload":"2001:db8:1000::7\/128" + } + ] + } + ] +} diff --git a/tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_ospf_neighbor.json b/tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_ospf_neighbor.json new file mode 100644 index 0000000000..e4f27bf37f --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_ospf_neighbor.json @@ -0,0 +1,20 @@ +{ + "neighbors":[ + { + "neighborId":"3.3.3.3", + "priority":1, + "state":"Full", + "ifState":"PointToPoint", + "interfaceName":"eth-rt3", + "interfaceState":"PointToPoint" + }, + { + "neighborId":"1.1.1.1", + "priority":1, + "state":"Full", + "ifState":"PointToPoint", + "interfaceName":"eth-rt1", + "interfaceState":"PointToPoint" + } + ] +} diff --git a/tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_ospf_route.json b/tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_ospf_route.json new file mode 100644 index 0000000000..34013a19de --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_ospf_route.json @@ -0,0 +1,74 @@ +{ + "routes":{ + "2001:db8:1000::1\/128":{ + "isBestRoute":false, + "destinationType":"N", + "pathType":"E2", + "nextHops":[ + { + "interfaceName":"eth-rt1" + } + ] + }, + "2001:db8:1000::2\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IA", + "nextHops":[ + { + "interfaceName":"lo" + } + ] + }, + "2001:db8:1000::3\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IA", + "nextHops":[ + { + "interfaceName":"eth-rt3" + } + ] + }, + "2001:db8:1000::4\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IA", + "nextHops":[ + { + "interfaceName":"eth-rt3" + } + ] + }, + "2001:db8:1000::5\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IE", + "nextHops":[ + { + "interfaceName":"eth-rt3" + } + ] + }, + "2001:db8:1000::6\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IA", + "nextHops":[ + { + "interfaceName":"eth-rt3" + } + ] + }, + "2001:db8:1000::7\/128":{ + "isBestRoute":false, + "destinationType":"N", + "pathType":"E2", + "nextHops":[ + { + "interfaceName":"eth-rt3" + } + ] + } + } +} diff --git a/tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_route.json b/tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_route.json new file mode 100644 index 0000000000..624ff709e3 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_route.json @@ -0,0 +1,139 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":20, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt1", + "active":true + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "distance":110, + "metric":10, + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"lo", + "active":true + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":20, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":30, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":40, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":30, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "2001:db8:1000::7\/128":[ + { + "prefix":"2001:db8:1000::7\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":40, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/ospf6_gr_topo1/rt2/zebra.conf b/tests/topotests/ospf6_gr_topo1/rt2/zebra.conf new file mode 100644 index 0000000000..e4fe7620da --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt2/zebra.conf @@ -0,0 +1,22 @@ +password 1 +hostname rt2 +log file zebra.log +log commands +! +debug zebra event +debug zebra packet +debug zebra rib +debug zebra kernel +! +interface lo + ip address 2.2.2.2/32 + ipv6 address 2001:db8:1000::2/128 +! +interface eth-rt1 +! +interface eth-rt3 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ospf6_gr_topo1/rt3/ospf6d.conf b/tests/topotests/ospf6_gr_topo1/rt3/ospf6d.conf new file mode 100644 index 0000000000..6a63d8f788 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt3/ospf6d.conf @@ -0,0 +1,41 @@ +password 1 +hostname rt3 +log file ospf6d.log +log commands +! +debug ospf6 lsa router originate +debug ospf6 lsa router flooding +debug ospf6 zebra +debug ospf6 interface +debug ospf6 neighbor +debug ospf6 flooding +debug ospf6 graceful-restart +debug ospf6 spf process +! +interface lo + ipv6 ospf area 0 + ipv6 ospf network point-to-point +! +interface eth-rt2 + ipv6 ospf network point-to-point + ipv6 ospf area 0 + ipv6 ospf hello-interval 3 + ipv6 ospf dead-interval 9 +! +interface eth-rt4 + ipv6 ospf network point-to-point + ipv6 ospf area 0 + ipv6 ospf hello-interval 3 + ipv6 ospf dead-interval 9 +! +interface eth-rt6 + ipv6 ospf network point-to-point + ipv6 ospf area 0 + ipv6 ospf hello-interval 3 + ipv6 ospf dead-interval 9 +! +router ospf6 + ospf6 router-id 3.3.3.3 + graceful-restart grace-period 120 + graceful-restart helper-only +! diff --git a/tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_ospf_database.json b/tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_ospf_database.json new file mode 100644 index 0000000000..f8a8f76093 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_ospf_database.json @@ -0,0 +1,144 @@ +{ + "areaScopedLinkStateDb":[ + { + "areaId":"0", + "lsa":[ + { + "type":"Rtr", + "advRouter":"2.2.2.2" + }, + { + "type":"Rtr", + "advRouter":"3.3.3.3" + }, + { + "type":"Rtr", + "advRouter":"3.3.3.3" + }, + { + "type":"Rtr", + "advRouter":"3.3.3.3" + }, + { + "type":"Rtr", + "advRouter":"4.4.4.4" + }, + { + "type":"Rtr", + "advRouter":"6.6.6.6" + }, + { + "type":"IAP", + "advRouter":"2.2.2.2", + "payload":"2001:db8:1000::1\/128" + }, + { + "type":"IAP", + "advRouter":"4.4.4.4", + "payload":"2001:db8:1000::5\/128" + }, + { + "type":"IAP", + "advRouter":"6.6.6.6", + "payload":"2001:db8:1000::7\/128" + }, + { + "type":"IAR", + "advRouter":"2.2.2.2", + "payload":"1.1.1.1" + }, + { + "type":"IAR", + "advRouter":"6.6.6.6", + "payload":"7.7.7.7" + }, + { + "type":"INP", + "advRouter":"2.2.2.2", + "payload":"2001:db8:1000::2\/128" + }, + { + "type":"INP", + "advRouter":"3.3.3.3", + "payload":"2001:db8:1000::3\/128" + }, + { + "type":"INP", + "advRouter":"4.4.4.4", + "payload":"2001:db8:1000::4\/128" + }, + { + "type":"INP", + "advRouter":"6.6.6.6", + "payload":"2001:db8:1000::6\/128" + } + ] + } + ], + "interfaceScopedLinkStateDb":[ + { + "areaId":"0", + "interface":"eth-rt2", + "lsa":[ + { + "type":"Lnk", + "advRouter":"2.2.2.2" + }, + { + "type":"Lnk", + "advRouter":"3.3.3.3" + } + ] + }, + { + "areaId":"0", + "interface":"eth-rt4", + "lsa":[ + { + "type":"Lnk", + "advRouter":"3.3.3.3" + }, + { + "type":"Lnk", + "advRouter":"4.4.4.4" + } + ] + }, + { + "areaId":"0", + "interface":"eth-rt6", + "lsa":[ + { + "type":"Lnk", + "advRouter":"3.3.3.3" + }, + { + "type":"Lnk", + "advRouter":"6.6.6.6" + } + ] + }, + { + "areaId":"0", + "interface":"lo", + "lsa":[ + ] + } + ], + "asScopedLinkStateDb":[ + { + "lsa":[ + { + "type":"ASE", + "advRouter":"1.1.1.1", + "payload":"2001:db8:1000::1\/128" + }, + { + "type":"ASE", + "advRouter":"7.7.7.7", + "payload":"2001:db8:1000::7\/128" + } + ] + } + ] +} diff --git a/tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_ospf_neighbor.json b/tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_ospf_neighbor.json new file mode 100644 index 0000000000..d0d7f45b0e --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_ospf_neighbor.json @@ -0,0 +1,28 @@ +{ + "neighbors":[ + { + "neighborId":"2.2.2.2", + "priority":1, + "state":"Full", + "ifState":"PointToPoint", + "interfaceName":"eth-rt2", + "interfaceState":"PointToPoint" + }, + { + "neighborId":"4.4.4.4", + "priority":1, + "state":"Full", + "ifState":"PointToPoint", + "interfaceName":"eth-rt4", + "interfaceState":"PointToPoint" + }, + { + "neighborId":"6.6.6.6", + "priority":1, + "state":"Full", + "ifState":"PointToPoint", + "interfaceName":"eth-rt6", + "interfaceState":"PointToPoint" + } + ] +} diff --git a/tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_ospf_route.json b/tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_ospf_route.json new file mode 100644 index 0000000000..ee516b9d66 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_ospf_route.json @@ -0,0 +1,74 @@ +{ + "routes":{ + "2001:db8:1000::1\/128":{ + "isBestRoute":false, + "destinationType":"N", + "pathType":"E2", + "nextHops":[ + { + "interfaceName":"eth-rt2" + } + ] + }, + "2001:db8:1000::2\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IA", + "nextHops":[ + { + "interfaceName":"eth-rt2" + } + ] + }, + "2001:db8:1000::3\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IA", + "nextHops":[ + { + "interfaceName":"lo" + } + ] + }, + "2001:db8:1000::4\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IA", + "nextHops":[ + { + "interfaceName":"eth-rt4" + } + ] + }, + "2001:db8:1000::5\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IE", + "nextHops":[ + { + "interfaceName":"eth-rt4" + } + ] + }, + "2001:db8:1000::6\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IA", + "nextHops":[ + { + "interfaceName":"eth-rt6" + } + ] + }, + "2001:db8:1000::7\/128":{ + "isBestRoute":false, + "destinationType":"N", + "pathType":"E2", + "nextHops":[ + { + "interfaceName":"eth-rt6" + } + ] + } + } +} diff --git a/tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_route.json b/tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_route.json new file mode 100644 index 0000000000..f9b43dcdb9 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_route.json @@ -0,0 +1,139 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":30, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":20, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "distance":110, + "metric":10, + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"lo", + "active":true + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":20, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":30, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":20, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "2001:db8:1000::7\/128":[ + { + "prefix":"2001:db8:1000::7\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":30, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/ospf6_gr_topo1/rt3/zebra.conf b/tests/topotests/ospf6_gr_topo1/rt3/zebra.conf new file mode 100644 index 0000000000..3a9de21d30 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt3/zebra.conf @@ -0,0 +1,24 @@ +password 1 +hostname rt3 +log file zebra.log +log commands +! +debug zebra event +debug zebra packet +debug zebra rib +debug zebra kernel +! +interface lo + ip address 3.3.3.3/32 + ipv6 address 2001:db8:1000::3/128 +! +interface eth-rt2 +! +interface eth-rt4 +! +interface eth-rt6 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ospf6_gr_topo1/rt4/ospf6d.conf b/tests/topotests/ospf6_gr_topo1/rt4/ospf6d.conf new file mode 100644 index 0000000000..dff33d4094 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt4/ospf6d.conf @@ -0,0 +1,35 @@ +password 1 +hostname rt4 +log file ospf6d.log +log commands +! +debug ospf6 lsa router originate +debug ospf6 lsa router flooding +debug ospf6 zebra +debug ospf6 interface +debug ospf6 neighbor +debug ospf6 flooding +debug ospf6 graceful-restart +debug ospf6 spf process +! +interface lo + ipv6 ospf area 0 + ipv6 ospf network point-to-point +! +interface eth-rt3 + ipv6 ospf network point-to-point + ipv6 ospf area 0 + ipv6 ospf hello-interval 3 + ipv6 ospf dead-interval 9 +! +interface eth-rt5 + ipv6 ospf network point-to-point + ipv6 ospf area 2 + ipv6 ospf hello-interval 3 + ipv6 ospf dead-interval 9 +! +router ospf6 + ospf6 router-id 4.4.4.4 + graceful-restart grace-period 120 + graceful-restart helper-only +! diff --git a/tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_ospf_database.json b/tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_ospf_database.json new file mode 100644 index 0000000000..0954d1b8eb --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_ospf_database.json @@ -0,0 +1,188 @@ +{ + "areaScopedLinkStateDb":[ + { + "areaId":"0", + "lsa":[ + { + "type":"Rtr", + "advRouter":"2.2.2.2" + }, + { + "type":"Rtr", + "advRouter":"3.3.3.3" + }, + { + "type":"Rtr", + "advRouter":"3.3.3.3" + }, + { + "type":"Rtr", + "advRouter":"3.3.3.3" + }, + { + "type":"Rtr", + "advRouter":"4.4.4.4" + }, + { + "type":"Rtr", + "advRouter":"6.6.6.6" + }, + { + "type":"IAP", + "advRouter":"2.2.2.2", + "payload":"2001:db8:1000::1\/128" + }, + { + "type":"IAP", + "advRouter":"4.4.4.4", + "payload":"2001:db8:1000::5\/128" + }, + { + "type":"IAP", + "advRouter":"6.6.6.6", + "payload":"2001:db8:1000::7\/128" + }, + { + "type":"IAR", + "advRouter":"2.2.2.2", + "payload":"1.1.1.1" + }, + { + "type":"IAR", + "advRouter":"6.6.6.6", + "payload":"7.7.7.7" + }, + { + "type":"INP", + "advRouter":"2.2.2.2", + "payload":"2001:db8:1000::2\/128" + }, + { + "type":"INP", + "advRouter":"3.3.3.3", + "payload":"2001:db8:1000::3\/128" + }, + { + "type":"INP", + "advRouter":"4.4.4.4", + "payload":"2001:db8:1000::4\/128" + }, + { + "type":"INP", + "advRouter":"6.6.6.6", + "payload":"2001:db8:1000::6\/128" + } + ] + }, + { + "areaId":"2", + "lsa":[ + { + "type":"Rtr", + "advRouter":"4.4.4.4" + }, + { + "type":"Rtr", + "advRouter":"5.5.5.5" + }, + { + "type":"IAP", + "advRouter":"4.4.4.4", + "payload":"2001:db8:1000::4\/128" + }, + { + "type":"IAP", + "advRouter":"4.4.4.4", + "payload":"2001:db8:1000::2\/128" + }, + { + "type":"IAP", + "advRouter":"4.4.4.4", + "payload":"2001:db8:1000::3\/128" + }, + { + "type":"IAP", + "advRouter":"4.4.4.4", + "payload":"2001:db8:1000::6\/128" + }, + { + "type":"IAP", + "advRouter":"4.4.4.4", + "payload":"2001:db8:1000::1\/128" + }, + { + "type":"IAP", + "advRouter":"4.4.4.4", + "payload":"2001:db8:1000::7\/128" + }, + { + "type":"IAR", + "advRouter":"4.4.4.4", + "payload":"1.1.1.1" + }, + { + "type":"IAR", + "advRouter":"4.4.4.4", + "payload":"7.7.7.7" + }, + { + "type":"INP", + "advRouter":"5.5.5.5", + "payload":"2001:db8:1000::5\/128" + } + ] + } + ], + "interfaceScopedLinkStateDb":[ + { + "areaId":"0", + "interface":"eth-rt3", + "lsa":[ + { + "type":"Lnk", + "advRouter":"3.3.3.3" + }, + { + "type":"Lnk", + "advRouter":"4.4.4.4" + } + ] + }, + { + "areaId":"0", + "interface":"lo", + "lsa":[ + ] + }, + { + "areaId":"2", + "interface":"eth-rt5", + "lsa":[ + { + "type":"Lnk", + "advRouter":"4.4.4.4" + }, + { + "type":"Lnk", + "advRouter":"5.5.5.5" + } + ] + } + ], + "asScopedLinkStateDb":[ + { + "lsa":[ + { + "type":"ASE", + "advRouter":"1.1.1.1", + "payload":"2001:db8:1000::1\/128" + }, + { + "type":"ASE", + "advRouter":"7.7.7.7", + "payload":"2001:db8:1000::7\/128" + } + ] + } + ] +} diff --git a/tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_ospf_neighbor.json b/tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_ospf_neighbor.json new file mode 100644 index 0000000000..36abba4f87 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_ospf_neighbor.json @@ -0,0 +1,20 @@ +{ + "neighbors":[ + { + "neighborId":"3.3.3.3", + "priority":1, + "state":"Full", + "ifState":"PointToPoint", + "interfaceName":"eth-rt3", + "interfaceState":"PointToPoint" + }, + { + "neighborId":"5.5.5.5", + "priority":1, + "state":"Full", + "ifState":"PointToPoint", + "interfaceName":"eth-rt5", + "interfaceState":"PointToPoint" + } + ] +} diff --git a/tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_ospf_route.json b/tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_ospf_route.json new file mode 100644 index 0000000000..3e5f17f491 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_ospf_route.json @@ -0,0 +1,74 @@ +{ + "routes":{ + "2001:db8:1000::1\/128":{ + "isBestRoute":false, + "destinationType":"N", + "pathType":"E2", + "nextHops":[ + { + "interfaceName":"eth-rt3" + } + ] + }, + "2001:db8:1000::2\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IA", + "nextHops":[ + { + "interfaceName":"eth-rt3" + } + ] + }, + "2001:db8:1000::3\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IA", + "nextHops":[ + { + "interfaceName":"eth-rt3" + } + ] + }, + "2001:db8:1000::4\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IA", + "nextHops":[ + { + "interfaceName":"lo" + } + ] + }, + "2001:db8:1000::5\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IA", + "nextHops":[ + { + "interfaceName":"eth-rt5" + } + ] + }, + "2001:db8:1000::6\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IA", + "nextHops":[ + { + "interfaceName":"eth-rt3" + } + ] + }, + "2001:db8:1000::7\/128":{ + "isBestRoute":false, + "destinationType":"N", + "pathType":"E2", + "nextHops":[ + { + "interfaceName":"eth-rt3" + } + ] + } + } +} diff --git a/tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_route.json b/tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_route.json new file mode 100644 index 0000000000..f5212da4f6 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_route.json @@ -0,0 +1,139 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":40, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":30, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":20, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "distance":110, + "metric":10, + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"lo", + "active":true + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":20, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":30, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "2001:db8:1000::7\/128":[ + { + "prefix":"2001:db8:1000::7\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":40, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/ospf6_gr_topo1/rt4/zebra.conf b/tests/topotests/ospf6_gr_topo1/rt4/zebra.conf new file mode 100644 index 0000000000..eeea417b70 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt4/zebra.conf @@ -0,0 +1,22 @@ +password 1 +hostname rt4 +log file zebra.log +log commands +! +debug zebra event +debug zebra packet +debug zebra rib +debug zebra kernel +! +interface lo + ip address 4.4.4.4/32 + ipv6 address 2001:db8:1000::4/128 +! +interface eth-rt3 +! +interface eth-rt5 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ospf6_gr_topo1/rt5/ospf6d.conf b/tests/topotests/ospf6_gr_topo1/rt5/ospf6d.conf new file mode 100644 index 0000000000..49c3a8b86f --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt5/ospf6d.conf @@ -0,0 +1,29 @@ +password 1 +hostname rt5 +log file ospf6d.log +log commands +! +debug ospf6 lsa router originate +debug ospf6 lsa router flooding +debug ospf6 zebra +debug ospf6 interface +debug ospf6 neighbor +debug ospf6 flooding +debug ospf6 graceful-restart +debug ospf6 spf process +! +interface lo + ipv6 ospf area 2 + ipv6 ospf network point-to-point +! +interface eth-rt4 + ipv6 ospf network point-to-point + ipv6 ospf area 2 + ipv6 ospf hello-interval 3 + ipv6 ospf dead-interval 9 +! +router ospf6 + ospf6 router-id 5.5.5.5 + graceful-restart grace-period 120 + graceful-restart helper-only +! diff --git a/tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_ospf_database.json b/tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_ospf_database.json new file mode 100644 index 0000000000..4a163b984e --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_ospf_database.json @@ -0,0 +1,100 @@ +{ + "areaScopedLinkStateDb":[ + { + "areaId":"2", + "lsa":[ + { + "type":"Rtr", + "advRouter":"4.4.4.4" + }, + { + "type":"Rtr", + "advRouter":"5.5.5.5" + }, + { + "type":"IAP", + "advRouter":"4.4.4.4", + "payload":"2001:db8:1000::4\/128" + }, + { + "type":"IAP", + "advRouter":"4.4.4.4", + "payload":"2001:db8:1000::2\/128" + }, + { + "type":"IAP", + "advRouter":"4.4.4.4", + "payload":"2001:db8:1000::3\/128" + }, + { + "type":"IAP", + "advRouter":"4.4.4.4", + "payload":"2001:db8:1000::6\/128" + }, + { + "type":"IAP", + "advRouter":"4.4.4.4", + "payload":"2001:db8:1000::1\/128" + }, + { + "type":"IAP", + "advRouter":"4.4.4.4", + "payload":"2001:db8:1000::7\/128" + }, + { + "type":"IAR", + "advRouter":"4.4.4.4", + "payload":"1.1.1.1" + }, + { + "type":"IAR", + "advRouter":"4.4.4.4", + "payload":"7.7.7.7" + }, + { + "type":"INP", + "advRouter":"5.5.5.5", + "payload":"2001:db8:1000::5\/128" + } + ] + } + ], + "interfaceScopedLinkStateDb":[ + { + "areaId":"2", + "interface":"eth-rt4", + "lsa":[ + { + "type":"Lnk", + "advRouter":"4.4.4.4" + }, + { + "type":"Lnk", + "advRouter":"5.5.5.5" + } + ] + }, + { + "areaId":"2", + "interface":"lo", + "lsa":[ + ] + } + ], + "asScopedLinkStateDb":[ + { + "lsa":[ + { + "type":"ASE", + "advRouter":"1.1.1.1", + "payload":"2001:db8:1000::1\/128" + }, + { + "type":"ASE", + "advRouter":"7.7.7.7", + "payload":"2001:db8:1000::7\/128" + } + ] + } + ] +} diff --git a/tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_ospf_neighbor.json b/tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_ospf_neighbor.json new file mode 100644 index 0000000000..9b6ac911d1 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_ospf_neighbor.json @@ -0,0 +1,12 @@ +{ + "neighbors":[ + { + "neighborId":"4.4.4.4", + "priority":1, + "state":"Full", + "ifState":"PointToPoint", + "interfaceName":"eth-rt4", + "interfaceState":"PointToPoint" + } + ] +} diff --git a/tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_ospf_route.json b/tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_ospf_route.json new file mode 100644 index 0000000000..a56c3262c6 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_ospf_route.json @@ -0,0 +1,74 @@ +{ + "routes":{ + "2001:db8:1000::1\/128":{ + "isBestRoute":false, + "destinationType":"N", + "pathType":"E2", + "nextHops":[ + { + "interfaceName":"eth-rt4" + } + ] + }, + "2001:db8:1000::2\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IE", + "nextHops":[ + { + "interfaceName":"eth-rt4" + } + ] + }, + "2001:db8:1000::3\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IE", + "nextHops":[ + { + "interfaceName":"eth-rt4" + } + ] + }, + "2001:db8:1000::4\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IE", + "nextHops":[ + { + "interfaceName":"eth-rt4" + } + ] + }, + "2001:db8:1000::5\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IA", + "nextHops":[ + { + "interfaceName":"lo" + } + ] + }, + "2001:db8:1000::6\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IE", + "nextHops":[ + { + "interfaceName":"eth-rt4" + } + ] + }, + "2001:db8:1000::7\/128":{ + "isBestRoute":false, + "destinationType":"N", + "pathType":"E2", + "nextHops":[ + { + "interfaceName":"eth-rt4" + } + ] + } + } +} diff --git a/tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_route.json b/tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_route.json new file mode 100644 index 0000000000..5ea4f699fe --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_route.json @@ -0,0 +1,139 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":50, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":40, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":30, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":20, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "distance":110, + "metric":10, + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"lo", + "active":true + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":40, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ], + "2001:db8:1000::7\/128":[ + { + "prefix":"2001:db8:1000::7\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":50, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/ospf6_gr_topo1/rt5/zebra.conf b/tests/topotests/ospf6_gr_topo1/rt5/zebra.conf new file mode 100644 index 0000000000..0cdb90b129 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt5/zebra.conf @@ -0,0 +1,20 @@ +password 1 +hostname rt5 +log file zebra.log +log commands +! +debug zebra event +debug zebra packet +debug zebra rib +debug zebra kernel +! +interface lo + ip address 5.5.5.5/32 + ipv6 address 2001:db8:1000::5/128 +! +interface eth-rt4 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ospf6_gr_topo1/rt6/ospf6d.conf b/tests/topotests/ospf6_gr_topo1/rt6/ospf6d.conf new file mode 100644 index 0000000000..5d6d3280b9 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt6/ospf6d.conf @@ -0,0 +1,35 @@ +password 1 +hostname rt6 +log file ospf6d.log +log commands +! +debug ospf6 lsa router originate +debug ospf6 lsa router flooding +debug ospf6 zebra +debug ospf6 interface +debug ospf6 neighbor +debug ospf6 flooding +debug ospf6 graceful-restart +debug ospf6 spf process +! +interface lo + ipv6 ospf area 0 + ipv6 ospf network point-to-point +! +interface eth-rt3 + ipv6 ospf network point-to-point + ipv6 ospf area 0 + ipv6 ospf hello-interval 3 + ipv6 ospf dead-interval 9 +! +interface eth-rt7 + ipv6 ospf network point-to-point + ipv6 ospf area 3 + ipv6 ospf hello-interval 3 + ipv6 ospf dead-interval 9 +! +router ospf6 + ospf6 router-id 6.6.6.6 + graceful-restart grace-period 120 + graceful-restart helper-only +! diff --git a/tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_ospf_database.json b/tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_ospf_database.json new file mode 100644 index 0000000000..71872d19d0 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_ospf_database.json @@ -0,0 +1,183 @@ +{ + "areaScopedLinkStateDb":[ + { + "areaId":"0", + "lsa":[ + { + "type":"Rtr", + "advRouter":"2.2.2.2" + }, + { + "type":"Rtr", + "advRouter":"3.3.3.3" + }, + { + "type":"Rtr", + "advRouter":"3.3.3.3" + }, + { + "type":"Rtr", + "advRouter":"3.3.3.3" + }, + { + "type":"Rtr", + "advRouter":"4.4.4.4" + }, + { + "type":"Rtr", + "advRouter":"6.6.6.6" + }, + { + "type":"IAP", + "advRouter":"2.2.2.2", + "payload":"2001:db8:1000::1\/128" + }, + { + "type":"IAP", + "advRouter":"4.4.4.4", + "payload":"2001:db8:1000::5\/128" + }, + { + "type":"IAP", + "advRouter":"6.6.6.6", + "payload":"2001:db8:1000::7\/128" + }, + { + "type":"IAR", + "advRouter":"2.2.2.2", + "payload":"1.1.1.1" + }, + { + "type":"IAR", + "advRouter":"6.6.6.6", + "payload":"7.7.7.7" + }, + { + "type":"INP", + "advRouter":"2.2.2.2", + "payload":"2001:db8:1000::2\/128" + }, + { + "type":"INP", + "advRouter":"3.3.3.3", + "payload":"2001:db8:1000::3\/128" + }, + { + "type":"INP", + "advRouter":"4.4.4.4", + "payload":"2001:db8:1000::4\/128" + }, + { + "type":"INP", + "advRouter":"6.6.6.6", + "payload":"2001:db8:1000::6\/128" + } + ] + }, + { + "areaId":"3", + "lsa":[ + { + "type":"Rtr", + "advRouter":"6.6.6.6" + }, + { + "type":"Rtr", + "advRouter":"7.7.7.7" + }, + { + "type":"IAP", + "advRouter":"6.6.6.6", + "payload":"2001:db8:1000::6\/128" + }, + { + "type":"IAP", + "advRouter":"6.6.6.6", + "payload":"2001:db8:1000::2\/128" + }, + { + "type":"IAP", + "advRouter":"6.6.6.6", + "payload":"2001:db8:1000::3\/128" + }, + { + "type":"IAP", + "advRouter":"6.6.6.6", + "payload":"2001:db8:1000::4\/128" + }, + { + "type":"IAP", + "advRouter":"6.6.6.6", + "payload":"2001:db8:1000::1\/128" + }, + { + "type":"IAP", + "advRouter":"6.6.6.6", + "payload":"2001:db8:1000::5\/128" + }, + { + "type":"IAR", + "advRouter":"6.6.6.6", + "payload":"1.1.1.1" + }, + { + "type":"INP", + "advRouter":"7.7.7.7", + "payload":"2001:db8:1000::7\/128" + } + ] + } + ], + "interfaceScopedLinkStateDb":[ + { + "areaId":"0", + "interface":"eth-rt3", + "lsa":[ + { + "type":"Lnk", + "advRouter":"3.3.3.3" + }, + { + "type":"Lnk", + "advRouter":"6.6.6.6" + } + ] + }, + { + "areaId":"0", + "interface":"lo", + "lsa":[ + ] + }, + { + "areaId":"3", + "interface":"eth-rt7", + "lsa":[ + { + "type":"Lnk", + "advRouter":"6.6.6.6" + }, + { + "type":"Lnk", + "advRouter":"7.7.7.7" + } + ] + } + ], + "asScopedLinkStateDb":[ + { + "lsa":[ + { + "type":"ASE", + "advRouter":"1.1.1.1", + "payload":"2001:db8:1000::1\/128" + }, + { + "type":"ASE", + "advRouter":"7.7.7.7", + "payload":"2001:db8:1000::7\/128" + } + ] + } + ] +} diff --git a/tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_ospf_neighbor.json b/tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_ospf_neighbor.json new file mode 100644 index 0000000000..aba181ba3f --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_ospf_neighbor.json @@ -0,0 +1,20 @@ +{ + "neighbors":[ + { + "neighborId":"3.3.3.3", + "priority":1, + "state":"Full", + "ifState":"PointToPoint", + "interfaceName":"eth-rt3", + "interfaceState":"PointToPoint" + }, + { + "neighborId":"7.7.7.7", + "priority":1, + "state":"Full", + "ifState":"PointToPoint", + "interfaceName":"eth-rt7", + "interfaceState":"PointToPoint" + } + ] +} diff --git a/tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_ospf_route.json b/tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_ospf_route.json new file mode 100644 index 0000000000..c9494a9d57 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_ospf_route.json @@ -0,0 +1,74 @@ +{ + "routes":{ + "2001:db8:1000::1\/128":{ + "isBestRoute":false, + "destinationType":"N", + "pathType":"E2", + "nextHops":[ + { + "interfaceName":"eth-rt3" + } + ] + }, + "2001:db8:1000::2\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IA", + "nextHops":[ + { + "interfaceName":"eth-rt3" + } + ] + }, + "2001:db8:1000::3\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IA", + "nextHops":[ + { + "interfaceName":"eth-rt3" + } + ] + }, + "2001:db8:1000::4\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IA", + "nextHops":[ + { + "interfaceName":"eth-rt3" + } + ] + }, + "2001:db8:1000::5\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IE", + "nextHops":[ + { + "interfaceName":"eth-rt3" + } + ] + }, + "2001:db8:1000::6\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IA", + "nextHops":[ + { + "interfaceName":"lo" + } + ] + }, + "2001:db8:1000::7\/128":{ + "isBestRoute":false, + "destinationType":"N", + "pathType":"E2", + "nextHops":[ + { + "interfaceName":"eth-rt7" + } + ] + } + } +} diff --git a/tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_route.json b/tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_route.json new file mode 100644 index 0000000000..862f1baffb --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_route.json @@ -0,0 +1,139 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":40, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":30, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":20, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":30, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":40, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "distance":110, + "metric":10, + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"lo", + "active":true + } + ] + } + ], + "2001:db8:1000::7\/128":[ + { + "prefix":"2001:db8:1000::7\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":20, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt7", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/ospf6_gr_topo1/rt6/zebra.conf b/tests/topotests/ospf6_gr_topo1/rt6/zebra.conf new file mode 100644 index 0000000000..3c2312da8a --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt6/zebra.conf @@ -0,0 +1,22 @@ +password 1 +hostname rt6 +log file zebra.log +log commands +! +debug zebra event +debug zebra packet +debug zebra rib +debug zebra kernel +! +interface lo + ip address 6.6.6.6/32 + ipv6 address 2001:db8:1000::6/128 +! +interface eth-rt3 +! +interface eth-rt7 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ospf6_gr_topo1/rt7/ospf6d.conf b/tests/topotests/ospf6_gr_topo1/rt7/ospf6d.conf new file mode 100644 index 0000000000..f504fba4de --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt7/ospf6d.conf @@ -0,0 +1,30 @@ +password 1 +hostname rt7 +log file ospf6d.log +log commands +! +debug ospf6 lsa router originate +debug ospf6 lsa router flooding +debug ospf6 zebra +debug ospf6 interface +debug ospf6 neighbor +debug ospf6 flooding +debug ospf6 graceful-restart +debug ospf6 spf process +! +interface lo + ipv6 ospf area 3 + ipv6 ospf network point-to-point +! +interface eth-rt6 + ipv6 ospf network point-to-point + ipv6 ospf area 3 + ipv6 ospf hello-interval 3 + ipv6 ospf dead-interval 9 +! +router ospf6 + ospf6 router-id 7.7.7.7 + redistribute connected + graceful-restart grace-period 120 + graceful-restart helper-only +! diff --git a/tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_ospf_database.json b/tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_ospf_database.json new file mode 100644 index 0000000000..e70eb57b29 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_ospf_database.json @@ -0,0 +1,95 @@ +{ + "areaScopedLinkStateDb":[ + { + "areaId":"3", + "lsa":[ + { + "type":"Rtr", + "advRouter":"6.6.6.6" + }, + { + "type":"Rtr", + "advRouter":"7.7.7.7" + }, + { + "type":"IAP", + "advRouter":"6.6.6.6", + "payload":"2001:db8:1000::6\/128" + }, + { + "type":"IAP", + "advRouter":"6.6.6.6", + "payload":"2001:db8:1000::2\/128" + }, + { + "type":"IAP", + "advRouter":"6.6.6.6", + "payload":"2001:db8:1000::3\/128" + }, + { + "type":"IAP", + "advRouter":"6.6.6.6", + "payload":"2001:db8:1000::4\/128" + }, + { + "type":"IAP", + "advRouter":"6.6.6.6", + "payload":"2001:db8:1000::1\/128" + }, + { + "type":"IAP", + "advRouter":"6.6.6.6", + "payload":"2001:db8:1000::5\/128" + }, + { + "type":"IAR", + "advRouter":"6.6.6.6", + "payload":"1.1.1.1" + }, + { + "type":"INP", + "advRouter":"7.7.7.7", + "payload":"2001:db8:1000::7\/128" + } + ] + } + ], + "interfaceScopedLinkStateDb":[ + { + "areaId":"3", + "interface":"eth-rt6", + "lsa":[ + { + "type":"Lnk", + "advRouter":"6.6.6.6" + }, + { + "type":"Lnk", + "advRouter":"7.7.7.7" + } + ] + }, + { + "areaId":"3", + "interface":"lo", + "lsa":[ + ] + } + ], + "asScopedLinkStateDb":[ + { + "lsa":[ + { + "type":"ASE", + "advRouter":"1.1.1.1", + "payload":"2001:db8:1000::1\/128" + }, + { + "type":"ASE", + "advRouter":"7.7.7.7", + "payload":"2001:db8:1000::7\/128" + } + ] + } + ] +} diff --git a/tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_ospf_neighbor.json b/tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_ospf_neighbor.json new file mode 100644 index 0000000000..5548691ef3 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_ospf_neighbor.json @@ -0,0 +1,12 @@ +{ + "neighbors":[ + { + "neighborId":"6.6.6.6", + "priority":1, + "state":"Full", + "ifState":"PointToPoint", + "interfaceName":"eth-rt6", + "interfaceState":"PointToPoint" + } + ] +} diff --git a/tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_ospf_route.json b/tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_ospf_route.json new file mode 100644 index 0000000000..42ca54fded --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_ospf_route.json @@ -0,0 +1,74 @@ +{ + "routes":{ + "2001:db8:1000::1\/128":{ + "isBestRoute":false, + "destinationType":"N", + "pathType":"E2", + "nextHops":[ + { + "interfaceName":"eth-rt6" + } + ] + }, + "2001:db8:1000::2\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IE", + "nextHops":[ + { + "interfaceName":"eth-rt6" + } + ] + }, + "2001:db8:1000::3\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IE", + "nextHops":[ + { + "interfaceName":"eth-rt6" + } + ] + }, + "2001:db8:1000::4\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IE", + "nextHops":[ + { + "interfaceName":"eth-rt6" + } + ] + }, + "2001:db8:1000::5\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IE", + "nextHops":[ + { + "interfaceName":"eth-rt6" + } + ] + }, + "2001:db8:1000::6\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IE", + "nextHops":[ + { + "interfaceName":"eth-rt6" + } + ] + }, + "2001:db8:1000::7\/128":{ + "isBestRoute":true, + "destinationType":"N", + "pathType":"IA", + "nextHops":[ + { + "interfaceName":"lo" + } + ] + } + } +} diff --git a/tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_route.json b/tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_route.json new file mode 100644 index 0000000000..f5f8f710e5 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_route.json @@ -0,0 +1,139 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":50, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":40, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":30, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":40, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":50, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":110, + "metric":20, + "installed":true, + "nexthops":[ + { + "afi":"ipv6", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "2001:db8:1000::7\/128":[ + { + "prefix":"2001:db8:1000::7\/128", + "protocol":"ospf6", + "vrfId":0, + "vrfName":"default", + "distance":110, + "metric":10, + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"lo", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/ospf6_gr_topo1/rt7/zebra.conf b/tests/topotests/ospf6_gr_topo1/rt7/zebra.conf new file mode 100644 index 0000000000..9cc8c29c1e --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/rt7/zebra.conf @@ -0,0 +1,22 @@ +password 1 +hostname rt7 +log file zebra.log +log commands +! +debug zebra event +debug zebra packet +debug zebra rib +debug zebra kernel +! +interface lo + ip address 7.7.7.7/32 + ipv6 address 2001:db8:1000::7/128 +! +interface stub1 +! +interface eth-rt6 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ospf6_gr_topo1/test_ospf6_gr_topo1.py b/tests/topotests/ospf6_gr_topo1/test_ospf6_gr_topo1.py new file mode 100755 index 0000000000..ccbcadb8b1 --- /dev/null +++ b/tests/topotests/ospf6_gr_topo1/test_ospf6_gr_topo1.py @@ -0,0 +1,381 @@ +#!/usr/bin/env python + +# +# test_ospf6_gr_topo1.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2021 by +# Network Device Education Foundation, Inc. ("NetDEF") +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_ospf6_gr_topo1.py: + + +---------+ + | RT1 | + | 1.1.1.1 | + +---------+ + |eth-rt2 + | + |eth-rt1 + +---------+ + | RT2 | + | 2.2.2.2 | + +---------+ + |eth-rt3 + | + |eth-rt2 + +---------+ + | RT3 | + | 3.3.3.3 | + +---------+ + eth-rt4| |eth-rt6 + | | + +---------+ +--------+ + | | + |eth-rt3 |eth-rt3 + +---------+ +---------+ + | RT4 | | RT6 | + | 4.4.4.4 | | 6.6.6.6 | + +---------+ +---------+ + |eth-rt5 |eth-rt7 + | | + |eth-rt4 |eth-rt6 + +---------+ +---------+ + | RT5 | | RT7 | + | 5.5.5.5 | | 7.7.7.7 | + +---------+ +---------+ +""" + +import os +import sys +import pytest +import json +from time import sleep +from functools import partial + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from lib.common_config import ( + kill_router_daemons, + start_router_daemons, +) + +pytestmark = [pytest.mark.ospf6d] + +# Global multi-dimensional dictionary containing all expected outputs +outputs = {} + + +def build_topo(tgen): + # + # Define FRR Routers + # + for router in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6", "rt7"]: + 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["rt1"], nodeif="stub1") + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["rt2"], nodeif="eth-rt3") + switch.add_link(tgen.gears["rt3"], nodeif="eth-rt2") + + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["rt3"], nodeif="eth-rt4") + switch.add_link(tgen.gears["rt4"], nodeif="eth-rt3") + + switch = tgen.add_switch("s5") + switch.add_link(tgen.gears["rt3"], nodeif="eth-rt6") + switch.add_link(tgen.gears["rt6"], nodeif="eth-rt3") + + switch = tgen.add_switch("s6") + switch.add_link(tgen.gears["rt4"], nodeif="eth-rt5") + switch.add_link(tgen.gears["rt5"], nodeif="eth-rt4") + + switch = tgen.add_switch("s7") + switch.add_link(tgen.gears["rt6"], nodeif="eth-rt7") + switch.add_link(tgen.gears["rt7"], nodeif="eth-rt6") + + switch = tgen.add_switch("s8") + switch.add_link(tgen.gears["rt7"], nodeif="stub1") + + +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_OSPF6, os.path.join(CWD, "{}/ospf6d.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +def router_compare_json_output(rname, command, reference, tries): + "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()) + + test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected) + _, diff = topotest.run_and_expect(test_func, None, count=tries, wait=0.5) + assertmsg = '"{}" JSON output mismatches the expected result'.format(rname) + assert diff is None, assertmsg + + +def check_routers(initial_convergence=False, exiting=None, restarting=None): + for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6", "rt7"]: + # Check the RIB first, which should be preserved across restarts in + # all routers of the routing domain. + if initial_convergence == True: + tries = 240 + else: + tries = 1 + router_compare_json_output( + rname, "show ipv6 route ospf json", "show_ipv6_route.json", tries + ) + + # Check that all adjacencies are up and running (except when there's + # an OSPF instance that is shutting down). + if exiting == None: + tries = 240 + router_compare_json_output( + rname, + "show ipv6 ospf neighbor json", + "show_ipv6_ospf_neighbor.json", + tries, + ) + + # Check the OSPF RIB and LSDB. + # In the restarting router, wait up to one minute for the LSDB to converge. + if exiting != rname: + if initial_convergence == True or restarting == rname: + tries = 240 + else: + tries = 1 + router_compare_json_output( + rname, + "show ipv6 ospf database json", + "show_ipv6_ospf_database.json", + tries, + ) + router_compare_json_output( + rname, "show ipv6 ospf route json", "show_ipv6_ospf_route.json", tries + ) + + +# +# Test initial network convergence +# +def test_initial_convergence(): + logger.info("Test: verify initial network convergence") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + check_routers(initial_convergence=True) + + +# +# Test rt1 performing a graceful restart +# +def test_gr_rt1(): + logger.info("Test: verify rt1 performing a graceful restart") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.net["rt1"].cmd('vtysh -c "graceful-restart prepare ipv6 ospf"') + sleep(5) + kill_router_daemons(tgen, "rt1", ["ospf6d"], save_config=False) + check_routers(exiting="rt1") + + start_router_daemons(tgen, "rt1", ["ospf6d"]) + check_routers(restarting="rt1") + + +# +# Test rt2 performing a graceful restart +# +def test_gr_rt2(): + logger.info("Test: verify rt2 performing a graceful restart") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.net["rt2"].cmd('vtysh -c "graceful-restart prepare ipv6 ospf"') + sleep(5) + kill_router_daemons(tgen, "rt2", ["ospf6d"], save_config=False) + check_routers(exiting="rt2") + + start_router_daemons(tgen, "rt2", ["ospf6d"]) + check_routers(restarting="rt2") + + +# +# Test rt3 performing a graceful restart +# +def test_gr_rt3(): + logger.info("Test: verify rt3 performing a graceful restart") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.net["rt3"].cmd('vtysh -c "graceful-restart prepare ipv6 ospf"') + sleep(5) + kill_router_daemons(tgen, "rt3", ["ospf6d"], save_config=False) + check_routers(exiting="rt3") + + start_router_daemons(tgen, "rt3", ["ospf6d"]) + check_routers(restarting="rt3") + + +# +# Test rt4 performing a graceful restart +# +def test_gr_rt4(): + logger.info("Test: verify rt4 performing a graceful restart") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.net["rt4"].cmd('vtysh -c "graceful-restart prepare ipv6 ospf"') + sleep(5) + kill_router_daemons(tgen, "rt4", ["ospf6d"], save_config=False) + check_routers(exiting="rt4") + + start_router_daemons(tgen, "rt4", ["ospf6d"]) + check_routers(restarting="rt4") + + +# +# Test rt5 performing a graceful restart +# +def test_gr_rt5(): + logger.info("Test: verify rt5 performing a graceful restart") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.net["rt5"].cmd('vtysh -c "graceful-restart prepare ipv6 ospf"') + sleep(5) + kill_router_daemons(tgen, "rt5", ["ospf6d"], save_config=False) + check_routers(exiting="rt5") + + start_router_daemons(tgen, "rt5", ["ospf6d"]) + check_routers(restarting="rt5") + + +# +# Test rt6 performing a graceful restart +# +def test_gr_rt6(): + logger.info("Test: verify rt6 performing a graceful restart") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.net["rt6"].cmd('vtysh -c "graceful-restart prepare ipv6 ospf"') + sleep(5) + kill_router_daemons(tgen, "rt6", ["ospf6d"], save_config=False) + check_routers(exiting="rt6") + + start_router_daemons(tgen, "rt6", ["ospf6d"]) + check_routers(restarting="rt6") + + +# +# Test rt7 performing a graceful restart +# +def test_gr_rt7(): + logger.info("Test: verify rt7 performing a graceful restart") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.net["rt7"].cmd('vtysh -c "graceful-restart prepare ipv6 ospf"') + sleep(5) + kill_router_daemons(tgen, "rt7", ["ospf6d"], save_config=False) + check_routers(exiting="rt7") + + start_router_daemons(tgen, "rt7", ["ospf6d"]) + check_routers(restarting="rt7") + + +# Memory leak test template +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/ospf6_topo2/test_ospf6_topo2.py b/tests/topotests/ospf6_topo2/test_ospf6_topo2.py index 3738a0c33e..acaa3b8a9b 100644 --- a/tests/topotests/ospf6_topo2/test_ospf6_topo2.py +++ b/tests/topotests/ospf6_topo2/test_ospf6_topo2.py @@ -72,14 +72,20 @@ def expect_lsas(router, area, lsas, wait=5, extra_params=""): assert result is None, assertmsg -def expect_ospfv3_routes(router, routes, wait=5, detail=False): +def expect_ospfv3_routes(router, routes, wait=5, type=None, detail=False): "Run command `ipv6 ospf6 route` and expect route with type." tgen = get_topogen() if detail == False: - cmd = "show ipv6 ospf6 route json" + if type == None: + cmd = "show ipv6 ospf6 route json" + else: + cmd = "show ipv6 ospf6 route {} json".format(type) else: - cmd = "show ipv6 ospf6 route detail json" + if type == None: + cmd = "show ipv6 ospf6 route detail json" + else: + cmd = "show ipv6 ospf6 route {} detail json".format(type) logger.info("waiting OSPFv3 router '{}' route".format(router)) test_func = partial( @@ -91,6 +97,21 @@ def expect_ospfv3_routes(router, routes, wait=5, detail=False): assert result is None, assertmsg +def dont_expect_route(router, unexpected_route, type=None): + "Specialized test function to expect route go missing" + tgen = get_topogen() + + if type == None: + cmd = "show ipv6 ospf6 route json" + else: + cmd = "show ipv6 ospf6 route {} json".format(type) + + output = tgen.gears[router].vtysh_cmd(cmd, isjson=True) + if unexpected_route in output["routes"]: + return output["routes"][unexpected_route] + return None + + def build_topo(tgen): "Build function" @@ -338,13 +359,6 @@ def test_nssa_lsa_type7(): return lsa return None - def dont_expect_route(unexpected_route): - "Specialized test function to expect route go missing" - output = tgen.gears["r4"].vtysh_cmd("show ipv6 ospf6 route json", isjson=True) - if unexpected_route in output["routes"]: - return output["routes"][unexpected_route] - return None - logger.info("Expecting LSA type-7 and OSPFv3 route 2001:db8:100::/64 to go away") # Test that LSA doesn't exist. @@ -354,12 +368,138 @@ def test_nssa_lsa_type7(): assert result is None, assertmsg # Test that route doesn't exist. - test_func = partial(dont_expect_route, "2001:db8:100::/64") + test_func = partial(dont_expect_route, "r4", "2001:db8:100::/64") _, result = topotest.run_and_expect(test_func, None, count=130, wait=1) assertmsg = '"{}" route still exists'.format("r4") assert result is None, assertmsg +def test_nssa_no_summary(): + """ + Test the following: + * Type-3 inter-area routes should be removed when the NSSA no-summary option + is configured; + * A type-3 inter-area default route should be originated into the NSSA area + when the no-summary option is configured; + * Once the no-summary option is unconfigured, all previously existing + Type-3 inter-area routes should be re-added, and the inter-area default + route removed. + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # + # Configure area 1 as a NSSA totally stub area. + # + config = """ + configure terminal + router ospf6 + area 2 nssa no-summary + """ + tgen.gears["r2"].vtysh_cmd(config) + + logger.info("Expecting inter-area routes to be removed") + for route in ["2001:db8:1::/64", "2001:db8:2::/64"]: + test_func = partial(dont_expect_route, "r4", route, type="inter-area") + _, result = topotest.run_and_expect(test_func, None, count=130, wait=1) + assertmsg = "{}'s {} inter-area route still exists".format("r4", route) + assert result is None, assertmsg + + logger.info("Expecting inter-area default-route to be added") + routes = {"::/0": {}} + expect_ospfv3_routes("r4", routes, wait=30, type="inter-area") + + # + # Configure area 1 as a regular NSSA area. + # + config = """ + configure terminal + router ospf6 + area 2 nssa + """ + tgen.gears["r2"].vtysh_cmd(config) + + logger.info("Expecting inter-area routes to be re-added") + routes = {"2001:db8:1::/64": {}, "2001:db8:2::/64": {}} + expect_ospfv3_routes("r4", routes, wait=30, type="inter-area") + + logger.info("Expecting inter-area default route to be removed") + test_func = partial(dont_expect_route, "r4", "::/0", type="inter-area") + _, result = topotest.run_and_expect(test_func, None, count=130, wait=1) + assertmsg = "{}'s inter-area default route still exists".format("r4") + assert result is None, assertmsg + + +def test_area_filters(): + """ + Test ABR import/export filters. + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # + # Configure import/export filters on r2 (ABR for area 1). + # + config = """ + configure terminal + ipv6 access-list ACL_IMPORT seq 5 permit 2001:db8:2::/64 + ipv6 access-list ACL_IMPORT seq 10 deny any + ipv6 access-list ACL_EXPORT seq 10 deny any + router ospf6 + area 1 import-list ACL_IMPORT + area 1 export-list ACL_EXPORT + """ + tgen.gears["r2"].vtysh_cmd(config) + + logger.info("Expecting inter-area routes to be removed on r1") + for route in ["::/0", "2001:db8:3::/64"]: + test_func = partial(dont_expect_route, "r1", route, type="inter-area") + _, result = topotest.run_and_expect(test_func, None, count=130, wait=1) + assertmsg = "{}'s {} inter-area route still exists".format("r1", route) + assert result is None, assertmsg + + logger.info("Expecting inter-area routes to be removed on r3") + for route in ["2001:db8:1::/64"]: + test_func = partial(dont_expect_route, "r3", route, type="inter-area") + _, result = topotest.run_and_expect(test_func, None, count=130, wait=1) + assertmsg = "{}'s {} inter-area route still exists".format("r3", route) + assert result is None, assertmsg + + # + # Update the ACLs used by the import/export filters. + # + config = """ + configure terminal + ipv6 access-list ACL_IMPORT seq 6 permit 2001:db8:3::/64 + ipv6 access-list ACL_EXPORT seq 5 permit 2001:db8:1::/64 + """ + tgen.gears["r2"].vtysh_cmd(config) + + logger.info("Expecting 2001:db8:3::/64 to be re-added on r1") + routes = {"2001:db8:3::/64": {}} + expect_ospfv3_routes("r1", routes, wait=30, type="inter-area") + logger.info("Expecting 2001:db8:1::/64 to be re-added on r3") + routes = {"2001:db8:1::/64": {}} + expect_ospfv3_routes("r3", routes, wait=30, type="inter-area") + + # + # Unconfigure r2's ABR import/export filters. + # + config = """ + configure terminal + router ospf6 + no area 1 import-list ACL_IMPORT + no area 1 export-list ACL_EXPORT + """ + tgen.gears["r2"].vtysh_cmd(config) + + logger.info("Expecting ::/0 to be re-added on r1") + routes = {"::/0": {}} + expect_ospfv3_routes("r1", routes, wait=30, type="inter-area") + + def teardown_module(_mod): "Teardown the pytest environment" tgen = get_topogen() diff --git a/tests/topotests/route_scale/test_route_scale.py b/tests/topotests/route_scale/scale_test_common.py index fefeccd5e7..3557cb4413 100644 --- a/tests/topotests/route_scale/test_route_scale.py +++ b/tests/topotests/route_scale/scale_test_common.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -# test_route_scale.py +# scale_test_common.py # # Copyright (c) 2020 by # Cumulus Networks, Inc. @@ -23,7 +23,7 @@ # """ -test_route_scale.py: Testing route scale +scale_test_common.py: Common routines for testing route scale """ @@ -45,9 +45,6 @@ from lib.topogen import Topogen, TopoRouter, get_topogen from lib.topolog import logger -pytestmark = [pytest.mark.sharpd] - - ##################################################### ## ## Network Topology Definition @@ -55,7 +52,7 @@ pytestmark = [pytest.mark.sharpd] ##################################################### -def build(tgen): +def scale_build_common(tgen): "Build function" # Populate routers @@ -68,16 +65,9 @@ def build(tgen): switch.add_link(tgen.gears["r1"]) -##################################################### -## -## Tests starting -## -##################################################### - - -def setup_module(module): +def scale_setup_module(module): "Setup topology" - tgen = Topogen(build, module.__name__) + tgen = Topogen(scale_build_common, module.__name__) tgen.start_topology() router_list = tgen.routers() @@ -93,7 +83,7 @@ def setup_module(module): # tgen.mininet_cli() -def teardown_module(_mod): +def scale_teardown_module(_mod): "Teardown the pytest environment" tgen = get_topogen() @@ -101,7 +91,7 @@ def teardown_module(_mod): tgen.stop_topology() -def test_converge_protocols(): +def scale_converge_protocols(): "Wait for protocol convergence" tgen = get_topogen() @@ -156,7 +146,7 @@ def run_one_setup(r1, s): logger.info(output) -def test_route_install(): +def route_install_helper(iter): "Test route install for a variety of ecmp" tgen = get_topogen() @@ -166,6 +156,16 @@ def test_route_install(): r1 = tgen.gears["r1"] + # Avoid top ecmp case for runs with < 4G memory + output = tgen.net.cmd_raises("free") + m = re.search("Mem:\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)", output) + total_mem = int(m.group(2)) + if total_mem < 4000000 and iter == 5: + logger.info( + "Limited memory available: {}, skipping x32 testcase".format(total_mem) + ) + return; + installed_file = "{}/r1/installed.routes.json".format(CWD) expected_installed = json.loads(open(installed_file).read()) @@ -196,38 +196,20 @@ def test_route_install(): # Build up a list of dicts with params for each step of the test; # use defaults where the step doesn't supply a value scale_setups = [] - for s in scale_steps: - d = dict(zip(scale_keys, s)) - for k in scale_keys: - if k not in d: - d[k] = scale_defaults[k] + s = scale_steps[iter] - scale_setups.append(d) + d = dict(zip(scale_keys, s)) + for k in scale_keys: + if k not in d: + d[k] = scale_defaults[k] - # Avoid top ecmp case for runs with < 4G memory - output = tgen.net.cmd_raises("free") - m = re.search("Mem:\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)", output) - total_mem = int(m.group(2)) - if total_mem < 4000000: - logger.info( - "Limited memory available: {}, skipping x32 testcase".format(total_mem) - ) - scale_setups = scale_setups[0:-1] - - # Run each step using the dicts we've built - for s in scale_setups: - run_one_setup(r1, s) + run_one_setup(r1, d) # Mem leak testcase -def test_memory_leak(): +def scale_test_memory_leak(): "Run the memory leak test and report results." tgen = get_topogen() if not tgen.is_memleak_enabled(): pytest.skip("Memory leak test/report is disabled") tgen.report_memory_leaks() - - -if __name__ == "__main__": - args = ["-s"] + sys.argv[1:] - sys.exit(pytest.main(args)) diff --git a/tests/topotests/route_scale/test_route_scale1.py b/tests/topotests/route_scale/test_route_scale1.py new file mode 100644 index 0000000000..b563883b45 --- /dev/null +++ b/tests/topotests/route_scale/test_route_scale1.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python + +# +# test_route_scale1.py +# +# Copyright (c) 2021 by +# Nvidia, Inc. +# Donald Sharp +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_route_scale1.py: Testing route scale + +""" +import os +import re +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 + +from scale_test_common import scale_build_common, scale_setup_module, route_install_helper, scale_test_memory_leak, scale_converge_protocols, scale_teardown_module + + +pytestmark = [pytest.mark.sharpd] + +def build(tgen): + scale_build_common(tgen) + +def setup_module(module): + scale_setup_module(module) + +def teardown_module(_mod): + scale_teardown_module(_mod) + +def test_converge_protocols(): + scale_converge_protocols() + +def test_route_install_2nh(): + route_install_helper(1) + +def test_route_install_4nh(): + route_install_helper(2) + +def test_route_install_16nh(): + route_install_helper(4) + +def test_memory_leak(): + scale_test_memory_leak() + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/route_scale/test_route_scale2.py b/tests/topotests/route_scale/test_route_scale2.py new file mode 100644 index 0000000000..7045995f26 --- /dev/null +++ b/tests/topotests/route_scale/test_route_scale2.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python + +# +# test_route_scale2.py +# +# Copyright (c) 2022 by +# Nvidia, Inc. +# Donald Sharp +# +# 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 NVIDIA DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NVIDIA BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_route_scale2.py: Testing route scale + +""" +import os +import re +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 + +from scale_test_common import scale_build_common, scale_setup_module, route_install_helper, scale_test_memory_leak, scale_converge_protocols, scale_teardown_module + + +pytestmark = [pytest.mark.sharpd] + +def build(tgen): + scale_build_common(tgen) + +def setup_module(module): + scale_setup_module(module) + +def teardown_module(_mod): + scale_teardown_module(_mod) + +def test_converge_protocols(): + scale_converge_protocols() + +def test_route_install_1nh(): + route_install_helper(0) + +def test_route_install_8nh(): + route_install_helper(3) + +def test_route_install_32nh(): + route_install_helper(5) + +def test_memory_leak(): + scale_test_memory_leak() + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/srv6_locator/expected_chunks4.json b/tests/topotests/srv6_locator/expected_chunks4.json index 6e49738f37..0d4f101c7a 100644 --- a/tests/topotests/srv6_locator/expected_chunks4.json +++ b/tests/topotests/srv6_locator/expected_chunks4.json @@ -1,6 +1,2 @@ [ - { - "name": "loc3", - "chunks": [] - } ] diff --git a/tests/topotests/srv6_locator/expected_chunks5.json b/tests/topotests/srv6_locator/expected_chunks5.json index a18221859e..0d4f101c7a 100644 --- a/tests/topotests/srv6_locator/expected_chunks5.json +++ b/tests/topotests/srv6_locator/expected_chunks5.json @@ -1,8 +1,2 @@ [ - { - "name": "loc3", - "chunks": [ - "2001:db8:3:3::/64" - ] - } ] diff --git a/tests/topotests/srv6_locator/expected_chunks6.json b/tests/topotests/srv6_locator/expected_chunks6.json new file mode 100644 index 0000000000..0d4f101c7a --- /dev/null +++ b/tests/topotests/srv6_locator/expected_chunks6.json @@ -0,0 +1,2 @@ +[ +] diff --git a/tests/topotests/srv6_locator/expected_locators4.json b/tests/topotests/srv6_locator/expected_locators4.json index 7989f9021b..4b0f95f7be 100644 --- a/tests/topotests/srv6_locator/expected_locators4.json +++ b/tests/topotests/srv6_locator/expected_locators4.json @@ -23,11 +23,13 @@ ] }, { - "name":"loc3", - "statusUp":false, - "chunks":[ + "name": "loc3", + "prefix": "2001:db8:3:3::/64", + "statusUp": true, + "chunks": [ { - "proto":"sharp" + "prefix": "2001:db8:3:3::/64", + "proto": "system" } ] } diff --git a/tests/topotests/srv6_locator/expected_locators5.json b/tests/topotests/srv6_locator/expected_locators5.json index 8c512ebc46..bcffa004bd 100644 --- a/tests/topotests/srv6_locator/expected_locators5.json +++ b/tests/topotests/srv6_locator/expected_locators5.json @@ -1,17 +1,6 @@ { "locators":[ { - "name": "loc1", - "prefix": "2001:db8:1:1::/64", - "statusUp": true, - "chunks": [ - { - "prefix": "2001:db8:1:1::/64", - "proto": "system" - } - ] - }, - { "name": "loc2", "prefix": "2001:db8:2:2::/64", "statusUp": true, @@ -29,7 +18,7 @@ "chunks":[ { "prefix": "2001:db8:3:3::/64", - "proto": "sharp" + "proto": "system" } ] } diff --git a/tests/topotests/srv6_locator/expected_locators6.json b/tests/topotests/srv6_locator/expected_locators6.json new file mode 100644 index 0000000000..66d23d5556 --- /dev/null +++ b/tests/topotests/srv6_locator/expected_locators6.json @@ -0,0 +1,5 @@ +{ + "locators":[ + ] +} + diff --git a/tests/topotests/srv6_locator/test_srv6_locator.py b/tests/topotests/srv6_locator/test_srv6_locator.py index b48cd09bf9..bc5fa409d2 100755 --- a/tests/topotests/srv6_locator/test_srv6_locator.py +++ b/tests/topotests/srv6_locator/test_srv6_locator.py @@ -102,6 +102,10 @@ def test_srv6(): success, result = topotest.run_and_expect(func, None, count=5, wait=0.5) assert result is None, "Failed" + # FOR DEVELOPER: + # If you want to stop some specific line and start interactive shell, + # please use tgen.mininet_cli() to start it. + logger.info("Test1 for Locator Configuration") check_srv6_locator(router, "expected_locators1.json") check_sharpd_chunk(router, "expected_chunks1.json") @@ -116,25 +120,44 @@ def test_srv6(): check_srv6_locator(router, "expected_locators3.json") check_sharpd_chunk(router, "expected_chunks3.json") - logger.info("Test4 get chunk for non-exist locator by zclient") - router.vtysh_cmd("sharp srv6-manager get-locator-chunk loc3") + logger.info("Test4 additional locator loc3") + router.vtysh_cmd( + """ + configure terminal + segment-routing + srv6 + locators + locator loc3 + prefix 2001:db8:3:3::/64 + """ + ) check_srv6_locator(router, "expected_locators4.json") check_sharpd_chunk(router, "expected_chunks4.json") - logger.info("Test5 Test for Zclient. after locator loc3 was configured") + logger.info("Test5 delete locator and chunk is released automatically") router.vtysh_cmd( """ configure terminal segment-routing srv6 locators - locator loc3 - prefix 2001:db8:3:3::/64 + no locator loc1 """ ) check_srv6_locator(router, "expected_locators5.json") check_sharpd_chunk(router, "expected_chunks5.json") + logger.info("Test6 delete srv6 all configuration") + router.vtysh_cmd( + """ + configure terminal + segment-routing + no srv6 + """ + ) + check_srv6_locator(router, "expected_locators6.json") + check_sharpd_chunk(router, "expected_chunks6.json") + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] diff --git a/tests/topotests/zebra_netlink/r1/v4_route.json b/tests/topotests/zebra_netlink/r1/v4_route.json deleted file mode 100644 index 39041ebc95..0000000000 --- a/tests/topotests/zebra_netlink/r1/v4_route.json +++ /dev/null @@ -1,2302 +0,0 @@ -{ - "2.1.3.7\/32":[ - { - "prefix":"2.1.3.7\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.8\/32":[ - { - "prefix":"2.1.3.8\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.9\/32":[ - { - "prefix":"2.1.3.9\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.10\/32":[ - { - "prefix":"2.1.3.10\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.11\/32":[ - { - "prefix":"2.1.3.11\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.12\/32":[ - { - "prefix":"2.1.3.12\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.13\/32":[ - { - "prefix":"2.1.3.13\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.14\/32":[ - { - "prefix":"2.1.3.14\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.15\/32":[ - { - "prefix":"2.1.3.15\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.16\/32":[ - { - "prefix":"2.1.3.16\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.17\/32":[ - { - "prefix":"2.1.3.17\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.18\/32":[ - { - "prefix":"2.1.3.18\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.19\/32":[ - { - "prefix":"2.1.3.19\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.20\/32":[ - { - "prefix":"2.1.3.20\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.21\/32":[ - { - "prefix":"2.1.3.21\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.22\/32":[ - { - "prefix":"2.1.3.22\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.23\/32":[ - { - "prefix":"2.1.3.23\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.24\/32":[ - { - "prefix":"2.1.3.24\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.25\/32":[ - { - "prefix":"2.1.3.25\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.26\/32":[ - { - "prefix":"2.1.3.26\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.27\/32":[ - { - "prefix":"2.1.3.27\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.28\/32":[ - { - "prefix":"2.1.3.28\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.29\/32":[ - { - "prefix":"2.1.3.29\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.30\/32":[ - { - "prefix":"2.1.3.30\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.31\/32":[ - { - "prefix":"2.1.3.31\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.32\/32":[ - { - "prefix":"2.1.3.32\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.33\/32":[ - { - "prefix":"2.1.3.33\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.34\/32":[ - { - "prefix":"2.1.3.34\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.35\/32":[ - { - "prefix":"2.1.3.35\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.36\/32":[ - { - "prefix":"2.1.3.36\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.37\/32":[ - { - "prefix":"2.1.3.37\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.38\/32":[ - { - "prefix":"2.1.3.38\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.39\/32":[ - { - "prefix":"2.1.3.39\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.40\/32":[ - { - "prefix":"2.1.3.40\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.41\/32":[ - { - "prefix":"2.1.3.41\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.42\/32":[ - { - "prefix":"2.1.3.42\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.43\/32":[ - { - "prefix":"2.1.3.43\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.44\/32":[ - { - "prefix":"2.1.3.44\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.45\/32":[ - { - "prefix":"2.1.3.45\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.46\/32":[ - { - "prefix":"2.1.3.46\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.47\/32":[ - { - "prefix":"2.1.3.47\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.48\/32":[ - { - "prefix":"2.1.3.48\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.49\/32":[ - { - "prefix":"2.1.3.49\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.50\/32":[ - { - "prefix":"2.1.3.50\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.51\/32":[ - { - "prefix":"2.1.3.51\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.52\/32":[ - { - "prefix":"2.1.3.52\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.53\/32":[ - { - "prefix":"2.1.3.53\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.54\/32":[ - { - "prefix":"2.1.3.54\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.55\/32":[ - { - "prefix":"2.1.3.55\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.56\/32":[ - { - "prefix":"2.1.3.56\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.57\/32":[ - { - "prefix":"2.1.3.57\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.58\/32":[ - { - "prefix":"2.1.3.58\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.59\/32":[ - { - "prefix":"2.1.3.59\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.60\/32":[ - { - "prefix":"2.1.3.60\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.61\/32":[ - { - "prefix":"2.1.3.61\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.62\/32":[ - { - "prefix":"2.1.3.62\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.63\/32":[ - { - "prefix":"2.1.3.63\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.64\/32":[ - { - "prefix":"2.1.3.64\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.65\/32":[ - { - "prefix":"2.1.3.65\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.66\/32":[ - { - "prefix":"2.1.3.66\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.67\/32":[ - { - "prefix":"2.1.3.67\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.68\/32":[ - { - "prefix":"2.1.3.68\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.69\/32":[ - { - "prefix":"2.1.3.69\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.70\/32":[ - { - "prefix":"2.1.3.70\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.71\/32":[ - { - "prefix":"2.1.3.71\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.72\/32":[ - { - "prefix":"2.1.3.72\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.73\/32":[ - { - "prefix":"2.1.3.73\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.74\/32":[ - { - "prefix":"2.1.3.74\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.75\/32":[ - { - "prefix":"2.1.3.75\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.76\/32":[ - { - "prefix":"2.1.3.76\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.77\/32":[ - { - "prefix":"2.1.3.77\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.78\/32":[ - { - "prefix":"2.1.3.78\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.79\/32":[ - { - "prefix":"2.1.3.79\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.80\/32":[ - { - "prefix":"2.1.3.80\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.81\/32":[ - { - "prefix":"2.1.3.81\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.82\/32":[ - { - "prefix":"2.1.3.82\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.83\/32":[ - { - "prefix":"2.1.3.83\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.84\/32":[ - { - "prefix":"2.1.3.84\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.85\/32":[ - { - "prefix":"2.1.3.85\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.86\/32":[ - { - "prefix":"2.1.3.86\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.87\/32":[ - { - "prefix":"2.1.3.87\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.88\/32":[ - { - "prefix":"2.1.3.88\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.89\/32":[ - { - "prefix":"2.1.3.89\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.90\/32":[ - { - "prefix":"2.1.3.90\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.91\/32":[ - { - "prefix":"2.1.3.91\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.92\/32":[ - { - "prefix":"2.1.3.92\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.93\/32":[ - { - "prefix":"2.1.3.93\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.94\/32":[ - { - "prefix":"2.1.3.94\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.95\/32":[ - { - "prefix":"2.1.3.95\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.96\/32":[ - { - "prefix":"2.1.3.96\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.97\/32":[ - { - "prefix":"2.1.3.97\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.98\/32":[ - { - "prefix":"2.1.3.98\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.99\/32":[ - { - "prefix":"2.1.3.99\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.100\/32":[ - { - "prefix":"2.1.3.100\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.101\/32":[ - { - "prefix":"2.1.3.101\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.102\/32":[ - { - "prefix":"2.1.3.102\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.103\/32":[ - { - "prefix":"2.1.3.103\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.104\/32":[ - { - "prefix":"2.1.3.104\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.105\/32":[ - { - "prefix":"2.1.3.105\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ], - "2.1.3.106\/32":[ - { - "prefix":"2.1.3.106\/32", - "protocol":"sharp", - "selected":true, - "destSelected":true, - "distance":150, - "metric":0, - "installed":true, - "table":254, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.1.1", - "afi":"ipv4", - "interfaceName":"r1-eth0", - "active":true, - "weight":1 - } - ] - } - ] -} diff --git a/tests/topotests/zebra_netlink/test_zebra_netlink.py b/tests/topotests/zebra_netlink/test_zebra_netlink.py index 05cc0ae4a1..ca90c5cb15 100644 --- a/tests/topotests/zebra_netlink/test_zebra_netlink.py +++ b/tests/topotests/zebra_netlink/test_zebra_netlink.py @@ -24,21 +24,15 @@ test_zebra_netlink.py: Test some basic interactions with kernel using Netlink """ - -import os -import sys -import pytest +# pylint: disable=C0413 +import ipaddress import json +import sys 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 +import pytest from lib import topotest -from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topogen import Topogen, TopoRouter from lib.topolog import logger @@ -52,62 +46,73 @@ pytestmark = [pytest.mark.sharpd] ##################################################### -def setup_module(mod): +@pytest.fixture(scope="module") +def tgen(request): "Sets up the pytest environment" topodef = {"s1": ("r1")} - tgen = Topogen(topodef, mod.__name__) + tgen = Topogen(topodef, request.module.__name__) tgen.start_topology() + # Initialize all routers. router_list = tgen.routers() 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_SHARP, os.path.join(CWD, "{}/sharpd.conf".format(rname)) - ) + router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf") + router.load_config(TopoRouter.RD_SHARP) - # Initialize all routers. tgen.start_router() + yield tgen + tgen.stop_topology() -def teardown_module(_mod): - "Teardown the pytest environment" - tgen = get_topogen() - - # This function tears down the whole topology. - tgen.stop_topology() +@pytest.fixture(autouse=True) +def skip_on_failure(tgen): + if tgen.routers_have_failure(): + pytest.skip("skipped because of previous test failure") -def test_zebra_netlink_batching(): +def test_zebra_netlink_batching(tgen): "Test the situation where dataplane fills netlink send buffer entirely." logger.info( "Test the situation where dataplane fills netlink send buffer entirely." ) - tgen = get_topogen() - if tgen.routers_have_failure(): - pytest.skip("skipped because of previous test failure") r1 = tgen.gears["r1"] # Reduce the size of the buffer to hit the limit. r1.vtysh_cmd("conf t\nzebra kernel netlink batch-tx-buf 256 256") - r1.vtysh_cmd("sharp install routes 2.1.3.7 nexthop 192.168.1.1 100") - json_file = "{}/r1/v4_route.json".format(CWD) - expected = json.loads(open(json_file).read()) - test_func = partial( - topotest.router_json_cmp, - r1, - "show ip route json", - expected, - ) - _, result = topotest.run_and_expect(test_func, None, count=2, wait=0.5) - assertmsg = '"r1" JSON output mismatches' - assert result is None, assertmsg - - r1.vtysh_cmd("sharp remove routes 2.1.3.7 100") + count = 100 + r1.vtysh_cmd("sharp install routes 2.1.3.7 nexthop 192.168.1.1 " + str(count)) + + # Generate expected results + entry = { + "protocol": "sharp", + "distance": 150, + "metric": 0, + "installed": True, + "table": 254, + "nexthops": [ + { + "fib": True, + "ip": "192.168.1.1", + "afi": "ipv4", + "interfaceName": "r1-eth0", + "active": True, + "weight": 1, + } + ], + } + + match = {} + base = int(ipaddress.ip_address(u"2.1.3.7")) + for i in range(base, base + count): + pfx = str(ipaddress.ip_network((i, 32))) + match[pfx] = [dict(entry, prefix=pfx)] + + ok = topotest.router_json_cmp_retry(r1, "show ip route json", match) + assert ok, '"r1" JSON output mismatches' + + r1.vtysh_cmd("sharp remove routes 2.1.3.7 " + str(count)) if __name__ == "__main__": diff --git a/tests/zebra/test_lm_plugin.c b/tests/zebra/test_lm_plugin.c index 4a9344fee4..ecfb085793 100644 --- a/tests/zebra/test_lm_plugin.c +++ b/tests/zebra/test_lm_plugin.c @@ -77,7 +77,7 @@ static int lm_release_chunk_pi(struct zserv *client, uint32_t start, /* use external allocations */ -static void lp_plugin_init() +static void lp_plugin_init(void) { /* register our own hooks */ hook_register(lm_client_connect, test_client_connect); @@ -86,7 +86,7 @@ static void lp_plugin_init() hook_register(lm_release_chunk, lm_release_chunk_pi); } -static void lp_plugin_cleanup() +static void lp_plugin_cleanup(void) { /* register our own hooks */ hook_unregister(lm_client_connect, test_client_connect); @@ -98,7 +98,7 @@ static void lp_plugin_cleanup() /* tests */ -static void test_lp_plugin() +static void test_lp_plugin(void) { struct label_manager_chunk *lmc; diff --git a/tools/permutations.c b/tools/permutations.c index f51d4a4ec9..b280cc15b1 100644 --- a/tools/permutations.c +++ b/tools/permutations.c @@ -61,9 +61,22 @@ void permute(struct graph_node *start) struct cmd_token *stok = start->data; struct graph_node *gnn; struct listnode *ln; + bool is_neg = false; // recursive dfs listnode_add(position, start); + + for (ALL_LIST_ELEMENTS_RO(position, ln, gnn)) { + struct cmd_token *tok = gnn->data; + + if (tok->type == WORD_TKN && !strcmp(tok->text, "no")) { + is_neg = true; + break; + } + if (tok->type < SPECIAL_TKN) + break; + } + for (unsigned int i = 0; i < vector_active(start->to); i++) { struct graph_node *gn = vector_slot(start->to, i); struct cmd_token *tok = gn->data; @@ -82,6 +95,9 @@ void permute(struct graph_node *start) fprintf(stdout, "\n"); } else { bool skip = false; + + if (tok->type == NEG_ONLY_TKN && !is_neg) + continue; if (stok->type == FORK_TKN && tok->type != FORK_TKN) for (ALL_LIST_ELEMENTS_RO(position, ln, gnn)) if (gnn == gn) { diff --git a/vtysh/extract.pl.in b/vtysh/extract.pl.in index 86cf8c9657..334bd7affa 100755 --- a/vtysh/extract.pl.in +++ b/vtysh/extract.pl.in @@ -42,11 +42,13 @@ sub scan_file { $cppadd = $fabricd ? "-DFABRICD=1" : ""; - open (FH, "@CPP@ -P -std=gnu11 -DHAVE_CONFIG_H -DVTYSH_EXTRACT_PL -Ivtysh/@top_builddir@ -Ivtysh/@top_srcdir@ -Ivtysh/@top_srcdir@/lib -Ivtysh/@top_builddir@/lib -Ivtysh/@top_srcdir@/bgpd -Ivtysh/@top_srcdir@/bgpd/rfapi @LUA_INCLUDE@ @CPPFLAGS@ @LIBYANG_CFLAGS@ $cppadd $file |"); + $command_line = "@CPP@ -P -std=gnu11 -DHAVE_CONFIG_H -DVTYSH_EXTRACT_PL -Ivtysh/@top_builddir@ -Ivtysh/@top_srcdir@ -Ivtysh/@top_srcdir@/lib -Ivtysh/@top_builddir@/lib -Ivtysh/@top_srcdir@/bgpd -Ivtysh/@top_srcdir@/bgpd/rfapi @LUA_INCLUDE@ @CPPFLAGS@ @LIBYANG_CFLAGS@ $cppadd $file |"; + open (FH, $command_line) + || die "Open to the pipeline failed: $!\n\nCommand Issued:\n$command_line"; local $/; undef $/; $line = <FH>; if (!close (FH)) { - printf "File: $file failed to compile, when extracting cli from it please inspect\n" + die "File: $file failed to compile:\n$!\nwhen extracting cli from it please inspect\n" } # ?: makes a group non-capturing diff --git a/zebra/connected.c b/zebra/connected.c index e1dd0dbdff..0511b35185 100644 --- a/zebra/connected.c +++ b/zebra/connected.c @@ -307,9 +307,10 @@ void connected_up(struct interface *ifp, struct connected *ifc) } /* Add connected IPv4 route to the interface. */ -void connected_add_ipv4(struct interface *ifp, int flags, struct in_addr *addr, - uint16_t prefixlen, struct in_addr *dest, - const char *label, uint32_t metric) +void connected_add_ipv4(struct interface *ifp, int flags, + const struct in_addr *addr, uint16_t prefixlen, + const struct in_addr *dest, const char *label, + uint32_t metric) { struct prefix_ipv4 *p; struct connected *ifc; @@ -502,8 +503,8 @@ static void connected_delete_helper(struct connected *ifc, struct prefix *p) /* Delete connected IPv4 route to the interface. */ void connected_delete_ipv4(struct interface *ifp, int flags, - struct in_addr *addr, uint16_t prefixlen, - struct in_addr *dest) + const struct in_addr *addr, uint16_t prefixlen, + const struct in_addr *dest) { struct prefix p, d; struct connected *ifc; @@ -527,8 +528,9 @@ void connected_delete_ipv4(struct interface *ifp, int flags, } /* Add connected IPv6 route to the interface. */ -void connected_add_ipv6(struct interface *ifp, int flags, struct in6_addr *addr, - struct in6_addr *dest, uint16_t prefixlen, +void connected_add_ipv6(struct interface *ifp, int flags, + const struct in6_addr *addr, + const struct in6_addr *dest, uint16_t prefixlen, const char *label, uint32_t metric) { struct prefix_ipv6 *p; @@ -589,8 +591,9 @@ void connected_add_ipv6(struct interface *ifp, int flags, struct in6_addr *addr, connected_update(ifp, ifc); } -void connected_delete_ipv6(struct interface *ifp, struct in6_addr *address, - struct in6_addr *dest, uint16_t prefixlen) +void connected_delete_ipv6(struct interface *ifp, + const struct in6_addr *address, + const struct in6_addr *dest, uint16_t prefixlen) { struct prefix p, d; struct connected *ifc; diff --git a/zebra/connected.h b/zebra/connected.h index 14f6cb2db0..3ed9f6d5b9 100644 --- a/zebra/connected.h +++ b/zebra/connected.h @@ -39,13 +39,14 @@ extern struct connected *connected_check_ptp(struct interface *ifp, union prefixconstptr d); extern void connected_add_ipv4(struct interface *ifp, int flags, - struct in_addr *addr, uint16_t prefixlen, - struct in_addr *dest, const char *label, + const struct in_addr *addr, uint16_t prefixlen, + const struct in_addr *dest, const char *label, uint32_t metric); extern void connected_delete_ipv4(struct interface *ifp, int flags, - struct in_addr *addr, uint16_t prefixlen, - struct in_addr *dest); + const struct in_addr *addr, + uint16_t prefixlen, + const struct in_addr *dest); extern void connected_delete_ipv4_unnumbered(struct connected *ifc); @@ -53,12 +54,13 @@ extern void connected_up(struct interface *ifp, struct connected *ifc); extern void connected_down(struct interface *ifp, struct connected *ifc); extern void connected_add_ipv6(struct interface *ifp, int flags, - struct in6_addr *address, struct in6_addr *dest, - uint16_t prefixlen, const char *label, - uint32_t metric); + const struct in6_addr *address, + const struct in6_addr *dest, uint16_t prefixlen, + const char *label, uint32_t metric); extern void connected_delete_ipv6(struct interface *ifp, - struct in6_addr *address, - struct in6_addr *dest, uint16_t prefixlen); + const struct in6_addr *address, + const struct in6_addr *dest, + uint16_t prefixlen); extern int connected_is_unnumbered(struct interface *); diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index a78b7607a0..acd6697b65 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -1444,7 +1444,6 @@ int netlink_interface_addr(struct nlmsghdr *h, ns_id_t ns_id, int startup) NULL, ifa->ifa_prefixlen); } - /* * Linux kernel does not send route delete on interface down/addr del * so we have to re-process routes it owns (i.e. kernel routes) @@ -1455,6 +1454,215 @@ int netlink_interface_addr(struct nlmsghdr *h, ns_id_t ns_id, int startup) return 0; } +/* + * Parse and validate an incoming interface address change message, + * generating a dplane context object. + * This runs in the dplane pthread; the context is enqueued to the + * main pthread for processing. + */ +int netlink_interface_addr_dplane(struct nlmsghdr *h, ns_id_t ns_id, + int startup /*ignored*/) +{ + int len; + struct ifaddrmsg *ifa; + struct rtattr *tb[IFA_MAX + 1]; + void *addr; + void *broad; + char *label = NULL; + uint32_t metric = METRIC_MAX; + uint32_t kernel_flags = 0; + struct zebra_dplane_ctx *ctx; + struct prefix p; + + ifa = NLMSG_DATA(h); + + /* Validate message types */ + if (h->nlmsg_type != RTM_NEWADDR && h->nlmsg_type != RTM_DELADDR) + return 0; + + if (ifa->ifa_family != AF_INET && ifa->ifa_family != AF_INET6) { + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("%s: %s: Invalid address family: %u", + __func__, nl_msg_type_to_str(h->nlmsg_type), + ifa->ifa_family); + return 0; + } + + len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct ifaddrmsg)); + if (len < 0) { + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("%s: %s: netlink msg bad size: %d %zu", + __func__, nl_msg_type_to_str(h->nlmsg_type), + h->nlmsg_len, + (size_t)NLMSG_LENGTH( + sizeof(struct ifaddrmsg))); + return -1; + } + + netlink_parse_rtattr(tb, IFA_MAX, IFA_RTA(ifa), len); + + /* Flags passed through */ + if (tb[IFA_FLAGS]) + kernel_flags = *(int *)RTA_DATA(tb[IFA_FLAGS]); + else + kernel_flags = ifa->ifa_flags; + + if (IS_ZEBRA_DEBUG_KERNEL) { /* remove this line to see initial ifcfg */ + char buf[PREFIX_STRLEN]; + + zlog_debug("%s: %s nsid %u ifindex %u flags 0x%x:", __func__, + nl_msg_type_to_str(h->nlmsg_type), ns_id, + ifa->ifa_index, kernel_flags); + if (tb[IFA_LOCAL]) + zlog_debug(" IFA_LOCAL %s/%d", + inet_ntop(ifa->ifa_family, + RTA_DATA(tb[IFA_LOCAL]), buf, + sizeof(buf)), + ifa->ifa_prefixlen); + if (tb[IFA_ADDRESS]) + zlog_debug(" IFA_ADDRESS %s/%d", + inet_ntop(ifa->ifa_family, + RTA_DATA(tb[IFA_ADDRESS]), buf, + sizeof(buf)), + ifa->ifa_prefixlen); + if (tb[IFA_BROADCAST]) + zlog_debug(" IFA_BROADCAST %s/%d", + inet_ntop(ifa->ifa_family, + RTA_DATA(tb[IFA_BROADCAST]), buf, + sizeof(buf)), + ifa->ifa_prefixlen); + if (tb[IFA_LABEL]) + zlog_debug(" IFA_LABEL %s", + (const char *)RTA_DATA(tb[IFA_LABEL])); + + if (tb[IFA_CACHEINFO]) { + struct ifa_cacheinfo *ci = RTA_DATA(tb[IFA_CACHEINFO]); + + zlog_debug(" IFA_CACHEINFO pref %d, valid %d", + ci->ifa_prefered, ci->ifa_valid); + } + } + + /* Validate prefix length */ + + if (ifa->ifa_family == AF_INET + && ifa->ifa_prefixlen > IPV4_MAX_BITLEN) { + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("%s: %s: Invalid prefix length: %u", + __func__, nl_msg_type_to_str(h->nlmsg_type), + ifa->ifa_prefixlen); + return -1; + } + + if (ifa->ifa_family == AF_INET6) { + if (ifa->ifa_prefixlen > IPV6_MAX_BITLEN) { + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("%s: %s: Invalid prefix length: %u", + __func__, + nl_msg_type_to_str(h->nlmsg_type), + ifa->ifa_prefixlen); + return -1; + } + + /* Only consider valid addresses; we'll not get a kernel + * notification till IPv6 DAD has completed, but at init + * time, FRR does query for and will receive all addresses. + */ + if (h->nlmsg_type == RTM_NEWADDR + && (kernel_flags & (IFA_F_DADFAILED | IFA_F_TENTATIVE))) { + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("%s: %s: Invalid/tentative addr", + __func__, + nl_msg_type_to_str(h->nlmsg_type)); + return 0; + } + } + + /* logic copied from iproute2/ip/ipaddress.c:print_addrinfo() */ + if (tb[IFA_LOCAL] == NULL) + tb[IFA_LOCAL] = tb[IFA_ADDRESS]; + if (tb[IFA_ADDRESS] == NULL) + tb[IFA_ADDRESS] = tb[IFA_LOCAL]; + + /* local interface address */ + addr = (tb[IFA_LOCAL] ? RTA_DATA(tb[IFA_LOCAL]) : NULL); + + /* addr is primary key, SOL if we don't have one */ + if (addr == NULL) { + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("%s: %s: No local interface address", + __func__, nl_msg_type_to_str(h->nlmsg_type)); + return -1; + } + + /* Allocate a context object, now that validation is done. */ + ctx = dplane_ctx_alloc(); + if (h->nlmsg_type == RTM_NEWADDR) + dplane_ctx_set_op(ctx, DPLANE_OP_INTF_ADDR_ADD); + else + dplane_ctx_set_op(ctx, DPLANE_OP_INTF_ADDR_DEL); + + dplane_ctx_set_ifindex(ctx, ifa->ifa_index); + dplane_ctx_set_ns_id(ctx, ns_id); + + /* Convert addr to prefix */ + memset(&p, 0, sizeof(p)); + p.family = ifa->ifa_family; + p.prefixlen = ifa->ifa_prefixlen; + if (p.family == AF_INET) + p.u.prefix4 = *(struct in_addr *)addr; + else + p.u.prefix6 = *(struct in6_addr *)addr; + + dplane_ctx_set_intf_addr(ctx, &p); + + /* is there a peer address? */ + if (tb[IFA_ADDRESS] + && memcmp(RTA_DATA(tb[IFA_ADDRESS]), RTA_DATA(tb[IFA_LOCAL]), + RTA_PAYLOAD(tb[IFA_ADDRESS]))) { + broad = RTA_DATA(tb[IFA_ADDRESS]); + dplane_ctx_intf_set_connected(ctx); + } else if (tb[IFA_BROADCAST]) { + /* seeking a broadcast address */ + broad = RTA_DATA(tb[IFA_BROADCAST]); + dplane_ctx_intf_set_broadcast(ctx); + } else + broad = NULL; + + if (broad) { + /* Convert addr to prefix */ + memset(&p, 0, sizeof(p)); + p.family = ifa->ifa_family; + p.prefixlen = ifa->ifa_prefixlen; + if (p.family == AF_INET) + p.u.prefix4 = *(struct in_addr *)broad; + else + p.u.prefix6 = *(struct in6_addr *)broad; + + dplane_ctx_set_intf_dest(ctx, &p); + } + + /* Flags. */ + if (kernel_flags & IFA_F_SECONDARY) + dplane_ctx_intf_set_secondary(ctx); + + /* Label */ + if (tb[IFA_LABEL]) { + label = (char *)RTA_DATA(tb[IFA_LABEL]); + dplane_ctx_set_intf_label(ctx, label); + } + + if (tb[IFA_RT_PRIORITY]) + metric = *(uint32_t *)RTA_DATA(tb[IFA_RT_PRIORITY]); + + dplane_ctx_set_intf_metric(ctx, metric); + + /* Enqueue ctx for main pthread to process */ + dplane_provider_enqueue_to_zebra(ctx); + + return 0; +} + int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) { int len; diff --git a/zebra/if_netlink.h b/zebra/if_netlink.h index 4f09b10b75..a1ce7af8c7 100644 --- a/zebra/if_netlink.h +++ b/zebra/if_netlink.h @@ -29,6 +29,14 @@ extern "C" { extern int netlink_interface_addr(struct nlmsghdr *h, ns_id_t ns_id, int startup); + +/* + * Parse an incoming interface address change message, generate a dplane + * context object for processing. + */ +int netlink_interface_addr_dplane(struct nlmsghdr *h, ns_id_t ns_id, + int startup); + extern int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup); extern int interface_lookup_netlink(struct zebra_ns *zns); diff --git a/zebra/interface.c b/zebra/interface.c index 328ef3fa41..a68d00d55c 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -1205,6 +1205,109 @@ void zebra_if_set_protodown(struct interface *ifp, bool down) #endif } +/* + * Handle an interface addr event based on info in a dplane context object. + * This runs in the main pthread, using the info in the context object to + * modify an interface. + */ +void zebra_if_addr_update_ctx(struct zebra_dplane_ctx *ctx) +{ + struct interface *ifp; + uint8_t flags = 0; + const char *label = NULL; + ns_id_t ns_id; + struct zebra_ns *zns; + uint32_t metric = METRIC_MAX; + ifindex_t ifindex; + const struct prefix *addr, *dest = NULL; + enum dplane_op_e op; + + op = dplane_ctx_get_op(ctx); + ns_id = dplane_ctx_get_ns_id(ctx); + + zns = zebra_ns_lookup(ns_id); + if (zns == NULL) { + /* No ns - deleted maybe? */ + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("%s: can't find zns id %u", __func__, ns_id); + goto done; + } + + ifindex = dplane_ctx_get_ifindex(ctx); + + ifp = if_lookup_by_index_per_ns(zns, ifindex); + if (ifp == NULL) { + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("%s: can't find ifp at nsid %u index %d", + __func__, ns_id, ifindex); + goto done; + } + + addr = dplane_ctx_get_intf_addr(ctx); + + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("%s: %s: ifindex %u, addr %pFX", __func__, + dplane_op2str(op), ifindex, addr); + + /* Is there a peer or broadcast address? */ + dest = dplane_ctx_get_intf_dest(ctx); + if (dest->prefixlen == 0) + dest = NULL; + + if (dplane_ctx_intf_is_connected(ctx)) + SET_FLAG(flags, ZEBRA_IFA_PEER); + + /* Flags. */ + if (dplane_ctx_intf_is_secondary(ctx)) + SET_FLAG(flags, ZEBRA_IFA_SECONDARY); + + /* Label? */ + if (dplane_ctx_intf_has_label(ctx)) + label = dplane_ctx_get_intf_label(ctx); + + if (label && strcmp(ifp->name, label) == 0) + label = NULL; + + metric = dplane_ctx_get_intf_metric(ctx); + + /* Register interface address to the interface. */ + if (addr->family == AF_INET) { + if (op == DPLANE_OP_INTF_ADDR_ADD) + connected_add_ipv4( + ifp, flags, &addr->u.prefix4, addr->prefixlen, + dest ? &dest->u.prefix4 : NULL, label, metric); + else if (CHECK_FLAG(flags, ZEBRA_IFA_PEER)) { + /* Delete with a peer address */ + connected_delete_ipv4(ifp, flags, &addr->u.prefix4, + addr->prefixlen, + &dest->u.prefix4); + } else + connected_delete_ipv4(ifp, flags, &addr->u.prefix4, + addr->prefixlen, NULL); + } + + if (addr->family == AF_INET6) { + if (op == DPLANE_OP_INTF_ADDR_ADD) { + connected_add_ipv6(ifp, flags, &addr->u.prefix6, + dest ? &dest->u.prefix6 : NULL, + addr->prefixlen, label, metric); + } else + connected_delete_ipv6(ifp, &addr->u.prefix6, NULL, + addr->prefixlen); + } + + /* + * Linux kernel does not send route delete on interface down/addr del + * so we have to re-process routes it owns (i.e. kernel routes) + */ + if (op != DPLANE_OP_INTF_ADDR_ADD) + rib_update(RIB_UPDATE_KERNEL); + +done: + /* We're responsible for the ctx object */ + dplane_ctx_fini(&ctx); +} + /* Dump if address information to vty. */ static void connected_dump_vty(struct vty *vty, json_object *json, struct connected *connected) diff --git a/zebra/interface.h b/zebra/interface.h index d86bc68ef0..23e22bdda8 100644 --- a/zebra/interface.h +++ b/zebra/interface.h @@ -513,6 +513,7 @@ extern void zebra_l2_map_slave_to_bond(struct zebra_if *zif, vrf_id_t vrf); extern void zebra_l2_unmap_slave_from_bond(struct zebra_if *zif); extern const char *zebra_protodown_rc_str(enum protodown_reasons protodown_rc, char *pd_buf, uint32_t pd_buf_len); +void zebra_if_addr_update_ctx(struct zebra_dplane_ctx *ctx); #ifdef HAVE_PROC_NET_DEV extern void ifstat_update_proc(void); diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c index effec24c1f..602bdc1dc5 100644 --- a/zebra/kernel_netlink.c +++ b/zebra/kernel_netlink.c @@ -324,6 +324,10 @@ static int netlink_socket(struct nlsock *nl, unsigned long groups, return ret; } +/* + * Dispatch an incoming netlink message; used by the zebra main pthread's + * netlink event reader. + */ static int netlink_information_fetch(struct nlmsghdr *h, ns_id_t ns_id, int startup) { @@ -345,10 +349,6 @@ static int netlink_information_fetch(struct nlmsghdr *h, ns_id_t ns_id, return netlink_link_change(h, ns_id, startup); case RTM_DELLINK: return netlink_link_change(h, ns_id, startup); - case RTM_NEWADDR: - return netlink_interface_addr(h, ns_id, startup); - case RTM_DELADDR: - return netlink_interface_addr(h, ns_id, startup); case RTM_NEWNEIGH: case RTM_DELNEIGH: case RTM_GETNEIGH: @@ -361,6 +361,12 @@ static int netlink_information_fetch(struct nlmsghdr *h, ns_id_t ns_id, return netlink_nexthop_change(h, ns_id, startup); case RTM_DELNEXTHOP: return netlink_nexthop_change(h, ns_id, startup); + + /* Messages handled in the dplane thread */ + case RTM_NEWADDR: + case RTM_DELADDR: + return 0; + default: /* * If we have received this message then @@ -378,6 +384,32 @@ static int netlink_information_fetch(struct nlmsghdr *h, ns_id_t ns_id, return 0; } +/* + * Dispatch an incoming netlink message; used by the dataplane pthread's + * netlink event reader code. + */ +static int dplane_netlink_information_fetch(struct nlmsghdr *h, ns_id_t ns_id, + int startup) +{ + /* + * Dispatch the incoming messages that the dplane pthread handles + */ + switch (h->nlmsg_type) { + case RTM_NEWADDR: + case RTM_DELADDR: + return netlink_interface_addr_dplane(h, ns_id, startup); + + /* TODO */ + case RTM_NEWLINK: + case RTM_DELLINK: + + default: + break; + } + + return 0; +} + static int kernel_read(struct thread *thread) { struct zebra_ns *zns = (struct zebra_ns *)THREAD_ARG(thread); @@ -388,7 +420,7 @@ static int kernel_read(struct thread *thread) netlink_parse_info(netlink_information_fetch, &zns->netlink, &dp_info, 5, 0); - zns->t_netlink = NULL; + thread_add_read(zrouter.master, kernel_read, zns, zns->netlink.sock, &zns->t_netlink); @@ -396,6 +428,17 @@ static int kernel_read(struct thread *thread) } /* + * Called by the dplane pthread to read incoming OS messages and dispatch them. + */ +int kernel_dplane_read(struct zebra_dplane_info *info) +{ + netlink_parse_info(dplane_netlink_information_fetch, &info->nls, info, + 5, 0); + + return 0; +} + +/* * Filter out messages from self that occur on listener socket, * caused by our actions on the command socket(s) * @@ -408,7 +451,7 @@ static int kernel_read(struct thread *thread) * so that we only had to write one way to handle incoming * address add/delete changes. */ -static void netlink_install_filter(int sock, __u32 pid, __u32 dplane_pid) +static void netlink_install_filter(int sock, uint32_t pid, uint32_t dplane_pid) { /* * BPF_JUMP instructions and where you jump to are based upon @@ -476,8 +519,8 @@ static void netlink_install_filter(int sock, __u32 pid, __u32 dplane_pid) safe_strerror(errno)); } -void netlink_parse_rtattr_flags(struct rtattr **tb, int max, - struct rtattr *rta, int len, unsigned short flags) +void netlink_parse_rtattr_flags(struct rtattr **tb, int max, struct rtattr *rta, + int len, unsigned short flags) { unsigned short type; @@ -799,8 +842,7 @@ static int netlink_recv_msg(const struct nlsock *nl, struct msghdr msg, * ignored, -1 otherwise. */ static int netlink_parse_error(const struct nlsock *nl, struct nlmsghdr *h, - const struct zebra_dplane_info *zns, - bool startup) + bool is_cmd, bool startup) { struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(h); int errnum = err->error; @@ -833,7 +875,7 @@ static int netlink_parse_error(const struct nlsock *nl, struct nlmsghdr *h, } /* Deal with errors that occur because of races in link handling. */ - if (zns->is_cmd + if (is_cmd && ((msg_type == RTM_DELROUTE && (-errnum == ENODEV || -errnum == ESRCH)) || (msg_type == RTM_NEWROUTE @@ -852,7 +894,7 @@ static int netlink_parse_error(const struct nlsock *nl, struct nlmsghdr *h, * do not log these as an error. */ if (msg_type == RTM_DELNEIGH - || (zns->is_cmd && msg_type == RTM_NEWROUTE + || (is_cmd && msg_type == RTM_NEWROUTE && (-errnum == ESRCH || -errnum == ENETUNREACH))) { /* * This is known to happen in some situations, don't log as @@ -924,8 +966,9 @@ int netlink_parse_info(int (*filter)(struct nlmsghdr *, ns_id_t, int), /* Error handling. */ if (h->nlmsg_type == NLMSG_ERROR) { - int err = netlink_parse_error(nl, h, zns, - startup); + int err = netlink_parse_error( + nl, h, zns->is_cmd, startup); + if (err == 1) { if (!(h->nlmsg_flags & NLM_F_MULTI)) return 0; @@ -937,8 +980,8 @@ int netlink_parse_info(int (*filter)(struct nlmsghdr *, ns_id_t, int), /* OK we got netlink message. */ if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug( - "netlink_parse_info: %s type %s(%u), len=%d, seq=%u, pid=%u", - nl->name, + "%s: %s type %s(%u), len=%d, seq=%u, pid=%u", + __func__, nl->name, nl_msg_type_to_str(h->nlmsg_type), h->nlmsg_type, h->nlmsg_len, h->nlmsg_seq, h->nlmsg_pid); @@ -1140,7 +1183,8 @@ static int nl_batch_read_resp(struct nl_batch *bth) } if (h->nlmsg_type == NLMSG_ERROR) { - int err = netlink_parse_error(nl, h, bth->zns, 0); + int err = netlink_parse_error(nl, h, bth->zns->is_cmd, + false); if (err == -1) dplane_ctx_set_status( @@ -1359,6 +1403,8 @@ static enum netlink_msg_status nl_put_msg(struct nl_batch *bth, case DPLANE_OP_GRE_SET: return netlink_put_gre_set_msg(bth, ctx); + case DPLANE_OP_INTF_ADDR_ADD: + case DPLANE_OP_INTF_ADDR_DEL: case DPLANE_OP_NONE: return FRR_NETLINK_ERROR; } @@ -1455,12 +1501,25 @@ void kernel_init(struct zebra_ns *zns) exit(-1); } - snprintf(zns->netlink_dplane.name, sizeof(zns->netlink_dplane.name), - "netlink-dp (NS %u)", zns->ns_id); - zns->netlink_dplane.sock = -1; - if (netlink_socket(&zns->netlink_dplane, 0, zns->ns_id) < 0) { + /* Outbound socket for dplane programming of the host OS. */ + snprintf(zns->netlink_dplane_out.name, + sizeof(zns->netlink_dplane_out.name), "netlink-dp (NS %u)", + zns->ns_id); + zns->netlink_dplane_out.sock = -1; + if (netlink_socket(&zns->netlink_dplane_out, 0, zns->ns_id) < 0) { zlog_err("Failure to create %s socket", - zns->netlink_dplane.name); + zns->netlink_dplane_out.name); + exit(-1); + } + + /* Inbound socket for OS events coming to the dplane. */ + snprintf(zns->netlink_dplane_in.name, + sizeof(zns->netlink_dplane_in.name), "netlink-dp-in (NS %u)", + zns->ns_id); + zns->netlink_dplane_in.sock = -1; + if (netlink_socket(&zns->netlink_dplane_in, groups, zns->ns_id) < 0) { + zlog_err("Failure to create %s socket", + zns->netlink_dplane_in.name); exit(-1); } @@ -1483,8 +1542,8 @@ void kernel_init(struct zebra_ns *zns) errno, safe_strerror(errno)); one = 1; - ret = setsockopt(zns->netlink_dplane.sock, SOL_NETLINK, NETLINK_EXT_ACK, - &one, sizeof(one)); + ret = setsockopt(zns->netlink_dplane_out.sock, SOL_NETLINK, + NETLINK_EXT_ACK, &one, sizeof(one)); if (ret < 0) zlog_notice("Registration for extended dp ACK failed : %d %s", @@ -1496,8 +1555,8 @@ void kernel_init(struct zebra_ns *zns) * setsockopt fails, ignore the error. */ one = 1; - ret = setsockopt(zns->netlink_dplane.sock, SOL_NETLINK, NETLINK_CAP_ACK, - &one, sizeof(one)); + ret = setsockopt(zns->netlink_dplane_out.sock, SOL_NETLINK, + NETLINK_CAP_ACK, &one, sizeof(one)); if (ret < 0) zlog_notice( "Registration for reduced ACK packet size failed, probably running an early kernel"); @@ -1512,20 +1571,33 @@ void kernel_init(struct zebra_ns *zns) zlog_err("Can't set %s socket error: %s(%d)", zns->netlink_cmd.name, safe_strerror(errno), errno); - if (fcntl(zns->netlink_dplane.sock, F_SETFL, O_NONBLOCK) < 0) + if (fcntl(zns->netlink_dplane_out.sock, F_SETFL, O_NONBLOCK) < 0) zlog_err("Can't set %s socket error: %s(%d)", - zns->netlink_dplane.name, safe_strerror(errno), errno); + zns->netlink_dplane_out.name, safe_strerror(errno), + errno); + + if (fcntl(zns->netlink_dplane_in.sock, F_SETFL, O_NONBLOCK) < 0) + zlog_err("Can't set %s socket error: %s(%d)", + zns->netlink_dplane_in.name, safe_strerror(errno), + errno); /* Set receive buffer size if it's set from command line */ if (nl_rcvbufsize) { netlink_recvbuf(&zns->netlink, nl_rcvbufsize); netlink_recvbuf(&zns->netlink_cmd, nl_rcvbufsize); - netlink_recvbuf(&zns->netlink_dplane, nl_rcvbufsize); + netlink_recvbuf(&zns->netlink_dplane_out, nl_rcvbufsize); + netlink_recvbuf(&zns->netlink_dplane_in, nl_rcvbufsize); } - netlink_install_filter(zns->netlink.sock, + /* Set filter for inbound sockets, to exclude events we've generated + * ourselves. + */ + netlink_install_filter(zns->netlink.sock, zns->netlink_cmd.snl.nl_pid, + zns->netlink_dplane_out.snl.nl_pid); + + netlink_install_filter(zns->netlink_dplane_in.sock, zns->netlink_cmd.snl.nl_pid, - zns->netlink_dplane.snl.nl_pid); + zns->netlink_dplane_out.snl.nl_pid); zns->t_netlink = NULL; @@ -1549,13 +1621,18 @@ void kernel_terminate(struct zebra_ns *zns, bool complete) zns->netlink_cmd.sock = -1; } + if (zns->netlink_dplane_in.sock >= 0) { + close(zns->netlink_dplane_in.sock); + zns->netlink_dplane_in.sock = -1; + } + /* During zebra shutdown, we need to leave the dataplane socket * around until all work is done. */ if (complete) { - if (zns->netlink_dplane.sock >= 0) { - close(zns->netlink_dplane.sock); - zns->netlink_dplane.sock = -1; + if (zns->netlink_dplane_out.sock >= 0) { + close(zns->netlink_dplane_out.sock); + zns->netlink_dplane_out.sock = -1; } } } diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c index 5c060ac6f8..d9c69ceb6d 100644 --- a/zebra/kernel_socket.c +++ b/zebra/kernel_socket.c @@ -529,7 +529,7 @@ int ifm_read(struct if_msghdr *ifm) /* paranoia: sanity check structure */ if (ifm->ifm_msglen < sizeof(struct if_msghdr)) { flog_err(EC_ZEBRA_NETLINK_LENGTH_ERROR, - "ifm_read: ifm->ifm_msglen %d too short\n", + "ifm_read: ifm->ifm_msglen %d too short", ifm->ifm_msglen); return -1; } @@ -807,23 +807,17 @@ static void ifam_read_mesg(struct ifa_msghdr *ifm, union sockunion *addr, switch (sockunion_family(addr)) { case AF_INET: case AF_INET6: { - char buf[4][INET6_ADDRSTRLEN]; int masklen = (sockunion_family(addr) == AF_INET) ? ip_masklen(mask->sin.sin_addr) : ip6_masklen(mask->sin6.sin6_addr); zlog_debug( - "%s: ifindex %d, ifname %s, ifam_addrs {%s}, ifam_flags 0x%x, addr %s/%d broad %s dst %s gateway %s", + "%s: ifindex %d, ifname %s, ifam_addrs {%s}, ifam_flags 0x%x, addr %pSU/%d broad %pSU dst %pSU gateway %pSU", __func__, ifm->ifam_index, (ifnlen ? ifname : "(nil)"), rtatostr(ifm->ifam_addrs, fbuf, sizeof(fbuf)), - ifm->ifam_flags, - sockunion2str(addr, buf[0], sizeof(buf[0])), - masklen, - sockunion2str(brd, buf[1], sizeof(buf[1])), - sockunion2str(&dst, buf[2], sizeof(buf[2])), - sockunion2str(&gateway, buf[2], - sizeof(buf[2]))); + ifm->ifam_flags, addr, masklen, brd, &dst, + &gateway); } break; default: zlog_debug("%s: ifindex %d, ifname %s, ifam_addrs {%s}", @@ -951,7 +945,7 @@ static int rtm_read_mesg(struct rt_msghdr *rtm, union sockunion *dest, /* rt_msghdr version check. */ if (rtm->rtm_version != RTM_VERSION) flog_warn(EC_ZEBRA_RTM_VERSION_MISMATCH, - "Routing message version different %d should be %d.This may cause problem\n", + "Routing message version different %d should be %d.This may cause problem", rtm->rtm_version, RTM_VERSION); /* Be sure structure is cleared */ @@ -1463,6 +1457,14 @@ void kernel_terminate(struct zebra_ns *zns, bool complete) return; } +/* + * Called by the dplane pthread to read incoming OS messages and dispatch them. + */ +int kernel_dplane_read(struct zebra_dplane_info *info) +{ + return 0; +} + void kernel_update_multi(struct dplane_ctx_q *ctx_list) { struct zebra_dplane_ctx *ctx; diff --git a/zebra/router-id.c b/zebra/router-id.c index 689b9787ee..ac81d537d0 100644 --- a/zebra/router-id.c +++ b/zebra/router-id.c @@ -120,10 +120,12 @@ int router_id_get(afi_t afi, struct prefix *p, struct zebra_vrf *zvrf) static int router_id_set(afi_t afi, struct prefix *p, struct zebra_vrf *zvrf) { - struct prefix p2; + struct prefix after, before; struct listnode *node; struct zserv *client; + router_id_get(afi, &before, zvrf); + switch (afi) { case AFI_IP: zvrf->rid_user_assigned.u.prefix4.s_addr = p->u.prefix4.s_addr; @@ -135,10 +137,17 @@ static int router_id_set(afi_t afi, struct prefix *p, struct zebra_vrf *zvrf) return -1; } - router_id_get(afi, &p2, zvrf); + router_id_get(afi, &after, zvrf); + + /* + * If we've been told that the router-id is exactly the same + * do we need to really do anything here? + */ + if (prefix_same(&before, &after)) + return 0; for (ALL_LIST_ELEMENTS_RO(zrouter.client_list, node, client)) - zsend_router_id_update(client, afi, &p2, zvrf->vrf->vrf_id); + zsend_router_id_update(client, afi, &after, zvrf->vrf->vrf_id); return 0; } diff --git a/zebra/rt.h b/zebra/rt.h index 929a44ade7..90148d2c0d 100644 --- a/zebra/rt.h +++ b/zebra/rt.h @@ -110,6 +110,11 @@ extern int kernel_del_mac_nhg(uint32_t nhg_id); */ extern void kernel_update_multi(struct dplane_ctx_q *ctx_list); +/* + * Called by the dplane pthread to read incoming OS messages and dispatch them. + */ +int kernel_dplane_read(struct zebra_dplane_info *info); + #ifdef __cplusplus } #endif diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index 6666b3525e..496849251a 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -1137,6 +1137,31 @@ static int zsend_table_manager_connect_response(struct zserv *client, return zserv_send_message(client, s); } +/* SRv6 locator add notification from zebra daemon. */ +int zsend_zebra_srv6_locator_add(struct zserv *client, struct srv6_locator *loc) +{ + struct stream *s = stream_new(ZEBRA_MAX_PACKET_SIZ); + + zclient_create_header(s, ZEBRA_SRV6_LOCATOR_ADD, VRF_DEFAULT); + zapi_srv6_locator_encode(s, loc); + stream_putw_at(s, 0, stream_get_endp(s)); + + return zserv_send_message(client, s); +} + +/* SRv6 locator delete notification from zebra daemon. */ +int zsend_zebra_srv6_locator_delete(struct zserv *client, + struct srv6_locator *loc) +{ + struct stream *s = stream_new(ZEBRA_MAX_PACKET_SIZ); + + zclient_create_header(s, ZEBRA_SRV6_LOCATOR_DELETE, VRF_DEFAULT); + zapi_srv6_locator_encode(s, loc); + stream_putw_at(s, 0, stream_get_endp(s)); + + return zserv_send_message(client, s); +} + /* Inbound message handling ------------------------------------------------ */ const int cmd2type[] = { @@ -1611,7 +1636,8 @@ static struct nexthop *nexthop_from_zapi(const struct zapi_nexthop *api_nh, zlog_debug("%s: nh blackhole %d", __func__, api_nh->bh_type); - nexthop = nexthop_from_blackhole(api_nh->bh_type); + nexthop = + nexthop_from_blackhole(api_nh->bh_type, api_nh->vrf_id); break; } @@ -2221,8 +2247,8 @@ stream_failure: static void zread_router_id_add(ZAPI_HANDLER_ARGS) { afi_t afi; - struct prefix p; + struct prefix zero; STREAM_GETW(msg, afi); @@ -2238,6 +2264,18 @@ static void zread_router_id_add(ZAPI_HANDLER_ARGS) router_id_get(afi, &p, zvrf); + /* + * If we have not officially setup a router-id let's not + * tell the upper level protocol about it yet. + */ + memset(&zero, 0, sizeof(zero)); + if ((p.family == AF_INET && p.u.prefix4.s_addr == INADDR_ANY) + || (p.family == AF_INET6 + && memcmp(&p.u.prefix6, &zero.u.prefix6, + sizeof(struct in6_addr)) + == 0)) + return; + zsend_router_id_update(client, afi, &p, zvrf_id(zvrf)); stream_failure: diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index 39f865fbfc..ab06ea6438 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -37,11 +37,12 @@ #include "zebra/zebra_pbr.h" #include "printfrr.h" -/* Memory type for context blocks */ +/* Memory types */ DEFINE_MTYPE_STATIC(ZEBRA, DP_CTX, "Zebra DPlane Ctx"); DEFINE_MTYPE_STATIC(ZEBRA, DP_INTF, "Zebra DPlane Intf"); DEFINE_MTYPE_STATIC(ZEBRA, DP_PROV, "Zebra DPlane Provider"); DEFINE_MTYPE_STATIC(ZEBRA, DP_NETFILTER, "Zebra Netfilter Internal Object"); +DEFINE_MTYPE_STATIC(ZEBRA, DP_NS, "DPlane NSes"); #ifndef AOK # define AOK 0 @@ -402,6 +403,19 @@ struct zebra_dplane_provider { TAILQ_ENTRY(zebra_dplane_provider) dp_prov_link; }; +/* Declare types for list of zns info objects */ +PREDECL_DLIST(zns_info_list); + +struct dplane_zns_info { + struct zebra_dplane_info info; + + /* Read event */ + struct thread *t_read; + + /* List linkage */ + struct zns_info_list_item link; +}; + /* * Globals */ @@ -424,6 +438,9 @@ static struct zebra_dplane_globals { /* Ordered list of providers */ TAILQ_HEAD(zdg_prov_q, zebra_dplane_provider) dg_providers_q; + /* List of info about each zns */ + struct zns_info_list_head dg_zns_list; + /* Counter used to assign internal ids to providers */ uint32_t dg_provider_id; @@ -498,6 +515,9 @@ static struct zebra_dplane_globals { } zdplane_info; +/* Instantiate zns list type */ +DECLARE_DLIST(zns_info_list, struct dplane_zns_info, link); + /* * Lock and unlock for interactions with the zebra 'core' pthread */ @@ -690,6 +710,8 @@ static void dplane_ctx_free_internal(struct zebra_dplane_ctx *ctx) case DPLANE_OP_ADDR_INSTALL: case DPLANE_OP_ADDR_UNINSTALL: + case DPLANE_OP_INTF_ADDR_ADD: + case DPLANE_OP_INTF_ADDR_DEL: /* Maybe free label string, if allocated */ if (ctx->u.intf.label != NULL && ctx->u.intf.label != ctx->u.intf.label_buf) { @@ -1011,6 +1033,12 @@ const char *dplane_op2str(enum dplane_op_e op) case DPLANE_OP_GRE_SET: ret = "GRE_SET"; break; + + case DPLANE_OP_INTF_ADDR_ADD: + return "INTF_ADDR_ADD"; + + case DPLANE_OP_INTF_ADDR_DEL: + return "INTF_ADDR_DEL"; } return ret; @@ -1108,6 +1136,21 @@ vrf_id_t dplane_ctx_get_vrf(const struct zebra_dplane_ctx *ctx) return ctx->zd_vrf_id; } +/* In some paths we have only a namespace id */ +void dplane_ctx_set_ns_id(struct zebra_dplane_ctx *ctx, ns_id_t nsid) +{ + DPLANE_CTX_VALID(ctx); + + ctx->zd_ns_info.ns_id = nsid; +} + +ns_id_t dplane_ctx_get_ns_id(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->zd_ns_info.ns_id; +} + bool dplane_ctx_is_from_notif(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); @@ -1154,6 +1197,13 @@ ifindex_t dplane_ctx_get_ifindex(const struct zebra_dplane_ctx *ctx) return ctx->zd_ifindex; } +void dplane_ctx_set_ifindex(struct zebra_dplane_ctx *ctx, ifindex_t ifindex) +{ + DPLANE_CTX_VALID(ctx); + + ctx->zd_ifindex = ifindex; +} + void dplane_ctx_set_type(struct zebra_dplane_ctx *ctx, int type) { DPLANE_CTX_VALID(ctx); @@ -1669,6 +1719,13 @@ uint32_t dplane_ctx_get_intf_metric(const struct zebra_dplane_ctx *ctx) return ctx->u.intf.metric; } +void dplane_ctx_set_intf_metric(struct zebra_dplane_ctx *ctx, uint32_t metric) +{ + DPLANE_CTX_VALID(ctx); + + ctx->u.intf.metric = metric; +} + /* Is interface addr p2p? */ bool dplane_ctx_intf_is_connected(const struct zebra_dplane_ctx *ctx) { @@ -1691,6 +1748,27 @@ bool dplane_ctx_intf_is_broadcast(const struct zebra_dplane_ctx *ctx) return (ctx->u.intf.flags & DPLANE_INTF_BROADCAST); } +void dplane_ctx_intf_set_connected(struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + ctx->u.intf.flags |= DPLANE_INTF_CONNECTED; +} + +void dplane_ctx_intf_set_secondary(struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + ctx->u.intf.flags |= DPLANE_INTF_SECONDARY; +} + +void dplane_ctx_intf_set_broadcast(struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + ctx->u.intf.flags |= DPLANE_INTF_BROADCAST; +} + const struct prefix *dplane_ctx_get_intf_addr( const struct zebra_dplane_ctx *ctx) { @@ -1699,6 +1777,14 @@ const struct prefix *dplane_ctx_get_intf_addr( return &(ctx->u.intf.prefix); } +void dplane_ctx_set_intf_addr(struct zebra_dplane_ctx *ctx, + const struct prefix *p) +{ + DPLANE_CTX_VALID(ctx); + + prefix_copy(&(ctx->u.intf.prefix), p); +} + bool dplane_ctx_intf_has_dest(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); @@ -1711,10 +1797,15 @@ const struct prefix *dplane_ctx_get_intf_dest( { DPLANE_CTX_VALID(ctx); - if (ctx->u.intf.flags & DPLANE_INTF_HAS_DEST) - return &(ctx->u.intf.dest_prefix); - else - return NULL; + return &(ctx->u.intf.dest_prefix); +} + +void dplane_ctx_set_intf_dest(struct zebra_dplane_ctx *ctx, + const struct prefix *p) +{ + DPLANE_CTX_VALID(ctx); + + prefix_copy(&(ctx->u.intf.dest_prefix), p); } bool dplane_ctx_intf_has_label(const struct zebra_dplane_ctx *ctx) @@ -1731,6 +1822,35 @@ const char *dplane_ctx_get_intf_label(const struct zebra_dplane_ctx *ctx) return ctx->u.intf.label; } +void dplane_ctx_set_intf_label(struct zebra_dplane_ctx *ctx, const char *label) +{ + size_t len; + + DPLANE_CTX_VALID(ctx); + + if (ctx->u.intf.label && ctx->u.intf.label != ctx->u.intf.label_buf) + free(ctx->u.intf.label); + + ctx->u.intf.label = NULL; + + if (label) { + ctx->u.intf.flags |= DPLANE_INTF_HAS_LABEL; + + /* Use embedded buffer if it's adequate; else allocate. */ + len = strlen(label); + + if (len < sizeof(ctx->u.intf.label_buf)) { + strlcpy(ctx->u.intf.label_buf, label, + sizeof(ctx->u.intf.label_buf)); + ctx->u.intf.label = ctx->u.intf.label_buf; + } else { + ctx->u.intf.label = strdup(label); + } + } else { + ctx->u.intf.flags &= ~DPLANE_INTF_HAS_LABEL; + } +} + /* Accessors for MAC information */ vlanid_t dplane_ctx_mac_get_vlan(const struct zebra_dplane_ctx *ctx) { @@ -2177,9 +2297,9 @@ static int dplane_ctx_ns_init(struct zebra_dplane_ctx *ctx, * two messages in some 'update' cases. */ if (is_update) - zns->netlink_dplane.seq += 2; + zns->netlink_dplane_out.seq += 2; else - zns->netlink_dplane.seq++; + zns->netlink_dplane_out.seq++; #endif /* HAVE_NETLINK */ return AOK; @@ -4709,10 +4829,92 @@ static void dplane_info_from_zns(struct zebra_dplane_info *ns_info, #if defined(HAVE_NETLINK) ns_info->is_cmd = true; - ns_info->nls = zns->netlink_dplane; + ns_info->nls = zns->netlink_dplane_out; #endif /* NETLINK */ } +#ifdef HAVE_NETLINK +/* + * Callback when an OS (netlink) incoming event read is ready. This runs + * in the dplane pthread. + */ +static int dplane_incoming_read(struct thread *event) +{ + struct dplane_zns_info *zi = THREAD_ARG(event); + + kernel_dplane_read(&zi->info); + + /* Re-start read task */ + thread_add_read(zdplane_info.dg_master, dplane_incoming_read, zi, + zi->info.nls.sock, &zi->t_read); + + return 0; +} +#endif /* HAVE_NETLINK */ + +/* + * Notify dplane when namespaces are enabled and disabled. The dplane + * needs to start and stop reading incoming events from the zns. In the + * common case where vrfs are _not_ namespaces, there will only be one + * of these. + * + * This is called in the main pthread. + */ +void zebra_dplane_ns_enable(struct zebra_ns *zns, bool enabled) +{ + struct dplane_zns_info *zi; + + if (IS_ZEBRA_DEBUG_DPLANE) + zlog_debug("%s: %s for nsid %u", __func__, + (enabled ? "ENABLED" : "DISABLED"), zns->ns_id); + + /* Search for an existing zns info entry */ + frr_each (zns_info_list, &zdplane_info.dg_zns_list, zi) { + if (zi->info.ns_id == zns->ns_id) + break; + } + + if (enabled) { + /* Create a new entry if necessary; start reading. */ + if (zi == NULL) { + zi = XCALLOC(MTYPE_DP_NS, sizeof(*zi)); + + zi->info.ns_id = zns->ns_id; + + zns_info_list_add_tail(&zdplane_info.dg_zns_list, zi); + + if (IS_ZEBRA_DEBUG_DPLANE) + zlog_debug("%s: nsid %u, new zi %p", __func__, + zns->ns_id, zi); + } + + /* Make sure we're up-to-date with the zns object */ +#if defined(HAVE_NETLINK) + zi->info.is_cmd = false; + zi->info.nls = zns->netlink_dplane_in; + + /* Start read task for the dplane pthread. */ + if (zdplane_info.dg_master) + thread_add_read(zdplane_info.dg_master, + dplane_incoming_read, zi, + zi->info.nls.sock, &zi->t_read); +#endif + } else if (zi) { + if (IS_ZEBRA_DEBUG_DPLANE) + zlog_debug("%s: nsid %u, deleting zi %p", __func__, + zns->ns_id, zi); + + /* Stop reading, free memory */ + zns_info_list_del(&zdplane_info.dg_zns_list, zi); + + if (zdplane_info.dg_master) + thread_cancel_async(zdplane_info.dg_master, &zi->t_read, + NULL); + + XFREE(MTYPE_DP_NS, zi); + } +} + /* * Provider api to signal that work/events are available * for the dataplane pthread. @@ -4878,6 +5080,14 @@ static void kernel_dplane_log_detail(struct zebra_dplane_ctx *ctx) dplane_ctx_get_ifname(ctx), ctx->u.gre.link_ifindex); break; + + case DPLANE_OP_INTF_ADDR_ADD: + case DPLANE_OP_INTF_ADDR_DEL: + zlog_debug("Dplane incoming op %s, intf %s, addr %pFX", + dplane_op2str(dplane_ctx_get_op(ctx)), + dplane_ctx_get_ifname(ctx), + dplane_ctx_get_intf_addr(ctx)); + break; } } @@ -5020,6 +5230,11 @@ static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx) case DPLANE_OP_BR_PORT_UPDATE: break; + /* TODO -- error counters for incoming events? */ + case DPLANE_OP_INTF_ADDR_ADD: + case DPLANE_OP_INTF_ADDR_DEL: + break; + case DPLANE_OP_NONE: if (res != ZEBRA_DPLANE_REQUEST_SUCCESS) atomic_fetch_add_explicit(&zdplane_info.dg_other_errors, @@ -5355,9 +5570,21 @@ done: */ static int dplane_check_shutdown_status(struct thread *event) { + struct dplane_zns_info *zi; + if (IS_ZEBRA_DEBUG_DPLANE) zlog_debug("Zebra dataplane shutdown status check called"); + /* Remove any zns info entries as we stop the dplane pthread. */ + frr_each_safe (zns_info_list, &zdplane_info.dg_zns_list, zi) { + zns_info_list_del(&zdplane_info.dg_zns_list, zi); + + if (zdplane_info.dg_master) + thread_cancel(&zi->t_read); + + XFREE(MTYPE_DP_NS, zi); + } + if (dplane_work_pending()) { /* Reschedule dplane check on a short timer */ thread_add_timer_msec(zdplane_info.dg_master, @@ -5652,6 +5879,7 @@ static void zebra_dplane_init_internal(void) TAILQ_INIT(&zdplane_info.dg_update_ctx_q); TAILQ_INIT(&zdplane_info.dg_providers_q); + zns_info_list_init(&zdplane_info.dg_zns_list); zdplane_info.dg_updates_per_cycle = DPLANE_DEFAULT_NEW_WORK; @@ -5667,6 +5895,7 @@ static void zebra_dplane_init_internal(void) */ void zebra_dplane_start(void) { + struct dplane_zns_info *zi; struct zebra_dplane_provider *prov; struct frr_pthread_attr pattr = { .start = frr_pthread_attr_default.start, @@ -5686,6 +5915,14 @@ void zebra_dplane_start(void) thread_add_event(zdplane_info.dg_master, dplane_thread_loop, NULL, 0, &zdplane_info.dg_t_update); + /* Enqueue reads if necessary */ + frr_each (zns_info_list, &zdplane_info.dg_zns_list, zi) { +#if defined(HAVE_NETLINK) + thread_add_read(zdplane_info.dg_master, dplane_incoming_read, + zi, zi->info.nls.sock, &zi->t_read); +#endif + } + /* Call start callbacks for registered providers */ DPLANE_LOCK(); diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h index 3f3ff4de0f..a23de61c80 100644 --- a/zebra/zebra_dplane.h +++ b/zebra/zebra_dplane.h @@ -64,6 +64,12 @@ zebra_dplane_info_from_zns(struct zebra_dplane_info *zns_info, } /* + * Notify dplane when namespaces are enabled and disabled. The dplane + * needs to start and stop reading incoming events from the ns. + */ +void zebra_dplane_ns_enable(struct zebra_ns *zns, bool enabled); + +/* * Result codes used when returning status back to the main zebra context. */ @@ -98,7 +104,7 @@ enum zebra_dplane_result { */ /* - * Enqueue a route install or update for the dataplane. + * Operations that the dataplane can process. */ enum dplane_op_e { DPLANE_OP_NONE = 0, @@ -172,6 +178,10 @@ enum dplane_op_e { DPLANE_OP_NEIGH_TABLE_UPDATE, DPLANE_OP_GRE_SET, + + /* Incoming interface address events */ + DPLANE_OP_INTF_ADDR_ADD, + DPLANE_OP_INTF_ADDR_DEL, }; /* @@ -284,6 +294,7 @@ void dplane_ctx_set_dest(struct zebra_dplane_ctx *ctx, const char *dplane_ctx_get_ifname(const struct zebra_dplane_ctx *ctx); void dplane_ctx_set_ifname(struct zebra_dplane_ctx *ctx, const char *ifname); ifindex_t dplane_ctx_get_ifindex(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_ifindex(struct zebra_dplane_ctx *ctx, ifindex_t ifindex); /* Retrieve last/current provider id */ uint32_t dplane_ctx_get_provider(const struct zebra_dplane_ctx *ctx); @@ -306,6 +317,10 @@ uint32_t dplane_ctx_get_old_seq(const struct zebra_dplane_ctx *ctx); void dplane_ctx_set_vrf(struct zebra_dplane_ctx *ctx, vrf_id_t vrf); vrf_id_t dplane_ctx_get_vrf(const struct zebra_dplane_ctx *ctx); +/* In some paths we have only a namespace id */ +void dplane_ctx_set_ns_id(struct zebra_dplane_ctx *ctx, ns_id_t nsid); +ns_id_t dplane_ctx_get_ns_id(const struct zebra_dplane_ctx *ctx); + bool dplane_ctx_is_from_notif(const struct zebra_dplane_ctx *ctx); void dplane_ctx_set_notif_provider(struct zebra_dplane_ctx *ctx, uint32_t id); @@ -441,17 +456,26 @@ dplane_ctx_get_pw_backup_nhg(const struct zebra_dplane_ctx *ctx); /* Accessors for interface information */ uint32_t dplane_ctx_get_intf_metric(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_intf_metric(struct zebra_dplane_ctx *ctx, uint32_t metric); /* Is interface addr p2p? */ bool dplane_ctx_intf_is_connected(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_intf_set_connected(struct zebra_dplane_ctx *ctx); bool dplane_ctx_intf_is_secondary(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_intf_set_secondary(struct zebra_dplane_ctx *ctx); bool dplane_ctx_intf_is_broadcast(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_intf_set_broadcast(struct zebra_dplane_ctx *ctx); const struct prefix *dplane_ctx_get_intf_addr( const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_intf_addr(struct zebra_dplane_ctx *ctx, + const struct prefix *p); bool dplane_ctx_intf_has_dest(const struct zebra_dplane_ctx *ctx); const struct prefix *dplane_ctx_get_intf_dest( const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_intf_dest(struct zebra_dplane_ctx *ctx, + const struct prefix *p); bool dplane_ctx_intf_has_label(const struct zebra_dplane_ctx *ctx); const char *dplane_ctx_get_intf_label(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_intf_label(struct zebra_dplane_ctx *ctx, const char *label); /* Accessors for MAC information */ vlanid_t dplane_ctx_mac_get_vlan(const struct zebra_dplane_ctx *ctx); diff --git a/zebra/zebra_evpn_neigh.c b/zebra/zebra_evpn_neigh.c index 08ff7011e2..af46ea6d7a 100644 --- a/zebra/zebra_evpn_neigh.c +++ b/zebra/zebra_evpn_neigh.c @@ -2113,12 +2113,12 @@ void zebra_evpn_neigh_remote_macip_add(struct zebra_evpn *zevpn, "sync->remote neigh vni %u ip %pIA mac %pEA seq %d f0x%x", n->zevpn->vni, &n->ip, &n->emac, seq, n->flags); - zebra_evpn_neigh_clear_sync_info(n); if (IS_ZEBRA_NEIGH_ACTIVE(n)) zebra_evpn_neigh_send_del_to_client( zevpn->vni, &n->ip, &n->emac, n->flags, n->state, false /*force*/); + zebra_evpn_neigh_clear_sync_info(n); } if (memcmp(&n->emac, &mac->macaddr, sizeof(struct ethaddr)) diff --git a/zebra/zebra_fpm.c b/zebra/zebra_fpm.c index 64366d6f4f..43958fdfde 100644 --- a/zebra/zebra_fpm.c +++ b/zebra/zebra_fpm.c @@ -292,6 +292,9 @@ static void zfpm_start_connect_timer(const char *reason); static void zfpm_start_stats_timer(void); static void zfpm_mac_info_del(struct fpm_mac_info_t *fpm_mac); +static const char ipv4_ll_buf[16] = "169.254.0.1"; +union g_addr ipv4ll_gateway; + /* * zfpm_thread_should_yield */ @@ -1993,6 +1996,10 @@ static int zfpm_init(struct thread_master *master) zfpm_stats_init(&zfpm_g->last_ivl_stats); zfpm_stats_init(&zfpm_g->cumulative_stats); + memset(&ipv4ll_gateway, 0, sizeof(ipv4ll_gateway)); + if (inet_pton(AF_INET, ipv4_ll_buf, &ipv4ll_gateway.ipv4) != 1) + zlog_warn("inet_pton failed for %s", ipv4_ll_buf); + install_node(&zebra_node); install_element(ENABLE_NODE, &show_zebra_fpm_stats_cmd); install_element(ENABLE_NODE, &clear_zebra_fpm_stats_cmd); diff --git a/zebra/zebra_fpm_netlink.c b/zebra/zebra_fpm_netlink.c index efbd078a52..168e36ac9b 100644 --- a/zebra/zebra_fpm_netlink.c +++ b/zebra/zebra_fpm_netlink.c @@ -189,7 +189,12 @@ static int netlink_route_info_add_nh(struct netlink_route_info *ri, if (nexthop->type == NEXTHOP_TYPE_IPV6 || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) { - nhi.gateway = &nexthop->gate; + /* Special handling for IPv4 route with IPv6 Link Local next hop + */ + if (ri->af == AF_INET) + nhi.gateway = &ipv4ll_gateway; + else + nhi.gateway = &nexthop->gate; } if (nexthop->type == NEXTHOP_TYPE_IFINDEX) { @@ -276,7 +281,7 @@ static int netlink_route_info_fill(struct netlink_route_info *ri, int cmd, ri->af = rib_dest_af(dest); if (zvrf && zvrf->zns) - ri->nlmsg_pid = zvrf->zns->netlink_dplane.snl.nl_pid; + ri->nlmsg_pid = zvrf->zns->netlink_dplane_out.snl.nl_pid; ri->nlmsg_type = cmd; ri->rtm_table = table_info->table_id; diff --git a/zebra/zebra_fpm_private.h b/zebra/zebra_fpm_private.h index c169ee8c22..13415c7e1d 100644 --- a/zebra/zebra_fpm_private.h +++ b/zebra/zebra_fpm_private.h @@ -97,6 +97,8 @@ extern int zfpm_netlink_encode_mac(struct fpm_mac_info_t *mac, char *in_buf, extern struct route_entry *zfpm_route_for_update(rib_dest_t *dest); +extern union g_addr ipv4ll_gateway; + #ifdef __cplusplus } #endif diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c index ce11264383..aa015992d5 100644 --- a/zebra/zebra_nhg.c +++ b/zebra/zebra_nhg.c @@ -1773,6 +1773,14 @@ static struct nexthop *nexthop_set_resolved(afi_t afi, nexthop_add_labels(resolved_hop, label_type, num_labels, labels); + if (nexthop->nh_srv6) { + nexthop_add_srv6_seg6local(resolved_hop, + nexthop->nh_srv6->seg6local_action, + &nexthop->nh_srv6->seg6local_ctx); + nexthop_add_srv6_seg6(resolved_hop, + &nexthop->nh_srv6->seg6_segs); + } + resolved_hop->rparent = nexthop; _nexthop_add(&nexthop->resolved, resolved_hop); @@ -2979,6 +2987,8 @@ void zebra_nhg_dplane_result(struct zebra_dplane_ctx *ctx) case DPLANE_OP_IPSET_ENTRY_DELETE: case DPLANE_OP_NEIGH_TABLE_UPDATE: case DPLANE_OP_GRE_SET: + case DPLANE_OP_INTF_ADDR_ADD: + case DPLANE_OP_INTF_ADDR_DEL: break; } diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c index 27b8a3ea47..8ae677fb22 100644 --- a/zebra/zebra_ns.c +++ b/zebra/zebra_ns.c @@ -123,6 +123,7 @@ int zebra_ns_enable(ns_id_t ns_id, void **info) zns->ns_id = ns_id; kernel_init(zns); + zebra_dplane_ns_enable(zns, true); interface_list(zns); route_read(zns); kernel_read_pbr_rules(zns); @@ -140,6 +141,8 @@ static int zebra_ns_disable_internal(struct zebra_ns *zns, bool complete) { route_table_finish(zns->if_table); + zebra_dplane_ns_enable(zns, false /*Disable*/); + kernel_terminate(zns, complete); table_manager_disable(zns->ns_id); diff --git a/zebra/zebra_ns.h b/zebra/zebra_ns.h index f7d1f40782..8237de7dde 100644 --- a/zebra/zebra_ns.h +++ b/zebra/zebra_ns.h @@ -52,7 +52,12 @@ struct zebra_ns { #ifdef HAVE_NETLINK struct nlsock netlink; /* kernel messages */ struct nlsock netlink_cmd; /* command channel */ - struct nlsock netlink_dplane; /* dataplane channel */ + + /* dplane system's channels: one for outgoing programming, + * for the FIB e.g., and one for incoming events from the OS. + */ + struct nlsock netlink_dplane_out; + struct nlsock netlink_dplane_in; struct thread *t_netlink; #endif diff --git a/zebra/zebra_ptm.c b/zebra/zebra_ptm.c index 7e9382518f..e17465b112 100644 --- a/zebra/zebra_ptm.c +++ b/zebra/zebra_ptm.c @@ -520,7 +520,13 @@ static int zebra_ptm_handle_bfd_msg(void *arg, void *in_ctxt, if (!strcmp(ZEBRA_PTM_INVALID_VRF, vrf_str) && ifp) { vrf_id = ifp->vrf_id; } else { - vrf_id = vrf_name_to_id(vrf_str); + struct vrf *pVrf; + + pVrf = vrf_lookup_by_name(vrf_str); + if (pVrf) + vrf_id = pVrf->vrf_id; + else + vrf_id = VRF_DEFAULT; } if (!strcmp(bfdst_str, ZEBRA_PTM_BFDSTATUS_DOWN_STR)) { diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 1fb4e5e6fc..24c51e485f 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -4209,6 +4209,11 @@ static int rib_process_dplane_results(struct thread *thread) zebra_pbr_dplane_result(ctx); break; + case DPLANE_OP_INTF_ADDR_ADD: + case DPLANE_OP_INTF_ADDR_DEL: + zebra_if_addr_update_ctx(ctx); + break; + /* Some op codes not handled here */ case DPLANE_OP_ADDR_INSTALL: case DPLANE_OP_ADDR_UNINSTALL: diff --git a/zebra/zebra_srv6.c b/zebra/zebra_srv6.c index b11331a180..219d047694 100644 --- a/zebra/zebra_srv6.c +++ b/zebra/zebra_srv6.c @@ -106,15 +106,60 @@ void zebra_srv6_locator_add(struct srv6_locator *locator) { struct zebra_srv6 *srv6 = zebra_srv6_get_default(); struct srv6_locator *tmp; + struct listnode *node; + struct zserv *client; tmp = zebra_srv6_locator_lookup(locator->name); if (!tmp) listnode_add(srv6->locators, locator); + + /* + * Notify new locator info to zclients. + * + * The srv6 locators and their prefixes are managed by zserv(zebra). + * And an actual configuration the srv6 sid in the srv6 locator is done + * by zclient(bgpd, isisd, etc). The configuration of each locator + * allocation and specify it by zserv and zclient should be + * asynchronous. For that, zclient should be received the event via + * ZAPI when a srv6 locator is added on zebra. + * Basically, in SRv6, adding/removing SRv6 locators is performed less + * frequently than adding rib entries, so a broad to all zclients will + * not degrade the overall performance of FRRouting. + */ + for (ALL_LIST_ELEMENTS_RO(zrouter.client_list, node, client)) + zsend_zebra_srv6_locator_add(client, locator); } void zebra_srv6_locator_delete(struct srv6_locator *locator) { + struct listnode *n; + struct srv6_locator_chunk *c; struct zebra_srv6 *srv6 = zebra_srv6_get_default(); + struct zserv *client; + + /* + * Notify deleted locator info to zclients if needed. + * + * zclient(bgpd,isisd,etc) allocates a sid from srv6 locator chunk and + * uses it for its own purpose. For example, in the case of BGP L3VPN, + * the SID assigned to vpn unicast rib will be given. + * And when the locator is deleted by zserv(zebra), those SIDs need to + * be withdrawn. The zclient must initiate the withdrawal of the SIDs + * by ZEBRA_SRV6_LOCATOR_DELETE, and this notification is sent to the + * owner of each chunk. + */ + for (ALL_LIST_ELEMENTS_RO((struct list *)locator->chunks, n, c)) { + if (c->proto == ZEBRA_ROUTE_SYSTEM) + continue; + client = zserv_find_client(c->proto, c->instance); + if (!client) { + zlog_warn( + "%s: Not found zclient(proto=%u, instance=%u).", + __func__, c->proto, c->instance); + continue; + } + zsend_zebra_srv6_locator_delete(client, locator); + } listnode_delete(srv6->locators, locator); } @@ -171,19 +216,7 @@ assign_srv6_locator_chunk(uint8_t proto, if (!loc) { zlog_info("%s: locator %s was not found", __func__, locator_name); - - loc = srv6_locator_alloc(locator_name); - if (!loc) { - zlog_info("%s: locator %s can't allocated", - __func__, locator_name); - return NULL; - } - - loc->status_up = false; - chunk = srv6_locator_chunk_alloc(); - chunk->proto = NO_PROTO; - listnode_add(loc->chunks, chunk); - zebra_srv6_locator_add(loc); + return NULL; } for (ALL_LIST_ELEMENTS_RO((struct list *)loc->chunks, node, chunk)) { diff --git a/zebra/zebra_srv6_vty.c b/zebra/zebra_srv6_vty.c index d2b91b6c07..cb1e6c4228 100644 --- a/zebra/zebra_srv6_vty.c +++ b/zebra/zebra_srv6_vty.c @@ -197,6 +197,21 @@ DEFUN_NOSH (srv6, return CMD_SUCCESS; } +DEFUN (no_srv6, + no_srv6_cmd, + "no srv6", + NO_STR + "Segment Routing SRv6\n") +{ + struct zebra_srv6 *srv6 = zebra_srv6_get_default(); + struct srv6_locator *locator; + struct listnode *node, *nnode; + + for (ALL_LIST_ELEMENTS(srv6->locators, node, nnode, locator)) + zebra_srv6_locator_delete(locator); + return CMD_SUCCESS; +} + DEFUN_NOSH (srv6_locators, srv6_locators_cmd, "locators", @@ -233,6 +248,23 @@ DEFUN_NOSH (srv6_locator, return CMD_SUCCESS; } +DEFUN (no_srv6_locator, + no_srv6_locator_cmd, + "no locator WORD", + NO_STR + "Segment Routing SRv6 locator\n" + "Specify locator-name\n") +{ + struct srv6_locator *locator = zebra_srv6_locator_lookup(argv[2]->arg); + if (!locator) { + vty_out(vty, "%% Can't find SRv6 locator\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + zebra_srv6_locator_delete(locator); + return CMD_SUCCESS; +} + DEFPY (locator_prefix, locator_prefix_cmd, "prefix X:X::X:X/M$prefix [func-bits (16-64)$func_bit_len]", @@ -348,8 +380,10 @@ void zebra_srv6_vty_init(void) /* Command for change node */ install_element(CONFIG_NODE, &segment_routing_cmd); install_element(SEGMENT_ROUTING_NODE, &srv6_cmd); + install_element(SEGMENT_ROUTING_NODE, &no_srv6_cmd); install_element(SRV6_NODE, &srv6_locators_cmd); install_element(SRV6_LOCS_NODE, &srv6_locator_cmd); + install_element(SRV6_LOCS_NODE, &no_srv6_locator_cmd); /* Command for configuration */ install_element(SRV6_LOC_NODE, &locator_prefix_cmd); diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index b204b30ca7..79087c5849 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -353,13 +353,13 @@ static void show_nexthop_detail_helper(struct vty *vty, break; } break; - default: - break; } - if ((re->vrf_id != nexthop->vrf_id) - && (nexthop->type != NEXTHOP_TYPE_BLACKHOLE)) - vty_out(vty, "(vrf %s)", vrf_id_to_name(nexthop->vrf_id)); + if (re->vrf_id != nexthop->vrf_id) { + struct vrf *vrf = vrf_lookup_by_id(nexthop->vrf_id); + + vty_out(vty, "(vrf %s)", VRF_LOGNAME(vrf)); + } if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE)) vty_out(vty, " (duplicate nexthop removed)"); @@ -603,12 +603,9 @@ static void show_route_nexthop_helper(struct vty *vty, break; } break; - default: - break; } - if ((re == NULL || (nexthop->vrf_id != re->vrf_id)) - && (nexthop->type != NEXTHOP_TYPE_BLACKHOLE)) + if ((re == NULL || (nexthop->vrf_id != re->vrf_id))) vty_out(vty, " (vrf %s)", vrf_id_to_name(nexthop->vrf_id)); if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) @@ -776,12 +773,9 @@ static void show_nexthop_json_helper(json_object *json_nexthop, break; } break; - default: - break; } - if ((nexthop->vrf_id != re->vrf_id) - && (nexthop->type != NEXTHOP_TYPE_BLACKHOLE)) + if (nexthop->vrf_id != re->vrf_id) json_object_string_add(json_nexthop, "vrf", vrf_id_to_name(nexthop->vrf_id)); @@ -2247,8 +2241,6 @@ static void show_ip_route_nht_dump(struct vty *vty, struct nexthop *nexthop, break; } break; - default: - break; } } |
