diff options
124 files changed, 1537 insertions, 300 deletions
diff --git a/bfdd/subdir.am b/bfdd/subdir.am index ed1d3962bf..9aa522f3f0 100644 --- a/bfdd/subdir.am +++ b/bfdd/subdir.am @@ -8,7 +8,7 @@ sbin_PROGRAMS += bfdd/bfdd dist_examples_DATA += bfdd/bfdd.conf.sample vtysh_scan += $(top_srcdir)/bfdd/bfdd_vty.c vtysh_scan += $(top_srcdir)/bfdd/bfdd_cli.c -man8 += $(MANBUILD)/bfdd.8 +man8 += $(MANBUILD)/frr-bfdd.8 endif bfdd_libbfd_a_SOURCES = \ diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index bdac2a8dcc..16de59b72c 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -2136,8 +2136,7 @@ static int bgp_attr_encap(uint8_t type, struct peer *peer, /* IN */ * 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 */ -static bgp_attr_parse_ret_t bgp_attr_psid_sub(int32_t type, - int32_t length, +static bgp_attr_parse_ret_t bgp_attr_psid_sub(uint8_t type, uint16_t length, struct bgp_attr_parser_args *args, struct bgp_nlri *mp_update) { @@ -2150,11 +2149,12 @@ static bgp_attr_parse_ret_t bgp_attr_psid_sub(int32_t type, int srgb_count; if (type == BGP_PREFIX_SID_LABEL_INDEX) { - if (length != BGP_PREFIX_SID_LABEL_INDEX_LENGTH) { - flog_err( - EC_BGP_ATTR_LEN, - "Prefix SID label index length is %d instead of %d", - length, BGP_PREFIX_SID_LABEL_INDEX_LENGTH); + if (STREAM_READABLE(peer->curr) < length + || length != BGP_PREFIX_SID_LABEL_INDEX_LENGTH) { + flog_err(EC_BGP_ATTR_LEN, + "Prefix SID label index length is %" PRIu16 + " instead of %u", + length, BGP_PREFIX_SID_LABEL_INDEX_LENGTH); return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, args->total); @@ -2186,9 +2186,11 @@ static bgp_attr_parse_ret_t bgp_attr_psid_sub(int32_t type, /* Placeholder code for the IPv6 SID type */ else if (type == BGP_PREFIX_SID_IPV6) { - if (length != BGP_PREFIX_SID_IPV6_LENGTH) { + if (STREAM_READABLE(peer->curr) < length + || length != BGP_PREFIX_SID_IPV6_LENGTH) { flog_err(EC_BGP_ATTR_LEN, - "Prefix SID IPv6 length is %d instead of %d", + "Prefix SID IPv6 length is %" PRIu16 + " instead of %u", length, BGP_PREFIX_SID_IPV6_LENGTH); return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, @@ -2204,15 +2206,54 @@ static bgp_attr_parse_ret_t bgp_attr_psid_sub(int32_t type, /* Placeholder code for the Originator SRGB type */ else if (type == BGP_PREFIX_SID_ORIGINATOR_SRGB) { - /* Ignore flags */ - stream_getw(peer->curr); + /* + * ietf-idr-bgp-prefix-sid-05: + * Length is the total length of the value portion of the + * TLV: 2 + multiple of 6. + * + * peer->curr stream readp should be at the beginning of the 16 + * bit flag field at this point in the code. + */ - length -= 2; + /* + * Check that the TLV length field is sane: at least 2 bytes of + * flag, and at least 1 SRGB (these are 6 bytes each) + */ + if (length < (2 + BGP_PREFIX_SID_ORIGINATOR_SRGB_LENGTH)) { + flog_err( + EC_BGP_ATTR_LEN, + "Prefix SID Originator SRGB length field claims length of %" PRIu16 " bytes, but the minimum for this TLV type is %u", + length, + 2 + BGP_PREFIX_SID_ORIGINATOR_SRGB_LENGTH); + return bgp_attr_malformed( + args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, + args->total); + } + /* + * Check that we actually have at least as much data as + * specified by the length field + */ + if (STREAM_READABLE(peer->curr) < length) { + flog_err(EC_BGP_ATTR_LEN, + "Prefix SID Originator SRGB specifies length %" PRIu16 ", but only %zu bytes remain", + length, STREAM_READABLE(peer->curr)); + return bgp_attr_malformed( + args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, + args->total); + } + + /* + * Check that the portion of the TLV containing the sequence of + * SRGBs corresponds to a multiple of the SRGB size; to get + * that length, we skip the 16 bit flags field + */ + stream_getw(peer->curr); + length -= 2; if (length % BGP_PREFIX_SID_ORIGINATOR_SRGB_LENGTH) { flog_err( EC_BGP_ATTR_LEN, - "Prefix SID Originator SRGB length is %d, it must be a multiple of %d ", + "Prefix SID Originator SRGB length field claims attribute SRGB sequence section is %" PRIu16 "bytes, but it must be a multiple of %u", length, BGP_PREFIX_SID_ORIGINATOR_SRGB_LENGTH); return bgp_attr_malformed( args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, @@ -2234,12 +2275,24 @@ static bgp_attr_parse_ret_t bgp_attr_psid_sub(int32_t type, */ else if (type == BGP_PREFIX_SID_SRV6_L3_SERVICE || type == BGP_PREFIX_SID_SRV6_L2_SERVICE) { + + if (STREAM_READABLE(peer->curr) < length) { + flog_err( + EC_BGP_ATTR_LEN, + "Prefix SID SRv6 length is %" PRIu16 + " - too long, only %zu remaining in this UPDATE", + length, STREAM_READABLE(peer->curr)); + return bgp_attr_malformed( + args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, + args->total); + } + if (bgp_debug_update(peer, NULL, NULL, 1)) zlog_debug( "%s attr Prefix-SID sub-type=%u is not supported, skipped", peer->host, type); - for (int i = 0; i < length; i++) - stream_getc(peer->curr); + + stream_forward_getp(peer->curr, length); } return BGP_ATTR_PARSE_PROCEED; @@ -2248,9 +2301,8 @@ static bgp_attr_parse_ret_t bgp_attr_psid_sub(int32_t type, /* Prefix SID attribute * draft-ietf-idr-bgp-prefix-sid-05 */ -bgp_attr_parse_ret_t -bgp_attr_prefix_sid(int32_t tlength, struct bgp_attr_parser_args *args, - struct bgp_nlri *mp_update) +bgp_attr_parse_ret_t bgp_attr_prefix_sid(struct bgp_attr_parser_args *args, + struct bgp_nlri *mp_update) { struct peer *const peer = args->peer; struct attr *const attr = args->attr; @@ -2258,31 +2310,40 @@ bgp_attr_prefix_sid(int32_t tlength, struct bgp_attr_parser_args *args, attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_PREFIX_SID); - while (tlength) { - int32_t type, length; + uint8_t type; + uint16_t length; + size_t headersz = sizeof(type) + sizeof(length); - type = stream_getc(peer->curr); - length = stream_getw(peer->curr); + while (STREAM_READABLE(peer->curr) > 0) { - ret = bgp_attr_psid_sub(type, length, args, mp_update); + if (STREAM_READABLE(peer->curr) < headersz) { + flog_err( + EC_BGP_ATTR_LEN, + "Malformed Prefix SID 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); + } - if (ret != BGP_ATTR_PARSE_PROCEED) - return ret; - /* - * Subtract length + the T and the L - * since length is the Vector portion - */ - tlength -= length + 3; + type = stream_getc(peer->curr); + length = stream_getw(peer->curr); - if (tlength < 0) { + if (STREAM_READABLE(peer->curr) < length) { flog_err( EC_BGP_ATTR_LEN, - "Prefix SID internal length %d causes us to read beyond the total Prefix SID length", - length); + "Malformed Prefix SID attribute - insufficient data (need %" PRIu8 + " for attribute body, have %zu remaining in UPDATE)", + length, STREAM_READABLE(peer->curr)); return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, args->total); } + + ret = bgp_attr_psid_sub(type, length, args, mp_update); + + if (ret != BGP_ATTR_PARSE_PROCEED) + return ret; } return BGP_ATTR_PARSE_PROCEED; @@ -2678,8 +2739,7 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr, startp); break; case BGP_ATTR_PREFIX_SID: - ret = bgp_attr_prefix_sid(length, - &attr_args, mp_update); + ret = bgp_attr_prefix_sid(&attr_args, mp_update); break; case BGP_ATTR_PMSI_TUNNEL: ret = bgp_attr_pmsi_tunnel(&attr_args); diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h index 40e87e190a..8bd527c0ff 100644 --- a/bgpd/bgp_attr.h +++ b/bgpd/bgp_attr.h @@ -319,7 +319,7 @@ extern int bgp_mp_reach_parse(struct bgp_attr_parser_args *args, extern int bgp_mp_unreach_parse(struct bgp_attr_parser_args *args, struct bgp_nlri *); extern bgp_attr_parse_ret_t -bgp_attr_prefix_sid(int32_t tlength, struct bgp_attr_parser_args *args, +bgp_attr_prefix_sid(struct bgp_attr_parser_args *args, struct bgp_nlri *mp_update); extern struct bgp_attr_encap_subtlv * diff --git a/bgpd/bgp_open.c b/bgpd/bgp_open.c index f17bc7b8c0..23b893c1c8 100644 --- a/bgpd/bgp_open.c +++ b/bgpd/bgp_open.c @@ -747,6 +747,12 @@ static int bgp_capability_hostname(struct peer *peer, if (len) { str[len] = '\0'; + + if (peer->domainname != NULL) { + XFREE(MTYPE_BGP_PEER_HOST, peer->domainname); + peer->domainname = NULL; + } + peer->domainname = XSTRDUP(MTYPE_BGP_PEER_HOST, str); } diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c index 83194e010a..14f5fefb20 100644 --- a/bgpd/bgp_pbr.c +++ b/bgpd/bgp_pbr.c @@ -1725,7 +1725,7 @@ static void bgp_pbr_policyroute_remove_from_zebra_unit( temp.type = IPSET_NET_NET; } if (bpf->vrf_id == VRF_UNKNOWN) /* XXX case BGP destroy */ - temp.vrf_id = 0; + temp.vrf_id = VRF_DEFAULT; else temp.vrf_id = bpf->vrf_id; bpme = &temp2; diff --git a/bgpd/bgp_updgrp.c b/bgpd/bgp_updgrp.c index 5f3f5cde9a..5b3eb2c719 100644 --- a/bgpd/bgp_updgrp.c +++ b/bgpd/bgp_updgrp.c @@ -143,6 +143,7 @@ static void conf_copy(struct peer *dst, struct peer *src, afi_t afi, dst->v_routeadv = src->v_routeadv; dst->flags = src->flags; dst->af_flags[afi][safi] = src->af_flags[afi][safi]; + dst->pmax_out[afi][safi] = src->pmax_out[afi][safi]; XFREE(MTYPE_BGP_PEER_HOST, dst->host); dst->host = XSTRDUP(MTYPE_BGP_PEER_HOST, src->host); diff --git a/bgpd/bgp_updgrp.h b/bgpd/bgp_updgrp.h index bb547454f2..fe654bb3e3 100644 --- a/bgpd/bgp_updgrp.h +++ b/bgpd/bgp_updgrp.h @@ -208,7 +208,7 @@ struct update_subgroup { struct bgp_synchronize *sync; /* send prefix count */ - unsigned long scount; + uint32_t scount; /* announcement attribute hash */ struct hash *hash; diff --git a/bgpd/bgp_updgrp_adv.c b/bgpd/bgp_updgrp_adv.c index 49e87adc3c..26dda8ebda 100644 --- a/bgpd/bgp_updgrp_adv.c +++ b/bgpd/bgp_updgrp_adv.c @@ -64,6 +64,12 @@ static int bgp_adj_out_compare(const struct bgp_adj_out *o1, if (o1->subgroup > o2->subgroup) return 1; + if (o1->addpath_tx_id < o2->addpath_tx_id) + return -1; + + if (o1->addpath_tx_id > o2->addpath_tx_id) + return 1; + return 0; } RB_GENERATE(bgp_adj_out_rb, bgp_adj_out, adj_entry, bgp_adj_out_compare); @@ -72,32 +78,17 @@ static inline struct bgp_adj_out *adj_lookup(struct bgp_node *rn, struct update_subgroup *subgrp, uint32_t addpath_tx_id) { - struct bgp_adj_out *adj, lookup; - struct peer *peer; - afi_t afi; - safi_t safi; - int addpath_capable; + struct bgp_adj_out lookup; if (!rn || !subgrp) return NULL; - peer = SUBGRP_PEER(subgrp); - afi = SUBGRP_AFI(subgrp); - safi = SUBGRP_SAFI(subgrp); - addpath_capable = bgp_addpath_encode_tx(peer, afi, safi); - /* update-groups that do not support addpath will pass 0 for - * addpath_tx_id so do not both matching against it */ + * addpath_tx_id. */ lookup.subgroup = subgrp; - adj = RB_FIND(bgp_adj_out_rb, &rn->adj_out, &lookup); - if (adj) { - if (addpath_capable) { - if (adj->addpath_tx_id == addpath_tx_id) - return adj; - } else - return adj; - } - return NULL; + lookup.addpath_tx_id = addpath_tx_id; + + return RB_FIND(bgp_adj_out_rb, &rn->adj_out, &lookup); } static void adj_free(struct bgp_adj_out *adj) @@ -403,13 +394,14 @@ struct bgp_adj_out *bgp_adj_out_alloc(struct update_subgroup *subgrp, adj = XCALLOC(MTYPE_BGP_ADJ_OUT, sizeof(struct bgp_adj_out)); adj->subgroup = subgrp; + adj->addpath_tx_id = addpath_tx_id; + if (rn) { RB_INSERT(bgp_adj_out_rb, &rn->adj_out, adj); bgp_lock_node(rn); adj->rn = rn; } - adj->addpath_tx_id = addpath_tx_id; TAILQ_INSERT_TAIL(&(subgrp->adjq), adj, subgrp_adj_train); SUBGRP_INCR_STAT(subgrp, adj_count); return adj; @@ -710,12 +702,11 @@ void subgroup_default_originate(struct update_subgroup *subgrp, int withdraw) { struct bgp *bgp; struct attr attr; + struct attr *new_attr = &attr; struct aspath *aspath; - struct bgp_path_info tmp_info; struct prefix p; struct peer *from; struct bgp_node *rn; - struct bgp_path_info *ri; struct peer *peer; route_map_result_t ret = RMAP_DENYMATCH; afi_t afi; @@ -755,37 +746,33 @@ void subgroup_default_originate(struct update_subgroup *subgrp, int withdraw) } if (peer->default_rmap[afi][safi].name) { + struct attr attr_tmp = attr; + struct bgp_path_info bpi_rmap = {0}; + + bpi_rmap.peer = bgp->peer_self; + bpi_rmap.attr = &attr_tmp; + SET_FLAG(bgp->peer_self->rmap_type, PEER_RMAP_TYPE_DEFAULT); + + /* Iterate over the RIB to see if we can announce + * the default route. We announce the default + * route only if route-map has a match. + */ for (rn = bgp_table_top(bgp->rib[afi][safi]); rn; rn = bgp_route_next(rn)) { - for (ri = bgp_node_get_bgp_path_info(rn); - ri; ri = ri->next) { - struct attr dummy_attr; - - /* Provide dummy so the route-map can't modify - * the attributes */ - dummy_attr = *ri->attr; - tmp_info.peer = ri->peer; - tmp_info.attr = &dummy_attr; - - ret = route_map_apply( - peer->default_rmap[afi][safi].map, - &rn->p, RMAP_BGP, &tmp_info); - - /* The route map might have set attributes. If - * we don't flush them - * here, they will be leaked. */ - bgp_attr_flush(&dummy_attr); - if (ret != RMAP_DENYMATCH) - break; - } + ret = route_map_apply(peer->default_rmap[afi][safi].map, + &rn->p, RMAP_BGP, &bpi_rmap); + if (ret != RMAP_DENYMATCH) break; } bgp->peer_self->rmap_type = 0; + new_attr = bgp_attr_intern(&attr_tmp); - if (ret == RMAP_DENYMATCH) + if (ret == RMAP_DENYMATCH) { + bgp_attr_flush(&attr_tmp); withdraw = 1; + } } if (withdraw) { @@ -797,12 +784,12 @@ void subgroup_default_originate(struct update_subgroup *subgrp, int withdraw) SUBGRP_STATUS_DEFAULT_ORIGINATE)) { if (bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_SHUTDOWN)) { - bgp_attr_add_gshut_community(&attr); + bgp_attr_add_gshut_community(new_attr); } SET_FLAG(subgrp->sflags, SUBGRP_STATUS_DEFAULT_ORIGINATE); - subgroup_default_update_packet(subgrp, &attr, from); + subgroup_default_update_packet(subgrp, new_attr, from); /* The 'neighbor x.x.x.x default-originate' default will * act as an diff --git a/bgpd/bgp_updgrp_packet.c b/bgpd/bgp_updgrp_packet.c index 9329c8d892..39eb065288 100644 --- a/bgpd/bgp_updgrp_packet.c +++ b/bgpd/bgp_updgrp_packet.c @@ -744,6 +744,22 @@ struct bpacket *subgroup_update_packet(struct update_subgroup *subgrp) addpath_tx_id = adj->addpath_tx_id; path = adv->pathi; + /* Check if we need to add a prefix to the packet if + * maximum-prefix-out is set for the peer. + */ + if (CHECK_FLAG(peer->af_flags[afi][safi], + PEER_FLAG_MAX_PREFIX_OUT) + && subgrp->scount >= peer->pmax_out[afi][safi]) { + if (BGP_DEBUG(update, UPDATE_OUT) + || BGP_DEBUG(update, UPDATE_PREFIX)) { + zlog_debug( + "%s reached maximum prefix to be send (%" PRIu32 + ")", + peer->host, peer->pmax_out[afi][safi]); + } + goto next; + } + space_remaining = STREAM_CONCAT_REMAIN(s, snlri, STREAM_SIZE(s)) - BGP_MAX_PACKET_SIZE_OVERFLOW; space_needed = @@ -894,7 +910,7 @@ struct bpacket *subgroup_update_packet(struct update_subgroup *subgrp) subgrp->scount++; adj->attr = bgp_attr_intern(adv->baa->attr); - +next: adv = bgp_advertise_clean_subgroup(subgrp, adj); } diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 53d9732956..9dc6549d9c 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -6058,6 +6058,56 @@ static int peer_maximum_prefix_unset_vty(struct vty *vty, const char *ip_str, return bgp_vty_return(vty, ret); } +/* Maximum number of prefix to be sent to the neighbor. */ +DEFUN(neighbor_maximum_prefix_out, + neighbor_maximum_prefix_out_cmd, + "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix-out (1-4294967295)", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Maximum number of prefixes to be sent to this peer\n" + "Maximum no. of prefix limit\n") +{ + int idx_peer = 1; + int idx_number = 3; + struct peer *peer; + uint32_t max; + afi_t afi = bgp_node_afi(vty); + safi_t safi = bgp_node_safi(vty); + + peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg); + if (!peer) + return CMD_WARNING_CONFIG_FAILED; + + max = strtoul(argv[idx_number]->arg, NULL, 10); + + SET_FLAG(peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_OUT); + peer->pmax_out[afi][safi] = max; + + return CMD_SUCCESS; +} + +DEFUN(no_neighbor_maximum_prefix_out, + no_neighbor_maximum_prefix_out_cmd, + "no neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix-out", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Maximum number of prefixes to be sent to this peer\n") +{ + int idx_peer = 2; + struct peer *peer; + afi_t afi = bgp_node_afi(vty); + safi_t safi = bgp_node_safi(vty); + + peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg); + if (!peer) + return CMD_WARNING_CONFIG_FAILED; + + peer->pmax_out[afi][safi] = 0; + + return CMD_SUCCESS; +} + /* Maximum number of prefix configuration. prefix count is different for each peer configuration. So this configuration can be set for each peer configuration. */ @@ -9191,6 +9241,11 @@ static void bgp_show_peer_afi(struct vty *vty, struct peer *p, afi_t afi, (PAF_SUBGRP(paf))->scount); /* Maximum prefix */ + if (CHECK_FLAG(p->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_OUT)) + json_object_int_add(json_addr, "prefixOutAllowedMax", + p->pmax_out[afi][safi]); + + /* Maximum prefix */ if (CHECK_FLAG(p->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX)) { json_object_int_add(json_addr, "prefixAllowedMax", p->pmax[afi][safi]); @@ -9476,6 +9531,13 @@ static void bgp_show_peer_afi(struct vty *vty, struct peer *p, afi_t afi, vty_out(vty, " %" PRIu32 " accepted prefixes\n", p->pcount[afi][safi]); + /* maximum-prefix-out */ + if (CHECK_FLAG(p->af_flags[afi][safi], + PEER_FLAG_MAX_PREFIX_OUT)) + vty_out(vty, + " Maximum allowed prefixes sent %" PRIu32 "\n", + p->pmax_out[afi][safi]); + /* Maximum prefix */ if (CHECK_FLAG(p->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX)) { vty_out(vty, @@ -13579,6 +13641,11 @@ static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp, vty_out(vty, "\n"); } + /* maximum-prefix-out */ + if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_MAX_PREFIX_OUT)) + vty_out(vty, " neighbor %s maximum-prefix-out %" PRIu32 "\n", + addr, peer->pmax_out[afi][safi]); + /* Route server client. */ if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_RSERVER_CLIENT)) { @@ -15115,6 +15182,26 @@ void bgp_vty_init(void) install_element(BGP_VPNV6_NODE, &neighbor_unsuppress_map_cmd); install_element(BGP_VPNV6_NODE, &no_neighbor_unsuppress_map_cmd); + /* neighbor maximum-prefix-out commands. */ + install_element(BGP_NODE, &neighbor_maximum_prefix_out_cmd); + install_element(BGP_NODE, &no_neighbor_maximum_prefix_out_cmd); + install_element(BGP_IPV4_NODE, &neighbor_maximum_prefix_out_cmd); + install_element(BGP_IPV4_NODE, &no_neighbor_maximum_prefix_out_cmd); + install_element(BGP_IPV4M_NODE, &neighbor_maximum_prefix_out_cmd); + install_element(BGP_IPV4M_NODE, &no_neighbor_maximum_prefix_out_cmd); + install_element(BGP_IPV4L_NODE, &neighbor_maximum_prefix_out_cmd); + install_element(BGP_IPV4L_NODE, &no_neighbor_maximum_prefix_out_cmd); + install_element(BGP_IPV6_NODE, &neighbor_maximum_prefix_out_cmd); + install_element(BGP_IPV6_NODE, &no_neighbor_maximum_prefix_out_cmd); + install_element(BGP_IPV6M_NODE, &neighbor_maximum_prefix_out_cmd); + install_element(BGP_IPV6M_NODE, &no_neighbor_maximum_prefix_out_cmd); + install_element(BGP_IPV6L_NODE, &neighbor_maximum_prefix_out_cmd); + install_element(BGP_IPV6L_NODE, &no_neighbor_maximum_prefix_out_cmd); + install_element(BGP_VPNV4_NODE, &neighbor_maximum_prefix_out_cmd); + install_element(BGP_VPNV4_NODE, &no_neighbor_maximum_prefix_out_cmd); + install_element(BGP_VPNV6_NODE, &neighbor_maximum_prefix_out_cmd); + install_element(BGP_VPNV6_NODE, &no_neighbor_maximum_prefix_out_cmd); + /* "neighbor maximum-prefix" commands. */ install_element(BGP_NODE, &neighbor_maximum_prefix_hidden_cmd); install_element(BGP_NODE, diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 069c53d7df..c99ddaf0a6 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -1384,6 +1384,8 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, if (bgp_debug_zebra(p)) { char prefix_buf[PREFIX_STRLEN]; char nh_buf[INET6_ADDRSTRLEN]; + char eth_buf[ETHER_ADDR_STRLEN + 7] = {'\0'}; + char buf1[ETHER_ADDR_STRLEN]; char label_buf[20]; int i; @@ -1421,13 +1423,19 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, } label_buf[0] = '\0'; + eth_buf[0] = '\0'; if (has_valid_label && !CHECK_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE)) - sprintf(label_buf, "label %u", - api_nh->labels[0]); - zlog_debug(" nhop [%d]: %s if %u VRF %u %s", + snprintf(label_buf, sizeof(label_buf), + "label %u", api_nh->labels[0]); + if (CHECK_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE) + && !is_zero_mac(&api_nh->rmac)) + snprintf(eth_buf, sizeof(eth_buf), " RMAC %s", + prefix_mac2str(&api_nh->rmac, + buf1, sizeof(buf1))); + zlog_debug(" nhop [%d]: %s if %u VRF %u %s %s", i + 1, nh_buf, api_nh->ifindex, - api_nh->vrf_id, label_buf); + api_nh->vrf_id, label_buf, eth_buf); } } diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 7d81579009..4c4787ed5b 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -988,6 +988,7 @@ struct peer { #define PEER_FLAG_WEIGHT (1 << 24) /* weight */ #define PEER_FLAG_ALLOWAS_IN_ORIGIN (1 << 25) /* allowas-in origin */ #define PEER_FLAG_SEND_LARGE_COMMUNITY (1 << 26) /* Send large Communities */ +#define PEER_FLAG_MAX_PREFIX_OUT (1 << 27) /* outgoing maximum prefix */ enum bgp_addpath_strat addpath_type[AFI_MAX][SAFI_MAX]; @@ -1120,9 +1121,6 @@ struct peer { /* timestamp when the last msg was written */ _Atomic time_t last_update; - /* Send prefix count. */ - unsigned long scount[AFI_MAX][SAFI_MAX]; - /* Notify data. */ struct bgp_notify notify; @@ -1173,6 +1171,9 @@ struct peer { uint16_t pmax_restart[AFI_MAX][SAFI_MAX]; #define MAXIMUM_PREFIX_THRESHOLD_DEFAULT 75 + /* Send prefix count. */ + uint32_t pmax_out[AFI_MAX][SAFI_MAX]; + /* allowas-in. */ char allowas_in[AFI_MAX][SAFI_MAX]; diff --git a/bgpd/subdir.am b/bgpd/subdir.am index 203cf779b9..ff15248a98 100644 --- a/bgpd/subdir.am +++ b/bgpd/subdir.am @@ -46,7 +46,7 @@ endif if BGP_BMP module_LTLIBRARIES += bgpd/bgpd_bmp.la endif -man8 += $(MANBUILD)/bgpd.8 +man8 += $(MANBUILD)/frr-bgpd.8 endif bgpd_libbgp_a_SOURCES = \ diff --git a/configure.ac b/configure.ac index 0694e3ed2c..c8371f304e 100755 --- a/configure.ac +++ b/configure.ac @@ -7,7 +7,7 @@ ## AC_PREREQ([2.60]) -AC_INIT([frr], [7.3-dev], [https://github.com/frrouting/frr/issues]) +AC_INIT([frr], [7.4-dev], [https://github.com/frrouting/frr/issues]) PACKAGE_URL="https://frrouting.org/" AC_SUBST([PACKAGE_URL]) PACKAGE_FULLNAME="FRRouting" @@ -30,7 +30,7 @@ build_clippy="true" dnl case 1: external clippy if test -n "$with_clippy" -a "$with_clippy" != "no" -a "$with_clippy" != "yes"; then - if test "$enable_clippy_only" == "yes"; then + if test "$enable_clippy_only" = "yes"; then AC_MSG_ERROR([--enable-clippy-only does not make sense with --with-clippy]) fi @@ -249,7 +249,18 @@ if test "x${enable_gcov}" = "xyes"; then fi LDFLAGS="${LDFLAGS} -lgcov" -elif test "x${enable_dev_build}" = "xyes"; then +fi + +if test "x${enable_clang_coverage}" = "xyes"; then + AC_C_FLAG([-fprofile-instr-generate], [ + AC_MSG_ERROR([$CC does not support -fprofile-instr-generate.]) + ]) + AC_C_FLAG([-fcoverage-mapping], [ + AC_MSG_ERROR([$CC does not support -fcoverage-mapping.]) + ]) +fi + +if test "x${enable_dev_build}" = "xyes"; then AC_DEFINE([DEV_BUILD], [1], [Build for development]) if test "z$orig_cflags" = "z"; then AC_C_FLAG([-g3]) @@ -574,7 +585,9 @@ AC_ARG_ENABLE([clippy-only], AC_ARG_ENABLE([numeric_version], AS_HELP_STRING([--enable-numeric-version], [Only numeric digits allowed in version (for Alpine)])) AC_ARG_ENABLE([gcov], - AS_HELP_STRING([--enable-gcov], [Add code coverage information])) + AS_HELP_STRING([--enable-gcov], [Collect coverage information with gcov])) +AC_ARG_ENABLE([clang_coverage], + AS_HELP_STRING([--enable-clang-coverage], [Collect coverage information with Clang Coverage])) AC_ARG_ENABLE([bfdd], AS_HELP_STRING([--disable-bfdd], [do not build bfdd])) AC_ARG_ENABLE([address-sanitizer], @@ -2100,6 +2113,19 @@ if test x"${enable_backtrace}" != x"no" ; then AC_DEFINE([HAVE_LIBUNWIND], [1], [libunwind]) backtrace_ok=yes ], [ + true + ]) + + if test "$backtrace_ok" = "no"; then + AC_CHECK_HEADER([unwind.h], [ + AC_SEARCH_LIBS([unw_getcontext], [unwind], [ + AC_DEFINE([HAVE_LIBUNWIND], [1], [libunwind]) + backtrace_ok=yes + ]) + ]) + fi + + if test "$backtrace_ok" = "no"; then case "$host_os" in sunos* | solaris2*) AC_CHECK_FUNCS([printstack], [ @@ -2116,7 +2142,7 @@ if test x"${enable_backtrace}" != x"no" ; then ],, [-lm]) ]) fi - ]) + fi if test x"${enable_backtrace}" = x"yes" -a x"${backtrace_ok}" = x"no"; then dnl user explicitly requested backtrace but we failed to find support diff --git a/debian/frr.logrotate b/debian/frr.logrotate index 1dc9122ac4..a6b2b22f56 100644 --- a/debian/frr.logrotate +++ b/debian/frr.logrotate @@ -17,7 +17,7 @@ # open, as well as the daemons, so always signal the daemons. # It's safe, a NOP if (only) syslog is being used. for i in babeld bgpd eigrpd isisd ldpd nhrpd ospf6d ospfd \ - pimd ripd ripngd zebra staticd fabricd; do + pimd ripd ripngd zebra pbrd staticd bfdd fabricd vrrpd; do if [ -e /var/run/frr/$i.pid ] ; then pids="$pids $(cat /var/run/frr/$i.pid)" fi diff --git a/debian/frr.manpages b/debian/frr.manpages index f5aa972304..5075fd763d 100644 --- a/debian/frr.manpages +++ b/debian/frr.manpages @@ -1,16 +1,16 @@ +doc/manpages/_build/man/frr-bgpd.8 +doc/manpages/_build/man/frr-eigrpd.8 +doc/manpages/_build/man/frr-fabricd.8 +doc/manpages/_build/man/frr-isisd.8 +doc/manpages/_build/man/frr-ldpd.8 +doc/manpages/_build/man/frr-nhrpd.8 +doc/manpages/_build/man/frr-ospf6d.8 +doc/manpages/_build/man/frr-ospfd.8 +doc/manpages/_build/man/frr-pimd.8 +doc/manpages/_build/man/frr-ripd.8 +doc/manpages/_build/man/frr-ripngd.8 +doc/manpages/_build/man/frr-watchfrr.8 +doc/manpages/_build/man/frr-zebra.8 doc/manpages/_build/man/frr.1 -doc/manpages/_build/man/bgpd.8 -doc/manpages/_build/man/pimd.8 -doc/manpages/_build/man/eigrpd.8 -doc/manpages/_build/man/ldpd.8 -doc/manpages/_build/man/nhrpd.8 -doc/manpages/_build/man/ospf6d.8 -doc/manpages/_build/man/ospfd.8 -doc/manpages/_build/man/ripd.8 -doc/manpages/_build/man/ripngd.8 -doc/manpages/_build/man/vtysh.1 -doc/manpages/_build/man/zebra.8 -doc/manpages/_build/man/isisd.8 -doc/manpages/_build/man/watchfrr.8 doc/manpages/_build/man/mtracebis.8 -doc/manpages/_build/man/fabricd.8 +doc/manpages/_build/man/vtysh.1 diff --git a/doc/developer/building-frr-for-centos6.rst b/doc/developer/building-frr-for-centos6.rst index 3d9edbe3a1..04c6b922ce 100644 --- a/doc/developer/building-frr-for-centos6.rst +++ b/doc/developer/building-frr-for-centos6.rst @@ -213,7 +213,7 @@ Install daemon config file .. code-block:: shell - sudo install -p -m 644 redhat/daemons /etc/frr/ + sudo install -p -m 644 tools/etc/frr/daemons /etc/frr/ sudo chown frr:frr /etc/frr/daemons Edit /etc/frr/daemons as needed to select the required daemons @@ -241,12 +241,12 @@ Load the modified sysctl's on the system: sudo sysctl -p /etc/sysctl.d/90-routing-sysctl.conf -Add init.d startup files -^^^^^^^^^^^^^^^^^^^^^^^^ +Add init.d startup file +^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: shell - sudo install -p -m 755 redhat/frr.init /etc/init.d/frr + sudo install -p -m 755 tools/frr /etc/init.d/frr sudo chkconfig --add frr Enable FRR daemon at startup diff --git a/doc/developer/building-frr-for-centos7.rst b/doc/developer/building-frr-for-centos7.rst index cd90d41ffb..eb97150d67 100644 --- a/doc/developer/building-frr-for-centos7.rst +++ b/doc/developer/building-frr-for-centos7.rst @@ -104,7 +104,7 @@ Install daemon config file :: - sudo install -p -m 644 redhat/daemons /etc/frr/ + sudo install -p -m 644 tools/etc/frr/daemons /etc/frr/ sudo chown frr:frr /etc/frr/daemons Edit /etc/frr/daemons as needed to select the required daemons @@ -133,13 +133,12 @@ Load the modified sysctl's on the system: sudo sysctl -p /etc/sysctl.d/90-routing-sysctl.conf -Install frr Service and redhat init files -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Install frr Service +^^^^^^^^^^^^^^^^^^^ :: - sudo install -p -m 644 redhat/frr.service /usr/lib/systemd/system/frr.service - sudo install -p -m 755 redhat/frr.init /usr/lib/frr/frr + sudo install -p -m 644 tools/frr.service /usr/lib/systemd/system/frr.service Register the systemd files ^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/doc/developer/building-frr-for-centos8.rst b/doc/developer/building-frr-for-centos8.rst index 7751482b19..75beb53378 100644 --- a/doc/developer/building-frr-for-centos8.rst +++ b/doc/developer/building-frr-for-centos8.rst @@ -97,7 +97,7 @@ Install daemon config file :: - sudo install -p -m 644 redhat/daemons /etc/frr/ + sudo install -p -m 644 tools/etc/frr/daemons /etc/frr/ sudo chown frr:frr /etc/frr/daemons Edit /etc/frr/daemons as needed to select the required daemons @@ -126,13 +126,12 @@ Load the modified sysctl's on the system: sudo sysctl -p /etc/sysctl.d/90-routing-sysctl.conf -Install frr Service and redhat init files -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Install frr Service +^^^^^^^^^^^^^^^^^^^ :: - sudo install -p -m 644 redhat/frr.service /usr/lib/systemd/system/frr.service - sudo install -p -m 755 redhat/frr.init /usr/lib/frr/frr + sudo install -p -m 644 tools/frr.service /usr/lib/systemd/system/frr.service Register the systemd files ^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/doc/developer/building-frr-for-fedora.rst b/doc/developer/building-frr-for-fedora.rst index d8405eb351..4ab59490fd 100644 --- a/doc/developer/building-frr-for-fedora.rst +++ b/doc/developer/building-frr-for-fedora.rst @@ -110,13 +110,12 @@ And load the kernel modules on the running system: sudo systemctl stop firewalld.service sudo iptables -F -Install service files -^^^^^^^^^^^^^^^^^^^^^ +Install frr Service +^^^^^^^^^^^^^^^^^^^ .. code-block:: console - sudo install -p -m 644 redhat/frr.service /usr/lib/systemd/system/frr.service - sudo install -p -m 755 redhat/frr.init /usr/lib/frr/frr + sudo install -p -m 644 tools/frr.service /usr/lib/systemd/system/frr.service sudo systemctl enable frr Enable daemons diff --git a/doc/developer/building.rst b/doc/developer/building.rst index b99667124f..859f612313 100644 --- a/doc/developer/building.rst +++ b/doc/developer/building.rst @@ -7,6 +7,7 @@ Building FRR .. toctree:: :maxdepth: 2 + static-linking building-frr-for-alpine building-frr-for-centos6 building-frr-for-centos7 diff --git a/doc/developer/static-linking.rst b/doc/developer/static-linking.rst new file mode 100644 index 0000000000..bc33207b38 --- /dev/null +++ b/doc/developer/static-linking.rst @@ -0,0 +1,98 @@ +.. _static-linking: + +Static Linking +============== + +This document describes how to build FRR without hard dependencies on shared +libraries. Note that it's not possible to build FRR *completely* statically. +This document just covers how to statically link the dependencies that aren't +likely to be present on a given platform - libfrr and libyang. The resultant +binaries should still be fairly portable. For example, here is the DSO +dependency list for `bgpd` after using these steps: + +.. code-block:: + + $ ldd bgpd + linux-vdso.so.1 (0x00007ffe3a989000) + libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f9dc10c0000) + libcap.so.2 => /lib/x86_64-linux-gnu/libcap.so.2 (0x00007f9dc0eba000) + libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f9dc0b1c000) + libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f9dc0918000) + libcrypt.so.1 => /lib/x86_64-linux-gnu/libcrypt.so.1 (0x00007f9dc06e0000) + libjson-c.so.3 => /lib/x86_64-linux-gnu/libjson-c.so.3 (0x00007f9dc04d5000) + librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007f9dc02cd000) + libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f9dc00ae000) + libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f9dbfe96000) + libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f9dbfaa5000) + /lib64/ld-linux-x86-64.so.2 (0x00007f9dc1449000) + +Procedure +--------- +Note that these steps have only been tested with LLVM 9 / clang. + +Today, libfrr can already be statically linked by passing these configure +options:: + + --enable-static --enable-static-bin --enable-shared + +libyang is more complicated. You must build and install libyang as a static +library. To do this, follow the usual libyang build procedure as listed in the +FRR developer docs, but set the ``ENABLE_STATIC`` option in your cmake +invocation. You also need to build with PIC enabled, which today is disabled +when building libyang statically. + +The resultant cmake command is:: + + cmake -DENABLE_STATIC=ON -DENABLE_LYD_PRIV=ON \ + -DCMAKE_INSTALL_PREFIX:PATH=/usr \ + -DCMAKE_POSITION_INDEPENDENT_CODE=TRUE \ + -DCMAKE_BUILD_TYPE:String="Release" .. + +This produces a bunch of ``.a`` static archives that need to ultimately be linked +into FRR. However, not only is it 6 archives rather than the usual ``libyang.so``, +you will now also need to link FRR with ``libpcre.a``. Ubuntu's ``libpcre3-dev`` +package provides this, but it hasn't been built with PIC enabled, so it's not +usable for our purposes. So download ``libpcre`` from +`SourceForge <https://sourceforge.net/projects/pcre/>`_, and build it +like this: + +.. code-block:: + + ./configure --with-pic + make + +Hopefully you get a nice, usable, PIC ``libpcre.a``. + +So now we have to link all these static libraries into FRR. Rather than modify +FRR to accomodate this, the best option is to create an archive with all of +libyang's dependencies. Then to avoid making any changes to FRR build foo, +rename this ``libyang.a`` and copy it over the usual static library location. +Ugly but it works. To do this, go into your libyang build directory, which +should have a bunch of ``.a`` files. Copy ``libpcre.a`` into this directory. +Write the following into a shell script and run it: + +.. code-block:: shell + + #!/bin/bash + ar -M <<EOM + CREATE libyang_fat.a + ADDLIB libyang.a + ADDLIB libyangdata.a + ADDLIB libmetadata.a + ADDLIB libnacm.a + ADDLIB libuser_inet_types.a + ADDLIB libuser_yang_types.a + ADDLIB libpcre.a + SAVE + END + EOM + ranlib libyang_fat.a + +``libyang_fat.a`` is your archive. Now copy this over your install +``libyang.a``, which on my machine is located at +``/usr/lib/x86_64-linux-gnu/libyang.a`` (try ``locate libyang.a`` if not). + +Now when you build FRR with the static options enabled as above, clang should +pick up the static libyang and link it, leaving you with FRR binaries that have +no hard DSO dependencies beyond common system libraries. To verify, run ``ldd`` +over the resultant binaries. diff --git a/doc/developer/subdir.am b/doc/developer/subdir.am index 2c49d6b875..791f7679a6 100644 --- a/doc/developer/subdir.am +++ b/doc/developer/subdir.am @@ -44,6 +44,7 @@ dev_RSTFILES = \ doc/developer/packaging-redhat.rst \ doc/developer/packaging.rst \ doc/developer/rcu.rst \ + doc/developer/static-linking.rst \ doc/developer/testing.rst \ doc/developer/topotests-snippets.rst \ doc/developer/topotests.rst \ diff --git a/doc/developer/topotests.rst b/doc/developer/topotests.rst index c2e72e2ec5..33ebe06d2f 100644 --- a/doc/developer/topotests.rst +++ b/doc/developer/topotests.rst @@ -79,6 +79,7 @@ If you prefer to manually build FRR, then use the following suggested config: --sysconfdir=/etc/frr \ --enable-vtysh \ --enable-pimd \ + --enable-sharpd \ --enable-multipath=64 \ --enable-user=frr \ --enable-group=frr \ diff --git a/doc/manpages/conf.py b/doc/manpages/conf.py index e7813d8176..9121d38fe0 100644 --- a/doc/manpages/conf.py +++ b/doc/manpages/conf.py @@ -313,28 +313,28 @@ latex_documents = [ fwfrr = "{0} routing engine for use with FRRouting." man_pages = [ - ('bgpd', 'bgpd', fwfrr.format("a BGPv4, BGPv4+, BGPv4- "), [], 8), - ('eigrpd', 'eigrpd', fwfrr.format("an EIGRP "), [], 8), - ('ospf6d', 'ospf6d', fwfrr.format("an OSPFv3 "), [], 8), - ('ospfd', 'ospfd', fwfrr.format("an OSPFv2 "), [], 8), - ('isisd', 'isisd', fwfrr.format("an IS-IS "), [], 8), - ('ospfclient', 'ospfclient', 'an example ospf-api client', [], 8), - ('ldpd', 'ldpd', fwfrr.format("an LDP "), [], 8), - ('nhrpd', 'nhrpd', fwfrr.format("a Next Hop Routing Protocol "), [], 8), - ('pimd', 'pimd', fwfrr.format("a PIM "), [], 8), - ('pbrd', 'pbrd', fwfrr.format("a PBR "), [], 8), - ('sharpd', 'sharpd', fwfrr.format("a SHARP "), [], 8), - ('staticd', 'staticd', fwfrr.format("a static route manager "), [], 8), - ('mtracebis', 'mtracebis', "a multicast trace client", [], 8), - ('ripd', 'ripd', fwfrr.format("a RIP "), [], 8), - ('ripngd', 'ripngd', fwfrr.format("a RIPNG "), [], 8), - ('zebra', 'zebra', 'a routing manager for use with associated FRRouting components.', [], 8), - ('watchfrr', 'watchfrr', 'a program to monitor the status of FRRouting daemons', [], 8), - ('vtysh', 'vtysh', 'an integrated shell for FRRouting.', [], 1), + ('frr-bfdd', 'frr-bfdd', fwfrr.format("a bfd"), [], 8), + ('frr-bgpd', 'frr-bgpd', fwfrr.format("a BGPv4, BGPv4+, BGPv4-"), [], 8), + ('frr-eigrpd', 'frr-eigrpd', fwfrr.format("an EIGRP"), [], 8), + ('frr-fabricd', 'frr-fabricd', fwfrr.format("an OpenFabric"), [], 8), + ('frr-isisd', 'frr-isisd', fwfrr.format("an IS-IS"), [], 8), + ('frr-ldpd', 'frr-ldpd', fwfrr.format("an LDP"), [], 8), + ('frr-nhrpd', 'frr-nhrpd', fwfrr.format("a Next Hop Routing Protocol"), [], 8), + ('frr-ospf6d', 'frr-ospf6d', fwfrr.format("an OSPFv3"), [], 8), + ('frr-ospfclient', 'frr-ospfclient', 'an example ospf-api client', [], 8), + ('frr-ospfd', 'frr-ospfd', fwfrr.format("an OSPFv2"), [], 8), + ('frr-pbrd', 'frr-pbrd', fwfrr.format("a PBR"), [], 8), + ('frr-pimd', 'frr-pimd', fwfrr.format("a PIM"), [], 8), + ('frr-ripd', 'frr-ripd', fwfrr.format("a RIP"), [], 8), + ('frr-ripngd', 'frr-ripngd', fwfrr.format("a RIPNG"), [], 8), + ('frr-sharpd', 'frr-sharpd', fwfrr.format("a SHARP"), [], 8), + ('frr-staticd', 'frr-staticd', fwfrr.format("a static route manager"), [], 8), + ('frr-vrrpd', 'frr-vrrpd', fwfrr.format("a VRRP"), [], 8), + ('frr-watchfrr', 'frr-watchfrr', 'a program to monitor the status of FRRouting daemons', [], 8), + ('frr-zebra', 'frr-zebra', 'a routing manager for use with associated FRRouting components.', [], 8), ('frr', 'frr', 'a systemd interaction script', [], 1), - ('bfdd', 'bfdd', fwfrr.format("a bfd"), [], 8), - ('fabricd', 'fabricd', fwfrr.format("an OpenFabric "), [], 8), - ('vrrpd', 'vrrpd', fwfrr.format("a VRRP"), [], 8), + ('mtracebis', 'mtracebis', "a multicast trace client", [], 8), + ('vtysh', 'vtysh', 'an integrated shell for FRRouting.', [], 1), ] # -- Options for Texinfo output ------------------------------------------- diff --git a/doc/manpages/defines.rst b/doc/manpages/defines.rst index 2a6a9fd1bd..ac24cfa8dc 100644 --- a/doc/manpages/defines.rst +++ b/doc/manpages/defines.rst @@ -1,3 +1,3 @@ .. |synopsis-options| replace:: [-d|-t|-dt] [-C] [-f config-file] [-i pid-file] [-z zclient-path] [-u user] [-g group] [-A vty-addr] [-P vty-port] [-M module[:options]] [-N pathspace] [--vty_socket vty-path] [--moduledir module-path] .. |synopsis-options-hv| replace:: [-h] [-v] -.. |seealso-programs| replace:: zebra(8), vtysh(1), ripd(8), ripngd(8), ospfd(8), ospf6d(8), bgpd(8), isisd(8), babeld(8), nhrpd(8), pimd(8), pbrd(8), ldpd(8), eigrpd(8), staticd(8), fabricd(8), vrrpd(8), mtracebis(8) +.. |seealso-programs| replace:: frr-zebra(8), vtysh(1), frr-ripd(8), frr-ripngd(8), frr-ospfd(8), frr-ospf6d(8), frr-bgpd(8), frr-isisd(8), frr-babeld(8), frr-nhrpd(8), frr-pimd(8), frr-pbrd(8), frr-ldpd(8), frr-eigrpd(8), frr-staticd(8), frr-fabricd(8), frr-vrrpd(8), mtracebis(8) diff --git a/doc/manpages/bfdd.rst b/doc/manpages/frr-bfdd.rst index 1f8b1475f4..1f8b1475f4 100644 --- a/doc/manpages/bfdd.rst +++ b/doc/manpages/frr-bfdd.rst diff --git a/doc/manpages/bgpd.rst b/doc/manpages/frr-bgpd.rst index f7e20265e5..f7e20265e5 100644 --- a/doc/manpages/bgpd.rst +++ b/doc/manpages/frr-bgpd.rst diff --git a/doc/manpages/eigrpd.rst b/doc/manpages/frr-eigrpd.rst index bc824468d0..bc824468d0 100644 --- a/doc/manpages/eigrpd.rst +++ b/doc/manpages/frr-eigrpd.rst diff --git a/doc/manpages/fabricd.rst b/doc/manpages/frr-fabricd.rst index c14c07661e..c14c07661e 100644 --- a/doc/manpages/fabricd.rst +++ b/doc/manpages/frr-fabricd.rst diff --git a/doc/manpages/isisd.rst b/doc/manpages/frr-isisd.rst index 68761f642c..68761f642c 100644 --- a/doc/manpages/isisd.rst +++ b/doc/manpages/frr-isisd.rst diff --git a/doc/manpages/ldpd.rst b/doc/manpages/frr-ldpd.rst index 113f06673e..113f06673e 100644 --- a/doc/manpages/ldpd.rst +++ b/doc/manpages/frr-ldpd.rst diff --git a/doc/manpages/nhrpd.rst b/doc/manpages/frr-nhrpd.rst index cae01c677b..cae01c677b 100644 --- a/doc/manpages/nhrpd.rst +++ b/doc/manpages/frr-nhrpd.rst diff --git a/doc/manpages/ospf6d.rst b/doc/manpages/frr-ospf6d.rst index cfc6860d5c..cfc6860d5c 100644 --- a/doc/manpages/ospf6d.rst +++ b/doc/manpages/frr-ospf6d.rst diff --git a/doc/manpages/ospfclient.rst b/doc/manpages/frr-ospfclient.rst index c52b108767..c52b108767 100644 --- a/doc/manpages/ospfclient.rst +++ b/doc/manpages/frr-ospfclient.rst diff --git a/doc/manpages/ospfd.rst b/doc/manpages/frr-ospfd.rst index 951a0229aa..951a0229aa 100644 --- a/doc/manpages/ospfd.rst +++ b/doc/manpages/frr-ospfd.rst diff --git a/doc/manpages/pbrd.rst b/doc/manpages/frr-pbrd.rst index d9a80b1f84..d9a80b1f84 100644 --- a/doc/manpages/pbrd.rst +++ b/doc/manpages/frr-pbrd.rst diff --git a/doc/manpages/pimd.rst b/doc/manpages/frr-pimd.rst index d7582668d2..d7582668d2 100644 --- a/doc/manpages/pimd.rst +++ b/doc/manpages/frr-pimd.rst diff --git a/doc/manpages/ripd.rst b/doc/manpages/frr-ripd.rst index af4590c824..af4590c824 100644 --- a/doc/manpages/ripd.rst +++ b/doc/manpages/frr-ripd.rst diff --git a/doc/manpages/ripngd.rst b/doc/manpages/frr-ripngd.rst index aedd689876..aedd689876 100644 --- a/doc/manpages/ripngd.rst +++ b/doc/manpages/frr-ripngd.rst diff --git a/doc/manpages/sharpd.rst b/doc/manpages/frr-sharpd.rst index 016f3f9254..016f3f9254 100644 --- a/doc/manpages/sharpd.rst +++ b/doc/manpages/frr-sharpd.rst diff --git a/doc/manpages/staticd.rst b/doc/manpages/frr-staticd.rst index ccbcf32e36..ccbcf32e36 100644 --- a/doc/manpages/staticd.rst +++ b/doc/manpages/frr-staticd.rst diff --git a/doc/manpages/vrrpd.rst b/doc/manpages/frr-vrrpd.rst index 0e73b07cda..0e73b07cda 100644 --- a/doc/manpages/vrrpd.rst +++ b/doc/manpages/frr-vrrpd.rst diff --git a/doc/manpages/watchfrr.rst b/doc/manpages/frr-watchfrr.rst index dceb423f82..dceb423f82 100644 --- a/doc/manpages/watchfrr.rst +++ b/doc/manpages/frr-watchfrr.rst diff --git a/doc/manpages/zebra.rst b/doc/manpages/frr-zebra.rst index cfb368bf44..cfb368bf44 100644 --- a/doc/manpages/zebra.rst +++ b/doc/manpages/frr-zebra.rst diff --git a/doc/manpages/index.rst b/doc/manpages/index.rst index 40f06efdfe..58a1d9e0db 100644 --- a/doc/manpages/index.rst +++ b/doc/manpages/index.rst @@ -6,25 +6,25 @@ .. toctree:: :maxdepth: 2 - bfdd - bgpd - eigrpd - isisd - fabricd - ldpd - nhrpd - ospf6d - ospfclient - ospfd - pimd - pbrd + frr + frr-bfdd + frr-bgpd + frr-eigrpd + frr-isisd + frr-fabricd + frr-ldpd + frr-nhrpd + frr-ospf6d + frr-ospfclient + frr-ospfd + frr-pimd + frr-pbrd + frr-ripd + frr-ripngd + frr-sharpd + frr-staticd + frr-watchfrr + frr-zebra + frr-vrrpd mtracebis - ripd - ripngd - sharpd - staticd - watchfrr - zebra vtysh - vrrpd - frr diff --git a/doc/manpages/subdir.am b/doc/manpages/subdir.am index 19d2d8d6ae..9284212099 100644 --- a/doc/manpages/subdir.am +++ b/doc/manpages/subdir.am @@ -3,34 +3,34 @@ # man_RSTFILES = \ - doc/manpages/bgpd.rst \ + doc/manpages/bfd-options.rst \ doc/manpages/common-options.rst \ doc/manpages/conf.py \ doc/manpages/defines.rst \ - doc/manpages/eigrpd.rst \ doc/manpages/epilogue.rst \ - doc/manpages/fabricd.rst \ + doc/manpages/frr-bfdd.rst \ + doc/manpages/frr-bgpd.rst \ + doc/manpages/frr-eigrpd.rst \ + doc/manpages/frr-fabricd.rst \ + doc/manpages/frr-isisd.rst \ + doc/manpages/frr-ldpd.rst \ + doc/manpages/frr-nhrpd.rst \ + doc/manpages/frr-ospf6d.rst \ + doc/manpages/frr-ospfclient.rst \ + doc/manpages/frr-ospfd.rst \ + doc/manpages/frr-pbrd.rst \ + doc/manpages/frr-pimd.rst \ + doc/manpages/frr-ripd.rst \ + doc/manpages/frr-ripngd.rst \ + doc/manpages/frr-sharpd.rst \ + doc/manpages/frr-staticd.rst \ + doc/manpages/frr-vrrpd.rst \ + doc/manpages/frr-watchfrr.rst \ + doc/manpages/frr-zebra.rst \ doc/manpages/frr.rst \ doc/manpages/index.rst \ - doc/manpages/isisd.rst \ - doc/manpages/ldpd.rst \ doc/manpages/mtracebis.rst \ - doc/manpages/nhrpd.rst \ - doc/manpages/ospf6d.rst \ - doc/manpages/ospfclient.rst \ - doc/manpages/ospfd.rst \ - doc/manpages/pimd.rst \ - doc/manpages/ripd.rst \ - doc/manpages/pbrd.rst \ - doc/manpages/ripngd.rst \ - doc/manpages/sharpd.rst \ - doc/manpages/staticd.rst \ doc/manpages/vtysh.rst \ - doc/manpages/watchfrr.rst \ - doc/manpages/zebra.rst \ - doc/manpages/bfdd.rst \ - doc/manpages/bfd-options.rst \ - doc/manpages/vrrpd.rst \ # end EXTRA_DIST += $(man_RSTFILES) diff --git a/doc/user/bfd.rst b/doc/user/bfd.rst index b3fc7f15a6..e6a3c4977a 100644 --- a/doc/user/bfd.rst +++ b/doc/user/bfd.rst @@ -476,3 +476,13 @@ You can also clear packet counters per session with the following commands, only Session down events: 0 Zebra notifications: 4 +Logging / debugging +=================== + +There are no fine grained debug controls for bfdd. Just enable debug logs. + +:: + + config + log file /var/log/frr/frr.log debugging + log syslog debugging diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index 2d4d0c4945..d3ac4b22ab 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -1075,6 +1075,11 @@ Configuring Peers granular and offers much smarter matching criterion than number of received prefixes, making it more suited to implementing policy. +.. index:: [no] neighbor PEER maximum-prefix-out NUMBER +.. clicmd:: [no] neighbor PEER maximum-prefix-out NUMBER + + Sets a maximum number of prefixes we can send to a given peer. + .. index:: [no] neighbor PEER local-as AS-NUMBER [no-prepend] [replace-as] .. clicmd:: [no] neighbor PEER local-as AS-NUMBER [no-prepend] [replace-as] diff --git a/eigrpd/subdir.am b/eigrpd/subdir.am index 5a65c654e9..e59c88b471 100644 --- a/eigrpd/subdir.am +++ b/eigrpd/subdir.am @@ -12,7 +12,7 @@ vtysh_scan += \ $(top_srcdir)/eigrpd/eigrp_vty.c \ # end # $(top_srcdir)/eigrpd/eigrp_routemap.c -man8 += $(MANBUILD)/eigrpd.8 +man8 += $(MANBUILD)/frr-eigrpd.8 endif eigrpd_libeigrp_a_SOURCES = \ diff --git a/isisd/subdir.am b/isisd/subdir.am index e77fef41dd..5dddb7d345 100644 --- a/isisd/subdir.am +++ b/isisd/subdir.am @@ -14,7 +14,7 @@ vtysh_scan += \ $(top_srcdir)/isisd/isis_vty_fabricd.c \ $(top_srcdir)/isisd/isisd.c \ # end -man8 += $(MANBUILD)/isisd.8 +man8 += $(MANBUILD)/frr-isisd.8 endif if FABRICD diff --git a/ldpd/subdir.am b/ldpd/subdir.am index 42c5ad024b..f464bad9e7 100644 --- a/ldpd/subdir.am +++ b/ldpd/subdir.am @@ -7,7 +7,7 @@ noinst_LIBRARIES += ldpd/libldp.a sbin_PROGRAMS += ldpd/ldpd dist_examples_DATA += ldpd/ldpd.conf.sample vtysh_scan += $(top_srcdir)/ldpd/ldp_vty_cmds.c -man8 += $(MANBUILD)/ldpd.8 +man8 += $(MANBUILD)/frr-ldpd.8 endif ldpd_libldp_a_SOURCES = \ diff --git a/lib/gitversion.pl b/lib/gitversion.pl index 2718046d0b..dd25c8976a 100644 --- a/lib/gitversion.pl +++ b/lib/gitversion.pl @@ -6,7 +6,7 @@ chdir $dir || die "$dir: $!\n"; my $gitdesc = `git describe --always --first-parent --tags --dirty --match 'frr-*' || echo -- \"0-gUNKNOWN\"`; chomp $gitdesc; -my $gitsuffix = ($gitdesc =~ /([0-9a-fA-F]{7}(-dirty)?)$/) ? "-g$1" : "-gUNKNOWN"; +my $gitsuffix = ($gitdesc =~ /-g([0-9a-fA-F]+(-dirty)?)$/) ? "-g$1" : "-gUNKNOWN"; printf STDERR "git suffix: %s\n", $gitsuffix; printf "#define GIT_SUFFIX \"%s\"\n", $gitsuffix; @@ -1093,6 +1093,7 @@ static const struct zebra_desc_table command_types[] = { DESC_ENTRY(ZEBRA_VXLAN_SG_ADD), DESC_ENTRY(ZEBRA_VXLAN_SG_DEL), DESC_ENTRY(ZEBRA_VXLAN_SG_REPLAY), + DESC_ENTRY(ZEBRA_ERROR), }; #undef DESC_ENTRY diff --git a/lib/nexthop.c b/lib/nexthop.c index d2ab70e209..e23f8b0792 100644 --- a/lib/nexthop.c +++ b/lib/nexthop.c @@ -34,6 +34,7 @@ #include "jhash.h" #include "printfrr.h" #include "vrf.h" +#include "nexthop_group.h" DEFINE_MTYPE_STATIC(LIB, NEXTHOP, "Nexthop") DEFINE_MTYPE_STATIC(LIB, NH_LABEL, "Nexthop label") @@ -565,8 +566,9 @@ uint32_t nexthop_hash(const struct nexthop *nexthop) return key; } -void nexthop_copy(struct nexthop *copy, const struct nexthop *nexthop, - struct nexthop *rparent) +void nexthop_copy_no_recurse(struct nexthop *copy, + const struct nexthop *nexthop, + struct nexthop *rparent) { copy->vrf_id = nexthop->vrf_id; copy->ifindex = nexthop->ifindex; @@ -583,6 +585,28 @@ void nexthop_copy(struct nexthop *copy, const struct nexthop *nexthop, &nexthop->nh_label->label[0]); } +void nexthop_copy(struct nexthop *copy, const struct nexthop *nexthop, + struct nexthop *rparent) +{ + nexthop_copy_no_recurse(copy, nexthop, rparent); + + /* Bit of a special case here, we need to handle the case + * of a nexthop resolving to agroup. Hence, we need to + * use a nexthop_group API. + */ + if (CHECK_FLAG(copy->flags, NEXTHOP_FLAG_RECURSIVE)) + copy_nexthops(©->resolved, nexthop->resolved, copy); +} + +struct nexthop *nexthop_dup_no_recurse(const struct nexthop *nexthop, + struct nexthop *rparent) +{ + struct nexthop *new = nexthop_new(); + + nexthop_copy_no_recurse(new, nexthop, rparent); + return new; +} + struct nexthop *nexthop_dup(const struct nexthop *nexthop, struct nexthop *rparent) { diff --git a/lib/nexthop.h b/lib/nexthop.h index 040b643a84..cb5efe00cc 100644 --- a/lib/nexthop.h +++ b/lib/nexthop.h @@ -187,9 +187,16 @@ extern unsigned int nexthop_level(struct nexthop *nexthop); /* Copies to an already allocated nexthop struct */ extern void nexthop_copy(struct nexthop *copy, const struct nexthop *nexthop, struct nexthop *rparent); +/* Copies to an already allocated nexthop struct, not including recurse info */ +extern void nexthop_copy_no_recurse(struct nexthop *copy, + const struct nexthop *nexthop, + struct nexthop *rparent); /* Duplicates a nexthop and returns the newly allocated nexthop */ extern struct nexthop *nexthop_dup(const struct nexthop *nexthop, struct nexthop *rparent); +/* Duplicates a nexthop and returns the newly allocated nexthop */ +extern struct nexthop *nexthop_dup_no_recurse(const struct nexthop *nexthop, + struct nexthop *rparent); #ifdef __cplusplus } diff --git a/lib/nexthop_group.c b/lib/nexthop_group.c index 0051cba625..3005a51c71 100644 --- a/lib/nexthop_group.c +++ b/lib/nexthop_group.c @@ -250,8 +250,7 @@ static void _nexthop_add_sorted(struct nexthop **head, { struct nexthop *position, *prev; - /* Ensure this gets set */ - nexthop->next = NULL; + assert(!nexthop->next); for (position = *head, prev = NULL; position; prev = position, position = position->next) { @@ -281,6 +280,8 @@ void nexthop_group_add_sorted(struct nexthop_group *nhg, { struct nexthop *tail; + assert(!nexthop->next); + /* Try to just append to the end first; * trust the list is already sorted */ @@ -363,10 +364,6 @@ void copy_nexthops(struct nexthop **tnh, const struct nexthop *nh, for (nh1 = nh; nh1; nh1 = nh1->next) { nexthop = nexthop_dup(nh1, rparent); _nexthop_add(tnh, nexthop); - - if (CHECK_FLAG(nh1->flags, NEXTHOP_FLAG_RECURSIVE)) - copy_nexthops(&nexthop->resolved, nh1->resolved, - nexthop); } } diff --git a/lib/zclient.c b/lib/zclient.c index fd1b181e58..b2c74cd0b9 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -143,6 +143,18 @@ void redist_del_instance(struct redist_proto *red, unsigned short instance) } } +void redist_del_all_instances(struct redist_proto *red) +{ + struct listnode *ln, *nn; + unsigned short *id; + + if (!red->instances) + return; + + for (ALL_LIST_ELEMENTS(red->instances, ln, nn, id)) + redist_del_instance(red, *id); +} + /* Stop zebra client services. */ void zclient_stop(struct zclient *zclient) { @@ -899,8 +911,6 @@ int zapi_nexthop_encode(struct stream *s, const struct zapi_nexthop *api_nh, stream_putc(s, api_nh->bh_type); break; case NEXTHOP_TYPE_IPV4: - stream_put_in_addr(s, &api_nh->gate.ipv4); - break; case NEXTHOP_TYPE_IPV4_IFINDEX: stream_put_in_addr(s, &api_nh->gate.ipv4); stream_putl(s, api_nh->ifindex); @@ -909,9 +919,6 @@ int zapi_nexthop_encode(struct stream *s, const struct zapi_nexthop *api_nh, stream_putl(s, api_nh->ifindex); break; case NEXTHOP_TYPE_IPV6: - stream_write(s, (uint8_t *)&api_nh->gate.ipv6, - 16); - break; case NEXTHOP_TYPE_IPV6_IFINDEX: stream_write(s, (uint8_t *)&api_nh->gate.ipv6, 16); @@ -1059,9 +1066,6 @@ static int zapi_nexthop_decode(struct stream *s, struct zapi_nexthop *api_nh, STREAM_GETC(s, api_nh->bh_type); break; case NEXTHOP_TYPE_IPV4: - STREAM_GET(&api_nh->gate.ipv4.s_addr, s, - IPV4_MAX_BYTELEN); - break; case NEXTHOP_TYPE_IPV4_IFINDEX: STREAM_GET(&api_nh->gate.ipv4.s_addr, s, IPV4_MAX_BYTELEN); @@ -1071,8 +1075,6 @@ static int zapi_nexthop_decode(struct stream *s, struct zapi_nexthop *api_nh, STREAM_GETL(s, api_nh->ifindex); break; case NEXTHOP_TYPE_IPV6: - STREAM_GET(&api_nh->gate.ipv6, s, 16); - break; case NEXTHOP_TYPE_IPV6_IFINDEX: STREAM_GET(&api_nh->gate.ipv6, s, 16); STREAM_GETL(s, api_nh->ifindex); @@ -1467,6 +1469,21 @@ stream_failure: return false; } +bool zapi_error_decode(struct stream *s, enum zebra_error_types *error) +{ + memset(error, 0, sizeof(*error)); + + STREAM_GET(error, s, sizeof(*error)); + + if (zclient_debug) + zlog_debug("%s: type: %s", __func__, + zebra_error_type2str(*error)); + + return true; +stream_failure: + return false; +} + /* * send a ZEBRA_REDISTRIBUTE_ADD or ZEBRA_REDISTRIBUTE_DELETE * for the route type (ZEBRA_ROUTE_KERNEL etc.). The zebra server will @@ -1706,6 +1723,17 @@ static void zclient_interface_down(struct zclient *zclient, vrf_id_t vrf_id) if_down_via_zapi(ifp); } +static void zclient_handle_error(ZAPI_CALLBACK_ARGS) +{ + enum zebra_error_types error; + struct stream *s = zclient->ibuf; + + zapi_error_decode(s, &error); + + if (zclient->handle_error) + (*zclient->handle_error)(error); +} + static void link_params_set_value(struct stream *s, struct if_link_params *iflp) { @@ -3153,6 +3181,8 @@ static int zclient_read(struct thread *thread) case ZEBRA_MLAG_FORWARD_MSG: zclient_mlag_handle_msg(command, zclient, length, vrf_id); break; + case ZEBRA_ERROR: + zclient_handle_error(command, zclient, length, vrf_id); default: break; } diff --git a/lib/zclient.h b/lib/zclient.h index 70304127a2..d1aa42da6d 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -183,8 +183,34 @@ typedef enum { ZEBRA_MLAG_CLIENT_REGISTER, ZEBRA_MLAG_CLIENT_UNREGISTER, ZEBRA_MLAG_FORWARD_MSG, + ZEBRA_ERROR, } zebra_message_types_t; +enum zebra_error_types { + ZEBRA_UNKNOWN_ERROR, /* Error of unknown type */ + ZEBRA_NO_VRF, /* Vrf in header was not found */ + ZEBRA_INVALID_MSG_TYPE, /* No handler found for msg type */ +}; + +static inline const char *zebra_error_type2str(enum zebra_error_types type) +{ + const char *ret = "UNKNOWN"; + + switch (type) { + case ZEBRA_UNKNOWN_ERROR: + ret = "ZEBRA_UNKNOWN_ERROR"; + break; + case ZEBRA_NO_VRF: + ret = "ZEBRA_NO_VRF"; + break; + case ZEBRA_INVALID_MSG_TYPE: + ret = "ZEBRA_INVALID_MSG_TYPE"; + break; + } + + return ret; +} + struct redist_proto { uint8_t enabled; struct list *instances; @@ -280,6 +306,7 @@ struct zclient { int (*mlag_process_up)(void); int (*mlag_process_down)(void); int (*mlag_handle_msg)(struct stream *msg, int len); + int (*handle_error)(enum zebra_error_types error); }; /* Zebra API message flag. */ @@ -547,6 +574,7 @@ extern unsigned short *redist_check_instance(struct redist_proto *, unsigned short); extern void redist_add_instance(struct redist_proto *, unsigned short); extern void redist_del_instance(struct redist_proto *, unsigned short); +extern void redist_del_all_instances(struct redist_proto *red); /* * Send to zebra that the specified vrf is using label to resolve @@ -728,6 +756,9 @@ int zapi_nexthop_from_nexthop(struct zapi_nexthop *znh, extern bool zapi_nexthop_update_decode(struct stream *s, struct zapi_route *nhr); +/* Decode the zebra error message */ +extern bool zapi_error_decode(struct stream *s, enum zebra_error_types *error); + static inline void zapi_route_set_blackhole(struct zapi_route *api, enum blackhole_type bh_type) { diff --git a/m4/ax_python.m4 b/m4/ax_python.m4 index 66338511a3..69809184ee 100644 --- a/m4/ax_python.m4 +++ b/m4/ax_python.m4 @@ -186,7 +186,7 @@ AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_MSG_RESULT([yes]) PYTHON_CFLAGS="`\"$pycfg\" --includes`" - if test x"${py_ver}" == x"3.8" || test x"{py_ver}" == x"3.9"; then + if test x"${py_ver}" = x"3.8" || test x"{py_ver}" = x"3.9"; then PYTHON_LIBS="`\"$pycfg\" --ldflags --embed`" else PYTHON_LIBS="`\"$pycfg\" --ldflags`" diff --git a/nhrpd/nhrp_peer.c b/nhrpd/nhrp_peer.c index 3a74b75696..c5e985cdac 100644 --- a/nhrpd/nhrp_peer.c +++ b/nhrpd/nhrp_peer.c @@ -896,8 +896,10 @@ void nhrp_peer_recv(struct nhrp_peer *p, struct zbuf *zb) extoff = htons(hdr->extension_offset); if (extoff) { - if (extoff >= realsize) { - info = "extoff larger than packet"; + assert(zb->head > zb->buf); + uint32_t header_offset = zb->head - zb->buf; + if ((extoff >= realsize) || (extoff < (header_offset))) { + info = "extoff larger than packet, or smaller than header"; goto drop; } paylen = extoff - (zb->head - zb->buf); diff --git a/nhrpd/subdir.am b/nhrpd/subdir.am index fe76623ac3..42a6380b17 100644 --- a/nhrpd/subdir.am +++ b/nhrpd/subdir.am @@ -5,7 +5,7 @@ if NHRPD sbin_PROGRAMS += nhrpd/nhrpd vtysh_scan += $(top_srcdir)/nhrpd/nhrp_vty.c -man8 += $(MANBUILD)/nhrpd.8 +man8 += $(MANBUILD)/frr-nhrpd.8 endif nhrpd_nhrpd_LDADD = lib/libfrr.la lib/libfrrcares.la $(LIBCAP) diff --git a/ospf6d/ospf6_flood.c b/ospf6d/ospf6_flood.c index 0828c2beb6..15dbd0716a 100644 --- a/ospf6d/ospf6_flood.c +++ b/ospf6d/ospf6_flood.c @@ -842,18 +842,6 @@ void ospf6_receive_lsa(struct ospf6_neighbor *from, zlog_debug("Received is duplicated LSA"); SET_FLAG(new->flag, OSPF6_LSA_DUPLICATE); } - if (old->header->adv_router - == from->ospf6_if->area->ospf6->router_id - && OSPF6_LSA_IS_MAXAGE(new)) { - ospf6_acknowledge_lsa(new, ismore_recent, from); - ospf6_lsa_delete(new); - if (is_debug) - zlog_debug( - "%s: Received is self orig MAXAGE LSA %s, discard (ismore_recent %d)", - __PRETTY_FUNCTION__, old->name, - ismore_recent); - return; - } } /* if no database copy or received is more recent */ diff --git a/ospf6d/subdir.am b/ospf6d/subdir.am index eac0eee45f..570b077cb1 100644 --- a/ospf6d/subdir.am +++ b/ospf6d/subdir.am @@ -26,7 +26,7 @@ vtysh_scan += \ if SNMP module_LTLIBRARIES += ospf6d/ospf6d_snmp.la endif -man8 += $(MANBUILD)/ospf6d.8 +man8 += $(MANBUILD)/frr-ospf6d.8 endif ospf6d_libospf6_a_SOURCES = \ diff --git a/ospfclient/subdir.am b/ospfclient/subdir.am index 94d489358c..756ad88f15 100644 --- a/ospfclient/subdir.am +++ b/ospfclient/subdir.am @@ -5,7 +5,7 @@ if OSPFCLIENT lib_LTLIBRARIES += ospfclient/libfrrospfapiclient.la noinst_PROGRAMS += ospfclient/ospfclient -man8 += $(MANBUILD)/ospfclient.8 +#man8 += $(MANBUILD)/frr-ospfclient.8 endif ospfclient_libfrrospfapiclient_la_LDFLAGS = -version-info 0:0:0 diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index 152a7e83b7..1542ef88fb 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -9679,7 +9679,7 @@ DEFUN (show_ip_ospf_vrfs, if (uj) json_vrf = json_object_new_object(); - if (ospf->vrf_id == 0) + if (ospf->vrf_id == VRF_DEFAULT) name = VRF_DEFAULT_NAME; else name = ospf->name; diff --git a/ospfd/subdir.am b/ospfd/subdir.am index 48dd741b24..6de4099c5b 100644 --- a/ospfd/subdir.am +++ b/ospfd/subdir.am @@ -19,7 +19,7 @@ vtysh_scan += \ if SNMP module_LTLIBRARIES += ospfd/ospfd_snmp.la endif -man8 += $(MANBUILD)/ospfd.8 +man8 += $(MANBUILD)/frr-ospfd.8 endif ospfd_libfrrospf_a_SOURCES = \ diff --git a/pbrd/subdir.am b/pbrd/subdir.am index 41d0e5a0b8..c55f0b41cc 100644 --- a/pbrd/subdir.am +++ b/pbrd/subdir.am @@ -10,7 +10,7 @@ vtysh_scan += \ $(top_srcdir)/pbrd/pbr_vty.c \ $(top_srcdir)/pbrd/pbr_debug.c \ # end -man8 += $(MANBUILD)/pbrd.8 +man8 += $(MANBUILD)/frr-pbrd.8 endif pbrd_libpbr_a_SOURCES = \ diff --git a/pimd/pim_zlookup.c b/pimd/pim_zlookup.c index 77526281d1..0454c0d69e 100644 --- a/pimd/pim_zlookup.c +++ b/pimd/pim_zlookup.c @@ -180,6 +180,14 @@ static int zclient_read_nexthop(struct pim_instance *pim, zclient_lookup_failed(zlookup); return -1; } + + if (command == ZEBRA_ERROR) { + enum zebra_error_types error; + + zapi_error_decode(s, &error); + /* Do nothing with it for now */ + return -1; + } } raddr.s_addr = stream_get_ipv4(s); @@ -338,7 +346,8 @@ static int zclient_lookup_nexthop_once(struct pim_instance *pim, s = zlookup->obuf; stream_reset(s); - zclient_create_header(s, ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB, pim->vrf_id); + zclient_create_header(s, ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB, + pim->vrf->vrf_id); stream_put_in_addr(s, &addr); stream_putw_at(s, 0, stream_get_endp(s)); diff --git a/pimd/subdir.am b/pimd/subdir.am index 5407e566a5..b5d135d032 100644 --- a/pimd/subdir.am +++ b/pimd/subdir.am @@ -9,7 +9,7 @@ bin_PROGRAMS += pimd/mtracebis noinst_PROGRAMS += pimd/test_igmpv3_join dist_examples_DATA += pimd/pimd.conf.sample vtysh_scan += $(top_srcdir)/pimd/pim_cmd.c -man8 += $(MANBUILD)/pimd.8 +man8 += $(MANBUILD)/frr-pimd.8 man8 += $(MANBUILD)/mtracebis.8 endif diff --git a/ripd/subdir.am b/ripd/subdir.am index dfdfc88a56..00984672ed 100644 --- a/ripd/subdir.am +++ b/ripd/subdir.am @@ -15,7 +15,7 @@ vtysh_scan += \ if SNMP module_LTLIBRARIES += ripd/ripd_snmp.la endif -man8 += $(MANBUILD)/ripd.8 +man8 += $(MANBUILD)/frr-ripd.8 endif ripd_librip_a_SOURCES = \ diff --git a/ripngd/subdir.am b/ripngd/subdir.am index 07d0cb892c..4e219c9947 100644 --- a/ripngd/subdir.am +++ b/ripngd/subdir.am @@ -10,7 +10,7 @@ vtysh_scan += \ $(top_srcdir)/ripngd/ripng_debug.c \ $(top_srcdir)/ripngd/ripngd.c \ # end -man8 += $(MANBUILD)/ripngd.8 +man8 += $(MANBUILD)/frr-ripngd.8 endif ripngd_libripng_a_SOURCES = \ diff --git a/sharpd/subdir.am b/sharpd/subdir.am index 4a9028f6fe..89b183d832 100644 --- a/sharpd/subdir.am +++ b/sharpd/subdir.am @@ -7,7 +7,7 @@ noinst_LIBRARIES += sharpd/libsharp.a sbin_PROGRAMS += sharpd/sharpd dist_examples_DATA += sharpd/sharpd.conf.sample vtysh_scan += $(top_srcdir)/sharpd/sharp_vty.c -man8 += $(MANBUILD)/sharpd.8 +man8 += $(MANBUILD)/frr-sharpd.8 endif sharpd_libsharp_a_SOURCES = \ diff --git a/solaris/prototype.doc.in b/solaris/prototype.doc.in index a8644b3145..9f7995350a 100644 --- a/solaris/prototype.doc.in +++ b/solaris/prototype.doc.in @@ -8,10 +8,10 @@ d none @mandir@=$DESTDIR/@mandir@ 0755 root bin d none @mandir@/man1=$DESTDIR/@mandir@/man1 0755 root bin f none @mandir@/man1/vtysh.1=$DESTDIR/@mandir@/man1/vtysh.1 0644 root bin d none @mandir@/man8=$DESTDIR/@mandir@/man8 0755 root bin -f none @mandir@/man8/bgpd.8=$DESTDIR/@mandir@/man8/bgpd.8 0644 root bin -f none @mandir@/man8/ospf6d.8=$DESTDIR/@mandir@/man8/ospf6d.8 0644 root bin -f none @mandir@/man8/ospfd.8=$DESTDIR/@mandir@/man8/ospfd.8 0644 root bin -f none @mandir@/man8/ripd.8=$DESTDIR/@mandir@/man8/ripd.8 0644 root bin -f none @mandir@/man8/ripngd.8=$DESTDIR/@mandir@/man8/ripngd.8 0644 root bin -f none @mandir@/man8/zebra.8=$DESTDIR/@mandir@/man8/zebra.8 0644 root bin -f none @mandir@/man8/isisd.8=$DESTDIR/@mandir@/man8/isisd.8 0644 root bin +f none @mandir@/man8/frr-bgpd.8=$DESTDIR/@mandir@/man8/bgpd.8 0644 root bin +f none @mandir@/man8/frr-ospf6d.8=$DESTDIR/@mandir@/man8/frr-ospf6d.8 0644 root bin +f none @mandir@/man8/frr-ospfd.8=$DESTDIR/@mandir@/man8/frr-ospfd.8 0644 root bin +f none @mandir@/man8/frr-ripd.8=$DESTDIR/@mandir@/man8/frr-ripd.8 0644 root bin +f none @mandir@/man8/frr-ripngd.8=$DESTDIR/@mandir@/man8/frr-ripngd.8 0644 root bin +f none @mandir@/man8/frr-zebra.8=$DESTDIR/@mandir@/man8/frr-zebra.8 0644 root bin +f none @mandir@/man8/frr-isisd.8=$DESTDIR/@mandir@/man8/frr-isisd.8 0644 root bin diff --git a/staticd/static_debug.c b/staticd/static_debug.c new file mode 100644 index 0000000000..9906e805a7 --- /dev/null +++ b/staticd/static_debug.c @@ -0,0 +1,124 @@ +/* + * Staticd debug related functions + * Copyright (C) 2019 Volta Networks Inc. + * Mark Stapp + * + * This file is part of Free Range Routing (FRR). + * + * 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, 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 "lib/command.h" +#include "lib/debug.h" + +#include "static_debug.h" + +/* + * Debug infra: a debug struct for each category, and a corresponding + * string. + */ + +/* clang-format off */ +struct debug static_dbg_events = {0, "Staticd events"}; + +struct debug *static_debug_arr[] = { + &static_dbg_events +}; + +const char *static_debugs_conflines[] = { + "debug static events" +}; +/* clang-format on */ + + +/* + * Set or unset all staticd debugs + * + * flags + * The flags to set + * + * set + * Whether to set or unset the specified flags + */ +static void static_debug_set_all(uint32_t flags, bool set) +{ + for (unsigned int i = 0; i < array_size(static_debug_arr); i++) { + DEBUG_FLAGS_SET(static_debug_arr[i], flags, set); + + /* if all modes have been turned off, don't preserve options */ + if (!DEBUG_MODE_CHECK(static_debug_arr[i], DEBUG_MODE_ALL)) + DEBUG_CLEAR(static_debug_arr[i]); + } +} + +static int static_debug_config_write_helper(struct vty *vty, bool config) +{ + uint32_t mode = DEBUG_MODE_ALL; + + if (config) + mode = DEBUG_MODE_CONF; + + for (unsigned int i = 0; i < array_size(static_debug_arr); i++) + if (DEBUG_MODE_CHECK(static_debug_arr[i], mode)) + vty_out(vty, "%s\n", static_debugs_conflines[i]); + + return 0; +} + +int static_config_write_debug(struct vty *vty) +{ + return static_debug_config_write_helper(vty, true); +} + +int static_debug_status_write(struct vty *vty) +{ + return static_debug_config_write_helper(vty, false); +} + +/* + * Set debugging status. + * + * vtynode + * vty->node + * + * onoff + * Whether to turn the specified debugs on or off + * + * events + * Debug general internal events + * + */ +void static_debug_set(int vtynode, bool onoff, bool events) +{ + uint32_t mode = DEBUG_NODE2MODE(vtynode); + + if (events) + DEBUG_MODE_SET(&static_dbg_events, mode, onoff); +} + +/* + * Debug lib initialization + */ + +struct debug_callbacks static_dbg_cbs = { + .debug_set_all = static_debug_set_all +}; + +void static_debug_init(void) +{ + debug_init(&static_dbg_cbs); +} diff --git a/staticd/static_debug.h b/staticd/static_debug.h new file mode 100644 index 0000000000..6e58118ed0 --- /dev/null +++ b/staticd/static_debug.h @@ -0,0 +1,73 @@ +/* + * Staticd debug related functions + * Copyright (C) 2019 Volta Networks Inc. + * Mark Stapp + * + * This file is part of Free Range Routing (FRR). + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _STATIC_DEBUG_H +#define _STATIC_DEBUG_H + + +#include <zebra.h> + +#include "lib/debug.h" + +/* staticd debugging records */ +struct debug static_dbg_events; + +/* + * Initialize staticd debugging. + * + * Installs VTY commands and registers callbacks. + */ +void static_debug_init(void); + +/* + * Print staticd debugging configuration. + * + * vty + * VTY to print debugging configuration to. + */ +int static_config_write_debug(struct vty *vty); + +/* + * Print staticd debugging configuration, human readable form. + * + * vty + * VTY to print debugging configuration to. + */ +int static_debug_status_write(struct vty *vty); + +/* + * Set debugging status. + * + * vtynode + * vty->node + * + * onoff + * Whether to turn the specified debugs on or off + * + * events + * Debug general internal events + * + */ +void static_debug_set(int vtynode, bool onoff, bool events); + + +#endif /* _STATIC_DEBUG_H */ diff --git a/staticd/static_main.c b/staticd/static_main.c index 18cb9638c9..43cb7db51d 100644 --- a/staticd/static_main.c +++ b/staticd/static_main.c @@ -36,6 +36,7 @@ #include "static_vty.h" #include "static_routes.h" #include "static_zebra.h" +#include "static_debug.h" char backup_config_file[256]; @@ -141,6 +142,7 @@ int main(int argc, char **argv, char **envp) master = frr_init(); access_list_init(); + static_debug_init(); static_vrf_init(); static_zebra_init(); diff --git a/staticd/static_vty.c b/staticd/static_vty.c index 8db37589af..6390fd811f 100644 --- a/staticd/static_vty.c +++ b/staticd/static_vty.c @@ -32,9 +32,13 @@ #include "static_memory.h" #include "static_vty.h" #include "static_routes.h" +#include "static_debug.h" #ifndef VTYSH_EXTRACT_PL #include "staticd/static_vty_clippy.c" #endif + +#define STATICD_STR "Static route daemon\n" + static struct static_vrf *static_vty_get_unknown_vrf(struct vty *vty, const char *vrf_name) { @@ -1439,21 +1443,43 @@ DEFPY(ipv6_route_vrf, from_str, gate_str, ifname, flag, tag_str, distance_str, label, table_str, false); } +DEFPY(debug_staticd, + debug_staticd_cmd, + "[no] debug static [{events$events}]", + NO_STR + DEBUG_STR + STATICD_STR + "Debug events\n") +{ + /* If no specific category, change all */ + if (strmatch(argv[argc - 1]->text, "static")) + static_debug_set(vty->node, !no, true); + else + static_debug_set(vty->node, !no, !!events); -DEFUN_NOSH (show_debugging_staticd, - show_debugging_staticd_cmd, + return CMD_SUCCESS; +} + +DEFUN_NOSH (show_debugging_static, + show_debugging_static_cmd, "show debugging [static]", SHOW_STR DEBUG_STR "Static Information\n") { - vty_out(vty, "Static debugging status\n"); + vty_out(vty, "Staticd debugging status\n"); + + static_debug_status_write(vty); return CMD_SUCCESS; } +static struct cmd_node debug_node = {DEBUG_NODE, "", 1}; + void static_vty_init(void) { + install_node(&debug_node, static_config_write_debug); + install_element(CONFIG_NODE, &ip_mroute_dist_cmd); install_element(CONFIG_NODE, &ip_route_blackhole_cmd); @@ -1470,7 +1496,9 @@ void static_vty_init(void) install_element(CONFIG_NODE, &ipv6_route_cmd); install_element(VRF_NODE, &ipv6_route_vrf_cmd); - install_element(VIEW_NODE, &show_debugging_staticd_cmd); + install_element(VIEW_NODE, &show_debugging_static_cmd); + install_element(VIEW_NODE, &debug_staticd_cmd); + install_element(CONFIG_NODE, &debug_staticd_cmd); static_list = list_new(); static_list->cmp = (int (*)(void *, void *))static_list_compare; diff --git a/staticd/subdir.am b/staticd/subdir.am index 17c4536fe9..30c69231c9 100644 --- a/staticd/subdir.am +++ b/staticd/subdir.am @@ -7,10 +7,11 @@ noinst_LIBRARIES += staticd/libstatic.a sbin_PROGRAMS += staticd/staticd dist_examples_DATA += staticd/staticd.conf.sample vtysh_scan += $(top_srcdir)/staticd/static_vty.c -man8 += $(MANBUILD)/staticd.8 +man8 += $(MANBUILD)/frr-staticd.8 endif staticd_libstatic_a_SOURCES = \ + staticd/static_debug.c \ staticd/static_memory.c \ staticd/static_nht.c \ staticd/static_routes.c \ @@ -20,6 +21,7 @@ staticd_libstatic_a_SOURCES = \ # end noinst_HEADERS += \ + staticd/static_debug.h \ staticd/static_memory.h \ staticd/static_nht.h \ staticd/static_zebra.h \ diff --git a/tests/bgpd/test_mp_attr.c b/tests/bgpd/test_mp_attr.c index af9e5791b7..c97ea57150 100644 --- a/tests/bgpd/test_mp_attr.c +++ b/tests/bgpd/test_mp_attr.c @@ -1027,7 +1027,7 @@ static void parse_test(struct peer *peer, struct test_segment *t, int type) parse_ret = bgp_mp_unreach_parse(&attr_args, &nlri); break; case BGP_ATTR_PREFIX_SID: - parse_ret = bgp_attr_prefix_sid(t->len, &attr_args, &nlri); + parse_ret = bgp_attr_prefix_sid(&attr_args, &nlri); break; default: printf("unknown type"); diff --git a/tests/topotests/all-protocol-startup/r1/ipv4_routes.ref b/tests/topotests/all-protocol-startup/r1/ipv4_routes.ref index a7d6fe11a6..61d17a61b3 100644 --- a/tests/topotests/all-protocol-startup/r1/ipv4_routes.ref +++ b/tests/topotests/all-protocol-startup/r1/ipv4_routes.ref @@ -10,6 +10,8 @@ C>* 192.168.8.0/26 is directly connected, r1-eth8, XX:XX:XX C>* 192.168.9.0/26 is directly connected, r1-eth9, XX:XX:XX O 192.168.0.0/24 [110/10] is directly connected, r1-eth0, XX:XX:XX O 192.168.3.0/26 [110/10] is directly connected, r1-eth3, XX:XX:XX +S>* 1.1.1.1/32 [1/0] is directly connected, r1-eth0, XX:XX:XX +S>* 1.1.1.2/32 [1/0] is directly connected, r1-eth1, XX:XX:XX S>* 4.5.6.10/32 [1/0] via 192.168.0.2, r1-eth0, XX:XX:XX S>* 4.5.6.11/32 [1/0] via 192.168.0.2, r1-eth0, XX:XX:XX S>* 4.5.6.12/32 [1/0] is directly connected, r1-eth0, XX:XX:XX diff --git a/tests/topotests/all-protocol-startup/r1/zebra.conf b/tests/topotests/all-protocol-startup/r1/zebra.conf index 85c8676964..fbf827604f 100644 --- a/tests/topotests/all-protocol-startup/r1/zebra.conf +++ b/tests/topotests/all-protocol-startup/r1/zebra.conf @@ -26,6 +26,11 @@ ipv6 route 4:5::6:12/128 r1-eth0 # by zebra but not installed. ip route 4.5.6.15/32 192.168.0.2 255 ipv6 route 4:5::6:15/128 fc00:0:0:0::2 255 + +# Routes to put into a nexthop-group +ip route 1.1.1.1/32 r1-eth0 +ip route 1.1.1.2/32 r1-eth1 + ! interface r1-eth0 description to sw0 - no routing protocol 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 9658c080c0..16609221c1 100755 --- a/tests/topotests/all-protocol-startup/test_all_protocol_startup.py +++ b/tests/topotests/all-protocol-startup/test_all_protocol_startup.py @@ -120,6 +120,7 @@ def setup_module(module): if net['r%s' % i].daemon_available('ldpd'): # Only test LDPd if it's installed and Kernel >= 4.5 net['r%s' % i].loadConf('ldpd', '%s/r%s/ldpd.conf' % (thisDir, i)) + net['r%s' % i].loadConf('sharpd') net['r%s' % i].startRouter() # For debugging after starting Quagga/FRR daemons, uncomment the next line @@ -346,6 +347,36 @@ def test_converge_protocols(): # For debugging after starting FRR/Quagga daemons, uncomment the next line ## CLI(net) +def test_nexthop_groups(): + global fatal_error + global net + + # Skip if previous fatal error condition is raised + if (fatal_error != ""): + pytest.skip(fatal_error) + + print("\n\n** Verifying Nexthop Groups") + print("******************************************\n") + + # Create a lib nexthop-group + net["r1"].cmd('vtysh -c "c t" -c "nexthop-group red" -c "nexthop 1.1.1.1" -c "nexthop 1.1.1.2"') + + # Create with sharpd using nexthop-group + net["r1"].cmd('vtysh -c "sharp install routes 2.2.2.1 nexthop-group red 1"') + + # Verify route and that zebra created NHGs for and they are valid/installed + output = net["r1"].cmd('vtysh -c "show ip route 2.2.2.1/32 nexthop-group"') + match = re.search(r"Nexthop Group ID: (\d+)", output); + assert match is not None, "Nexthop Group ID not found for sharpd route 2.2.2.1/32" + + nhe_id = int(match.group(1)) + + output = net["r1"].cmd('vtysh -c "show nexthop-group rib %d"' % nhe_id) + match = re.search(r"Valid", output) + assert match is not None, "Nexthop Group ID=%d not marked Valid" % nhe_id + + match = re.search(r"Installed", output) + assert match is not None, "Nexthop Group ID=%d not marked Installed" % nhe_id def test_rip_status(): global fatal_error diff --git a/tests/topotests/bgp_default-route_route-map/__init__.py b/tests/topotests/bgp_default-route_route-map/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/bgp_default-route_route-map/__init__.py diff --git a/tests/topotests/bgp_default-route_route-map/r1/bgpd.conf b/tests/topotests/bgp_default-route_route-map/r1/bgpd.conf new file mode 100644 index 0000000000..a9925ab661 --- /dev/null +++ b/tests/topotests/bgp_default-route_route-map/r1/bgpd.conf @@ -0,0 +1,9 @@ +router bgp 65000 + neighbor 192.168.255.2 remote-as 65001 + address-family ipv4 unicast + neighbor 192.168.255.2 default-originate route-map default + exit-address-family +! +route-map default permit 10 + set metric 123 +! diff --git a/tests/topotests/bgp_default-route_route-map/r1/zebra.conf b/tests/topotests/bgp_default-route_route-map/r1/zebra.conf new file mode 100644 index 0000000000..0a283c06d5 --- /dev/null +++ b/tests/topotests/bgp_default-route_route-map/r1/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 172.16.255.254/32 +! +interface r1-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_default-route_route-map/r2/bgpd.conf b/tests/topotests/bgp_default-route_route-map/r2/bgpd.conf new file mode 100644 index 0000000000..a8a6c49f4d --- /dev/null +++ b/tests/topotests/bgp_default-route_route-map/r2/bgpd.conf @@ -0,0 +1,6 @@ +router bgp 65001 + neighbor 192.168.255.1 remote-as 65000 + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_default-route_route-map/r2/zebra.conf b/tests/topotests/bgp_default-route_route-map/r2/zebra.conf new file mode 100644 index 0000000000..606c17bec9 --- /dev/null +++ b/tests/topotests/bgp_default-route_route-map/r2/zebra.conf @@ -0,0 +1,6 @@ +! +interface r2-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_default-route_route-map/test_bgp_default-originate_route-map.py b/tests/topotests/bgp_default-route_route-map/test_bgp_default-originate_route-map.py new file mode 100644 index 0000000000..992ee85ab1 --- /dev/null +++ b/tests/topotests/bgp_default-route_route-map/test_bgp_default-originate_route-map.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python + +# +# bgp_default-originate_route-map.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2019 by +# Donatas Abraitis <donatas.abraitis@gmail.com> +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +bgp_default-originate_route-map.py: + +Test if works the following commands: +router bgp 65031 + address-family ipv4 unicast + neighbor 192.168.255.2 default-originate route-map default + +route-map default permit 10 + set metric 123 +""" + +import os +import sys +import json +import time +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, '../')) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from mininet.topo import Topo + +class TemplateTopo(Topo): + def build(self, *_args, **_opts): + tgen = get_topogen(self) + + for routern in range(1, 3): + tgen.add_router('r{}'.format(routern)) + + switch = tgen.add_switch('s1') + switch.add_link(tgen.gears['r1']) + switch.add_link(tgen.gears['r2']) + +def setup_module(mod): + tgen = Topogen(TemplateTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.iteritems(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, + os.path.join(CWD, '{}/zebra.conf'.format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, + os.path.join(CWD, '{}/bgpd.conf'.format(rname)) + ) + + tgen.start_router() + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + +def test_bgp_default_originate_route_map(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears['r2'] + + def _bgp_converge(router): + output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json")) + expected = { + '192.168.255.1': { + 'bgpState': 'Established', + 'addressFamilyInfo': { + 'ipv4Unicast': { + 'acceptedPrefixCounter': 1 + } + } + } + } + return topotest.json_cmp(output, expected) + + def _bgp_default_route_has_metric(router): + output = json.loads(router.vtysh_cmd("show ip bgp 0.0.0.0/0 json")) + expected = { + 'paths': [ + { + 'med': 123 + } + ] + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge, router) + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + + assert result is None, 'Failed to see bgp convergence in "{}"'.format(router) + + test_func = functools.partial(_bgp_default_route_has_metric, router) + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + + assert result is None, 'Failed to see applied metric for default route in "{}"'.format(router) + +if __name__ == '__main__': + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_up.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_up.py index 21543ab78e..4ecaa4c026 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_up.py +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_up.py @@ -23,15 +23,16 @@ for rtr in rtrs: mem_z[rtr] = {'value': int(found.group(1)), 'units': found.group(2)} mem_b[rtr] = {'value': int(found.group(3)), 'units': found.group(4)} -luCommand('ce1', 'vtysh -c "sharp data nexthop"', 'sharpd is not running', 'none','check if sharpd running') -doSharp = True +luCommand('ce1', 'vtysh -c "show mem"', 'qmem sharpd', 'none','check if sharpd running') +doSharp = False found = luLast() if ret != False and found != None: if len(found.group()): - luCommand('ce1', 'vtysh -c "sharp data nexthop"', 'sharpd is not running', 'pass','sharpd NOT running, skipping test') - doSharp = False + doSharp = True -if doSharp == True: +if doSharp != True: + luCommand('ce1', 'vtysh -c "sharp data nexthop"', '.', 'pass','sharpd NOT running, skipping test') +else: luCommand('ce1', 'vtysh -c "sharp install routes 10.0.0.0 nexthop 99.0.0.1 {}"'.format(num),'','pass','Adding {} routes'.format(num)) luCommand('ce2', 'vtysh -c "sharp install routes 10.0.0.0 nexthop 99.0.0.2 {}"'.format(num),'','pass','Adding {} routes'.format(num)) rtrs = ['ce1', 'ce2', 'ce3'] diff --git a/tests/topotests/bgp_maximum_prefix_out/__init__.py b/tests/topotests/bgp_maximum_prefix_out/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/bgp_maximum_prefix_out/__init__.py diff --git a/tests/topotests/bgp_maximum_prefix_out/r1/bgpd.conf b/tests/topotests/bgp_maximum_prefix_out/r1/bgpd.conf new file mode 100644 index 0000000000..9a68809631 --- /dev/null +++ b/tests/topotests/bgp_maximum_prefix_out/r1/bgpd.conf @@ -0,0 +1,9 @@ +! +router bgp 65001 + neighbor 192.168.255.1 remote-as 65002 + address-family ipv4 unicast + redistribute connected + neighbor 192.168.255.1 maximum-prefix-out 2 + exit-address-family + ! +! diff --git a/tests/topotests/bgp_maximum_prefix_out/r1/zebra.conf b/tests/topotests/bgp_maximum_prefix_out/r1/zebra.conf new file mode 100644 index 0000000000..24162258b7 --- /dev/null +++ b/tests/topotests/bgp_maximum_prefix_out/r1/zebra.conf @@ -0,0 +1,13 @@ +! +interface lo + ip address 172.16.255.250/32 + ip address 172.16.255.251/32 + ip address 172.16.255.252/32 + ip address 172.16.255.253/32 + ip address 172.16.255.254/32 +! +interface r1-eth0 + ip address 192.168.255.2/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_maximum_prefix_out/r2/bgpd.conf b/tests/topotests/bgp_maximum_prefix_out/r2/bgpd.conf new file mode 100644 index 0000000000..1659c4bec4 --- /dev/null +++ b/tests/topotests/bgp_maximum_prefix_out/r2/bgpd.conf @@ -0,0 +1,6 @@ +! +router bgp 65002 + neighbor 192.168.255.2 remote-as 65001 + exit-address-family + ! +! diff --git a/tests/topotests/bgp_maximum_prefix_out/r2/zebra.conf b/tests/topotests/bgp_maximum_prefix_out/r2/zebra.conf new file mode 100644 index 0000000000..08dd374dee --- /dev/null +++ b/tests/topotests/bgp_maximum_prefix_out/r2/zebra.conf @@ -0,0 +1,6 @@ +! +interface r2-eth0 + ip address 192.168.255.1/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_maximum_prefix_out/test_bgp_maximum_prefix_out.py b/tests/topotests/bgp_maximum_prefix_out/test_bgp_maximum_prefix_out.py new file mode 100644 index 0000000000..d77aa5aff2 --- /dev/null +++ b/tests/topotests/bgp_maximum_prefix_out/test_bgp_maximum_prefix_out.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python + +# +# test_bgp_maximum_prefix_out.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 by +# Donatas Abraitis <donatas.abraitis@gmail.com> +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +Test if `neighbor <X.X.X.X> maximum-prefix-out <Y>` is working +correctly. +""" + +import os +import sys +import json +import time +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, '../')) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from mininet.topo import Topo + +class TemplateTopo(Topo): + def build(self, *_args, **_opts): + tgen = get_topogen(self) + + for routern in range(1, 3): + tgen.add_router('r{}'.format(routern)) + + switch = tgen.add_switch('s1') + switch.add_link(tgen.gears['r1']) + switch.add_link(tgen.gears['r2']) + +def setup_module(mod): + tgen = Topogen(TemplateTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.iteritems(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, + os.path.join(CWD, '{}/zebra.conf'.format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, + os.path.join(CWD, '{}/bgpd.conf'.format(rname)) + ) + + tgen.start_router() + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + +def test_bgp_maximum_prefix_out(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears['r2'] + + def _bgp_converge(router): + output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.2 json")) + expected = { + '192.168.255.2': { + 'bgpState': 'Established', + 'addressFamilyInfo': { + 'ipv4Unicast': { + 'acceptedPrefixCounter': 2 + } + } + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge, router) + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + + assert result is None, 'Failed bgp convergence in "{}"'.format(router) + +if __name__ == '__main__': + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/lib/ltemplate.py b/tests/topotests/lib/ltemplate.py index 1d12d11a26..a76d8e4b08 100644 --- a/tests/topotests/lib/ltemplate.py +++ b/tests/topotests/lib/ltemplate.py @@ -134,6 +134,7 @@ def teardown_module(mod): tgen = get_topogen() if _lt != None and _lt.scriptdir != None and _lt.prestarthooksuccess == True: + luShowResults(logger.info) print(luFinish()) # This function tears down the whole topology. diff --git a/tests/topotests/lib/lutil.py b/tests/topotests/lib/lutil.py index 7c89ada013..4ea97a3692 100755 --- a/tests/topotests/lib/lutil.py +++ b/tests/topotests/lib/lutil.py @@ -336,6 +336,14 @@ def luNumPass(): def luResult(target, success, str, logstr=None): return LUtil.result(target, success, str, logstr) +def luShowResults(prFunction): + printed = 0 + sf = open(LUtil.fsum_name, 'r') + for line in sf: + printed+=1 + prFunction(line.rstrip()) + sf.close() + def luShowFail(): printed = 0 sf = open(LUtil.fsum_name, 'r') diff --git a/tests/topotests/ospf6-topo1/r1/ip_6_address.nhg.ref b/tests/topotests/ospf6-topo1/r1/ip_6_address.nhg.ref new file mode 100644 index 0000000000..11fd9fe3c4 --- /dev/null +++ b/tests/topotests/ospf6-topo1/r1/ip_6_address.nhg.ref @@ -0,0 +1,10 @@ +fc00:1111:1111:1111::/64 nhid XXXX via fc00:1:1:1::1234 dev r1-stubnet proto XXXX metric 20 pref medium +fc00:1:1:1::/64 dev r1-stubnet proto XXXX metric 256 pref medium +fc00:2222:2222:2222::/64 nhid XXXX via fe80::__(r2-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium +fc00:2:2:2::/64 nhid XXXX via fe80::__(r2-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium +fc00:3333:3333:3333::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium +fc00:3:3:3::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium +fc00:4444:4444:4444::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium +fc00:4:4:4::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium +fc00:a:a:a::/64 dev r1-sw5 proto XXXX metric 256 pref medium +fc00:b:b:b::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium diff --git a/tests/topotests/ospf6-topo1/test_ospf6_topo1.py b/tests/topotests/ospf6-topo1/test_ospf6_topo1.py index b70ae02266..cb0c4af221 100755 --- a/tests/topotests/ospf6-topo1/test_ospf6_topo1.py +++ b/tests/topotests/ospf6-topo1/test_ospf6_topo1.py @@ -89,7 +89,7 @@ sys.path.append(os.path.join(CWD, '../')) from lib import topotest from lib.topogen import Topogen, TopoRouter, get_topogen from lib.topolog import logger - +import platform ##################################################### ## @@ -319,7 +319,10 @@ def test_linux_ipv6_kernel_routingTable(): # Now compare the routing tables (after substituting link-local addresses) for i in range(1, 5): - refTableFile = os.path.join(CWD, 'r{}/ip_6_address.ref'.format(i)) + if topotest.version_cmp(platform.release(), '5.3') < 0: + refTableFile = os.path.join(CWD, 'r{}/ip_6_address.ref'.format(i)) + else: + refTableFile = os.path.join(CWD, 'r{}/ip_6_address.nhg.ref'.format(i)) if os.path.isfile(refTableFile): expected = open(refTableFile).read().rstrip() @@ -333,6 +336,7 @@ def test_linux_ipv6_kernel_routingTable(): actual = actual.replace(ll[1], "fe80::__(%s)__" % ll[0]) # Mask out protocol name or number actual = re.sub(r"[ ]+proto [0-9a-z]+ +", " proto XXXX ", actual) + actual = re.sub(r"[ ]+nhid [0-9]+ +", " nhid XXXX ", actual) # Remove ff00::/8 routes (seen on some kernels - not from FRR) actual = re.sub(r'ff00::/8.*', '', actual) diff --git a/tests/topotests/pim-basic/r1/bgpd.conf b/tests/topotests/pim-basic/r1/bgpd.conf new file mode 100644 index 0000000000..8acaac96a0 --- /dev/null +++ b/tests/topotests/pim-basic/r1/bgpd.conf @@ -0,0 +1,3 @@ +router bgp 65001 + neighbor 10.0.30.3 remote-as external + redistribute connected diff --git a/tests/topotests/pim-basic/r1/pimd.conf b/tests/topotests/pim-basic/r1/pimd.conf index 5740c66e24..cec765699d 100644 --- a/tests/topotests/pim-basic/r1/pimd.conf +++ b/tests/topotests/pim-basic/r1/pimd.conf @@ -2,9 +2,12 @@ hostname r1 ! interface r1-eth0 ip igmp - ip pim sm + ip pim +! +interface r1-eth1 + ip pim ! interface lo - ip pim sm + ip pim ! -ip pim rp 10.254.0.1 +ip pim rp 10.254.0.3 diff --git a/tests/topotests/pim-basic/r1/rp-info.json b/tests/topotests/pim-basic/r1/rp-info.json new file mode 100644 index 0000000000..1f713c2d28 --- /dev/null +++ b/tests/topotests/pim-basic/r1/rp-info.json @@ -0,0 +1,9 @@ +{ + "10.254.0.3":[ + { + "outboundInterface":"r1-eth1", + "group":"224.0.0.0\/4", + "source":"Static" + } + ] +} diff --git a/tests/topotests/pim-basic/r1/zebra.conf b/tests/topotests/pim-basic/r1/zebra.conf index 2bf71294d0..b0a25f12aa 100644 --- a/tests/topotests/pim-basic/r1/zebra.conf +++ b/tests/topotests/pim-basic/r1/zebra.conf @@ -3,6 +3,9 @@ hostname r1 interface r1-eth0 ip address 10.0.20.1/24 ! +interface r1-eth1 + ip address 10.0.30.1/24 +! interface lo ip address 10.254.0.1/32 ! diff --git a/tests/topotests/pim-basic/rp/bgpd.conf b/tests/topotests/pim-basic/rp/bgpd.conf new file mode 100644 index 0000000000..6b16c067a5 --- /dev/null +++ b/tests/topotests/pim-basic/rp/bgpd.conf @@ -0,0 +1,3 @@ +router bgp 65003 + neighbor 10.0.30.1 remote-as external + redistribute connected diff --git a/tests/topotests/pim-basic/rp/pimd.conf b/tests/topotests/pim-basic/rp/pimd.conf new file mode 100644 index 0000000000..3f1b4d65c9 --- /dev/null +++ b/tests/topotests/pim-basic/rp/pimd.conf @@ -0,0 +1,9 @@ +hostname rp +! +interface rp-eth0 + ip pim +! +interface lo + ip pim +! +ip pim rp 10.254.0.3 diff --git a/tests/topotests/pim-basic/rp/upstream.json b/tests/topotests/pim-basic/rp/upstream.json new file mode 100644 index 0000000000..c33dea49e9 --- /dev/null +++ b/tests/topotests/pim-basic/rp/upstream.json @@ -0,0 +1,17 @@ +{ + "229.1.1.1":{ + "10.0.20.2":{ + "sourceStream":true, + "inboundInterface":"rp-eth0", + "rpfAddress":"10.0.20.2", + "source":"10.0.20.2", + "group":"229.1.1.1", + "state":"NotJ", + "joinState":"NotJoined", + "regState":"RegNoInfo", + "resetTimer":"--:--:--", + "refCount":1, + "sptBit":0 + } + } +} diff --git a/tests/topotests/pim-basic/rp/zebra.conf b/tests/topotests/pim-basic/rp/zebra.conf new file mode 100644 index 0000000000..0a1359ecd0 --- /dev/null +++ b/tests/topotests/pim-basic/rp/zebra.conf @@ -0,0 +1,8 @@ +hostname rp +! +interface rp-eth0 + ip address 10.0.30.3/24 +! +interface lo + ip address 10.254.0.3/32 +! diff --git a/tests/topotests/pim-basic/test_pim.py b/tests/topotests/pim-basic/test_pim.py index 6d54b8f2f0..0e0569e234 100644 --- a/tests/topotests/pim-basic/test_pim.py +++ b/tests/topotests/pim-basic/test_pim.py @@ -28,6 +28,8 @@ test_pim.py: Test pim import os import sys import pytest +import json +from functools import partial CWD = os.path.dirname(os.path.realpath(__file__)) sys.path.append(os.path.join(CWD, '../')) @@ -47,11 +49,27 @@ class PIMTopo(Topo): for routern in range(1, 3): tgen.add_router('r{}'.format(routern)) + tgen.add_router('rp') + + # r1 -> .1 + # r2 -> .2 + # rp -> .3 + # loopback network is 10.254.0.X/32 + # # r1 <- sw1 -> r2 + # r1-eth0 <-> r2-eth0 + # 10.0.20.0/24 sw = tgen.add_switch('sw1') sw.add_link(tgen.gears['r1']) sw.add_link(tgen.gears['r2']) + # r1 <- sw2 -> rp + # r1-eth1 <-> rp-eth0 + # 10.0.30.0/24 + sw = tgen.add_switch('sw2') + sw.add_link(tgen.gears['r1']) + sw.add_link(tgen.gears['rp']) + def setup_module(mod): "Sets up the pytest environment" @@ -68,9 +86,14 @@ def setup_module(mod): TopoRouter.RD_PIM, os.path.join(CWD, '{}/pimd.conf'.format(rname)) ) + router.load_config( + TopoRouter.RD_BGP, + os.path.join(CWD, '{}/bgpd.conf'.format(rname)) + ) # After loading the configurations, this function loads configured daemons. tgen.start_router() + #tgen.mininet_cli() def teardown_module(mod): @@ -80,6 +103,22 @@ def teardown_module(mod): # This function tears down the whole topology. tgen.stop_topology() +def test_pim_rp_setup(): + "Ensure basic routing has come up and the rp has an outgoing interface" + #Ensure rp and r1 establish pim neighbor ship and bgp has come up + #Finally ensure that the rp has an outgoing interface on r1 + tgen = get_topogen() + + r1 = tgen.gears['r1'] + json_file = '{}/{}/rp-info.json'.format(CWD, r1.name) + expected = json.loads(open(json_file).read()) + + test_func = partial(topotest.router_json_cmp, + r1, 'show ip pim rp-info json', expected) + _, result = topotest.run_and_expect(test_func, None, count=15, wait=5) + assertmsg = '"{}" JSON output mismatches'.format(r1.name) + assert result is None, assertmsg + #tgen.mininet_cli() def test_pim_send_mcast_stream(): "Establish a Multicast stream from r2 -> r1 and then ensure S,G is created as appropriate" @@ -90,6 +129,7 @@ def test_pim_send_mcast_stream(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) + rp = tgen.gears['rp'] r2 = tgen.gears['r2'] r1 = tgen.gears['r1'] @@ -111,7 +151,21 @@ def test_pim_send_mcast_stream(): } assert topotest.json_cmp(out, expected) is None, 'failed to converge pim' + #tgen.mininet_cli() + +def test_pim_rp_sees_stream(): + "Ensure that the RP sees the stream and has acted accordingly" + tgen = get_topogen() + + rp = tgen.gears['rp'] + json_file = '{}/{}/upstream.json'.format(CWD, rp.name) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + rp, 'show ip pim upstream json', expected) + _, result = topotest.run_and_expect(test_func, None, count=20, wait=.5) + assertmsg = '"{}" JSON output mismatches'.format(rp.name) + assert result is None, assertmsg def test_pim_igmp_report(): "Send a igmp report from r2->r1 and ensure that the *,G state is created on r1" diff --git a/tools/frr-reload.py b/tools/frr-reload.py index 3e97635dfe..45843faf13 100755 --- a/tools/frr-reload.py +++ b/tools/frr-reload.py @@ -934,6 +934,16 @@ def ignore_delete_re_add_lines(lines_to_add, lines_to_del): lines_to_del_to_del.append((ctx_keys, route_target_export_line)) lines_to_add_to_del.append((ctx_keys, route_target_both_line)) + # Deleting static routes under a vrf can lead to time-outs if each is sent + # as separate vtysh -c commands. Change them from being in lines_to_del and + # put the "no" form in lines_to_add + if ctx_keys[0].startswith('vrf ') and line: + if (line.startswith('ip route') or + line.startswith('ipv6 route')): + add_cmd = ('no ' + line) + lines_to_add.append((ctx_keys, add_cmd)) + lines_to_del_to_del.append((ctx_keys, line)) + if not deleted: found_add_line = line_exist(lines_to_add, ctx_keys, line) @@ -1054,6 +1064,19 @@ def compare_context_objects(newconf, running): for line in running_ctx.lines: lines_to_del.append((running_ctx_keys, line)) + # Some commands can happen at higher counts that make + # doing vtysh -c inefficient (and can time out.) For + # these commands, instead of adding them to lines_to_del, + # add the "no " version to lines_to_add. + elif (running_ctx_keys[0].startswith('ip route') or + running_ctx_keys[0].startswith('ipv6 route') or + running_ctx_keys[0].startswith('access-list') or + running_ctx_keys[0].startswith('ipv6 access-list') or + running_ctx_keys[0].startswith('ip prefix-list') or + running_ctx_keys[0].startswith('ipv6 prefix-list')): + add_cmd = ('no ' + running_ctx_keys[0],) + lines_to_add.append((add_cmd, None)) + # Non-global context elif running_ctx_keys and not any("address-family" in key for key in running_ctx_keys): lines_to_del.append((running_ctx_keys, None)) @@ -1392,6 +1415,11 @@ if __name__ == '__main__': if line == '!': continue + # Don't run "no" commands twice since they can error + # out the second time due to first deletion + if x == 1 and ctx_keys[0].startswith('no '): + continue + cmd = line_for_vtysh_file(ctx_keys, line, False) lines_to_configure.append(cmd) diff --git a/vrrpd/subdir.am b/vrrpd/subdir.am index d81594ad93..07358e0383 100644 --- a/vrrpd/subdir.am +++ b/vrrpd/subdir.am @@ -7,7 +7,7 @@ noinst_LIBRARIES += vrrpd/libvrrp.a sbin_PROGRAMS += vrrpd/vrrpd # dist_examples_DATA += staticd/staticd.conf.sample vtysh_scan += $(top_srcdir)/vrrpd/vrrp_vty.c -man8 += $(MANBUILD)/vrrpd.8 +man8 += $(MANBUILD)/frr-vrrpd.8 endif vrrpd_libvrrp_a_SOURCES = \ diff --git a/watchfrr/subdir.am b/watchfrr/subdir.am index 30f606c202..36af57cf82 100644 --- a/watchfrr/subdir.am +++ b/watchfrr/subdir.am @@ -5,7 +5,7 @@ if WATCHFRR sbin_PROGRAMS += watchfrr/watchfrr vtysh_scan += $(top_srcdir)/watchfrr/watchfrr_vty.c -man8 += $(MANBUILD)/watchfrr.8 +man8 += $(MANBUILD)/frr-watchfrr.8 endif noinst_HEADERS += \ diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c index c3d5bf8428..90d3aeb482 100644 --- a/zebra/kernel_netlink.c +++ b/zebra/kernel_netlink.c @@ -592,6 +592,7 @@ struct rtattr *addattr_nest(struct nlmsghdr *n, int maxlen, int type) struct rtattr *nest = NLMSG_TAIL(n); addattr_l(n, maxlen, type, NULL, 0); + nest->rta_type |= NLA_F_NESTED; return nest; } @@ -606,6 +607,7 @@ struct rtattr *rta_nest(struct rtattr *rta, int maxlen, int type) struct rtattr *nest = RTA_TAIL(rta); rta_addattr_l(rta, maxlen, type, NULL, 0); + nest->rta_type |= NLA_F_NESTED; return nest; } diff --git a/zebra/label_manager.c b/zebra/label_manager.c index 6e58f4b925..caebdc0f08 100644 --- a/zebra/label_manager.c +++ b/zebra/label_manager.c @@ -262,8 +262,12 @@ assign_specific_label_chunk(uint8_t proto, unsigned short instance, * included in the previous one */ for (node = first_node; node && (node != last_node); node = next) { + struct label_manager_chunk *death; + next = listnextnode(node); + death = listgetdata(node); list_delete_node(lbl_mgr.lc_list, node); + delete_label_chunk(death); } lmc = create_label_chunk(proto, instance, keep, base, end); diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 29a341abbd..dd6e62ee6c 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -2353,7 +2353,7 @@ int netlink_nexthop_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) unsigned char family; int type; afi_t afi = AFI_UNSPEC; - vrf_id_t vrf_id = 0; + vrf_id_t vrf_id = VRF_DEFAULT; struct interface *ifp = NULL; struct nhmsg *nhm = NULL; struct nexthop nh = {}; diff --git a/zebra/subdir.am b/zebra/subdir.am index 916f6cb5d1..77ed5a6caa 100644 --- a/zebra/subdir.am +++ b/zebra/subdir.am @@ -36,7 +36,7 @@ if LINUX module_LTLIBRARIES += zebra/zebra_cumulus_mlag.la endif -man8 += $(MANBUILD)/zebra.8 +man8 += $(MANBUILD)/frr-zebra.8 ## endif ZEBRA endif diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index 5a63c1e4f6..db54e6f25b 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -41,6 +41,7 @@ #include "lib/vrf.h" #include "lib/libfrr.h" #include "lib/sockopt.h" +#include "lib/lib_errors.h" #include "zebra/zebra_router.h" #include "zebra/rib.h" @@ -147,6 +148,25 @@ static int zserv_encode_nexthop(struct stream *s, struct nexthop *nexthop) return 1; } +/* + * Zebra error addition adds error type. + * + * + * 0 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | enum zebra_error_types | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + */ +static void zserv_encode_error(struct stream *s, enum zebra_error_types error) +{ + stream_put(s, &error, sizeof(error)); + + /* Write packet size. */ + stream_putw_at(s, 0, stream_get_endp(s)); +} + /* Send handlers ----------------------------------------------------------- */ /* Interface is added. Send ZEBRA_INTERFACE_ADD to client. */ @@ -1397,7 +1417,7 @@ static void zread_route_add(ZAPI_HANDLER_ARGS) struct nexthop *nexthop = NULL; struct nexthop_group *ng = NULL; int i, ret; - vrf_id_t vrf_id = 0; + vrf_id_t vrf_id; struct ipaddr vtep_ip; s = msg; @@ -1593,6 +1613,14 @@ static void zread_route_add(ZAPI_HANDLER_ARGS) if (CHECK_FLAG(api.message, ZAPI_MESSAGE_SRCPFX)) src_p = &api.src_prefix; + if (api.safi != SAFI_UNICAST && api.safi != SAFI_MULTICAST) { + flog_warn(EC_LIB_ZAPI_MISSMATCH, + "%s: Received safi: %d but we can only accept UNICAST or MULTICAST", + __func__, api.safi); + nexthop_group_delete(&ng); + XFREE(MTYPE_RE, re); + return; + } ret = rib_add_multipath(afi, api.safi, &api.prefix, src_p, re, ng); /* Stats */ @@ -2363,14 +2391,19 @@ static inline void zread_rule(ZAPI_HANDLER_ARGS) if (!(zpr.rule.filter.src_ip.family == AF_INET || zpr.rule.filter.src_ip.family == AF_INET6)) { - zlog_warn("Unsupported PBR source IP family: %s\n", - family2str(zpr.rule.filter.src_ip.family)); + zlog_warn( + "Unsupported PBR source IP family: %s (%" PRIu8 + ")\n", + family2str(zpr.rule.filter.src_ip.family), + zpr.rule.filter.src_ip.family); return; } if (!(zpr.rule.filter.dst_ip.family == AF_INET || zpr.rule.filter.dst_ip.family == AF_INET6)) { - zlog_warn("Unsupported PBR dest IP family: %s\n", - family2str(zpr.rule.filter.dst_ip.family)); + zlog_warn("Unsupported PBR IP family: %s (%" PRIu8 + ")\n", + family2str(zpr.rule.filter.dst_ip.family), + zpr.rule.filter.dst_ip.family); return; } @@ -2518,6 +2551,36 @@ stream_failure: return; } +static void zsend_error_msg(struct zserv *client, enum zebra_error_types error, + struct zmsghdr *bad_hdr) +{ + + struct stream *s = stream_new(ZEBRA_MAX_PACKET_SIZ); + + zclient_create_header(s, ZEBRA_ERROR, bad_hdr->vrf_id); + + zserv_encode_error(s, error); + + client->error_cnt++; + zserv_send_message(client, s); +} + +static void zserv_error_no_vrf(ZAPI_HANDLER_ARGS) +{ + if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV) + zlog_debug("ZAPI message specifies unknown VRF: %d", + hdr->vrf_id); + + return zsend_error_msg(client, ZEBRA_NO_VRF, hdr); +} + +static void zserv_error_invalid_msg_type(ZAPI_HANDLER_ARGS) +{ + zlog_info("Zebra received unknown command %d", hdr->command); + + return zsend_error_msg(client, ZEBRA_INVALID_MSG_TYPE, hdr); +} + void (*const zserv_handlers[])(ZAPI_HANDLER_ARGS) = { [ZEBRA_ROUTER_ID_ADD] = zread_router_id_add, [ZEBRA_ROUTER_ID_DELETE] = zread_router_id_delete, @@ -2643,16 +2706,12 @@ void zserv_handle_commands(struct zserv *client, struct stream *msg) /* lookup vrf */ zvrf = zebra_vrf_lookup_by_id(hdr.vrf_id); - if (!zvrf) { - if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV) - zlog_debug("ZAPI message specifies unknown VRF: %d", - hdr.vrf_id); - return; - } + if (!zvrf) + return zserv_error_no_vrf(client, &hdr, msg, zvrf); if (hdr.command >= array_size(zserv_handlers) || zserv_handlers[hdr.command] == NULL) - zlog_info("Zebra received unknown command %d", hdr.command); - else - zserv_handlers[hdr.command](client, &hdr, msg, zvrf); + return zserv_error_invalid_msg_type(client, &hdr, msg, zvrf); + + zserv_handlers[hdr.command](client, &hdr, msg, zvrf); } diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c index 4f41406a5c..7430307320 100644 --- a/zebra/zebra_nhg.c +++ b/zebra/zebra_nhg.c @@ -478,7 +478,7 @@ static void handle_recursive_depend(struct nhg_connected_tree_head *nhg_depends, struct nhg_hash_entry *depend = NULL; struct nexthop_group resolved_ng = {}; - nexthop_group_add_sorted(&resolved_ng, nh); + resolved_ng.nexthop = nh; depend = zebra_nhg_rib_find(0, &resolved_ng, afi); depends_add(nhg_depends, depend); @@ -507,7 +507,7 @@ static bool zebra_nhg_find(struct nhg_hash_entry **nhe, uint32_t id, if (lookup.nhg->nexthop->next) { /* Groups can have all vrfs and AF's in them */ lookup.afi = AFI_UNSPEC; - lookup.vrf_id = 0; + lookup.vrf_id = VRF_DEFAULT; } else { switch (lookup.nhg->nexthop->type) { case (NEXTHOP_TYPE_IFINDEX): @@ -1048,25 +1048,55 @@ int zebra_nhg_kernel_del(uint32_t id) } /* Some dependency helper functions */ -static struct nhg_hash_entry *depends_find(const struct nexthop *nh, afi_t afi) +static struct nhg_hash_entry *depends_find_recursive(const struct nexthop *nh, + afi_t afi) { - struct nexthop lookup; - struct nhg_hash_entry *nhe = NULL; + struct nhg_hash_entry *nhe; + struct nexthop *lookup = NULL; - if (!nh) - goto done; + lookup = nexthop_dup(nh, NULL); + + nhe = zebra_nhg_find_nexthop(0, lookup, afi, 0); + + nexthops_free(lookup); + + return nhe; +} + +static struct nhg_hash_entry *depends_find_singleton(const struct nexthop *nh, + afi_t afi) +{ + struct nhg_hash_entry *nhe; + struct nexthop lookup = {}; /* Capture a snapshot of this single nh; it might be part of a list, * so we need to make a standalone copy. */ - memset(&lookup, 0, sizeof(lookup)); - nexthop_copy(&lookup, nh, NULL); + nexthop_copy_no_recurse(&lookup, nh, NULL); nhe = zebra_nhg_find_nexthop(0, &lookup, afi, 0); /* The copy may have allocated labels; free them if necessary. */ nexthop_del_labels(&lookup); + return nhe; +} + +static struct nhg_hash_entry *depends_find(const struct nexthop *nh, afi_t afi) +{ + struct nhg_hash_entry *nhe = NULL; + + if (!nh) + goto done; + + /* We are separating these functions out to increase handling speed + * in the non-recursive case (by not alloc/freeing) + */ + if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_RECURSIVE)) + nhe = depends_find_recursive(nh, afi); + else + nhe = depends_find_singleton(nh, afi); + done: return nhe; } diff --git a/zebra/zebra_pbr.c b/zebra/zebra_pbr.c index 0c3adcdfa1..fe7a93a50c 100644 --- a/zebra/zebra_pbr.c +++ b/zebra/zebra_pbr.c @@ -652,12 +652,22 @@ static void *pbr_iptable_alloc_intern(void *arg) { struct zebra_pbr_iptable *zpi; struct zebra_pbr_iptable *new; + struct listnode *ln; + char *ifname; zpi = (struct zebra_pbr_iptable *)arg; new = XCALLOC(MTYPE_TMP, sizeof(struct zebra_pbr_iptable)); + /* Deep structure copy */ memcpy(new, zpi, sizeof(*zpi)); + new->interface_name_list = list_new(); + + if (zpi->interface_name_list) { + for (ALL_LIST_ELEMENTS_RO(zpi->interface_name_list, ln, ifname)) + listnode_add(new->interface_name_list, + XSTRDUP(MTYPE_PBR_IPTABLE_IFNAME, ifname)); + } return new; } diff --git a/zebra/zebra_ptm.c b/zebra/zebra_ptm.c index b48756302a..d7bbe779bd 100644 --- a/zebra/zebra_ptm.c +++ b/zebra/zebra_ptm.c @@ -1295,6 +1295,7 @@ static void zebra_ptm_send_bfdd(struct stream *msg) } stream_free(msgc); + stream_free(msg); } static void zebra_ptm_send_clients(struct stream *msg) @@ -1326,6 +1327,7 @@ static void zebra_ptm_send_clients(struct stream *msg) } stream_free(msgc); + stream_free(msg); } static int _zebra_ptm_bfd_client_deregister(struct zserv *zs) @@ -1421,6 +1423,7 @@ static void _zebra_ptm_reroute(struct zserv *zs, struct zebra_vrf *zvrf, stream_putw_at(msgc, 0, STREAM_READABLE(msgc)); zebra_ptm_send_bfdd(msgc); + msgc = NULL; /* Registrate process PID for shutdown hook. */ STREAM_GETL(msg, ppid); @@ -1429,7 +1432,8 @@ static void _zebra_ptm_reroute(struct zserv *zs, struct zebra_vrf *zvrf, return; stream_failure: - stream_free(msgc); + if (msgc) + stream_free(msgc); zlog_err("%s:%d failed to registrate client pid", __FILE__, __LINE__); } diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index b85bf83923..c8b96011dc 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -1351,9 +1351,10 @@ DEFPY (show_interface_nexthop_group, DEFPY (show_nexthop_group, show_nexthop_group_cmd, - "show nexthop-group <(0-4294967295)$id|[<ip$v4|ipv6$v6>] [vrf <NAME$vrf_name|all$vrf_all>]>", + "show nexthop-group rib <(0-4294967295)$id|[<ip$v4|ipv6$v6>] [vrf <NAME$vrf_name|all$vrf_all>]>", SHOW_STR "Show Nexthop Groups\n" + "RIB information\n" "Nexthop Group ID\n" IP_STR IP6_STR diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index 564573dcb3..ffb2528a24 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -8624,9 +8624,8 @@ void zebra_vxlan_macvlan_down(struct interface *ifp) struct interface *ifp; ifp = if_lookup_by_index_all_vrf(zif->link_ifindex); - zlog_debug("macvlan %s parent link is not found. Parent index %d ifp %s", - ifp->name, zif->link_ifindex, - ifp ? ifp->name : " "); + zlog_debug("macvlan parent link is not found. Parent index %d ifp %s", + zif->link_ifindex, ifp ? ifp->name : " "); } return; } @@ -9487,7 +9486,7 @@ void zebra_vxlan_advertise_subnet(ZAPI_HANDLER_ARGS) s = msg; STREAM_GETC(s, advertise); - vni = stream_get3(s); + STREAM_GET(&vni, s, 3); zvni = zvni_lookup(vni); if (!zvni) diff --git a/zebra/zserv.c b/zebra/zserv.c index 419f30e6d3..cca926f3b0 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -591,8 +591,10 @@ static void zserv_client_free(struct zserv *client) /* Free bitmaps. */ for (afi_t afi = AFI_IP; afi < AFI_MAX; afi++) { - for (int i = 0; i < ZEBRA_ROUTE_MAX; i++) + for (int i = 0; i < ZEBRA_ROUTE_MAX; i++) { vrf_bitmap_free(client->redist[afi][i]); + redist_del_all_instances(&client->mi_redist[afi][i]); + } vrf_bitmap_free(client->redist_default[afi]); } diff --git a/zebra/zserv.h b/zebra/zserv.h index ccc8d92aa2..d8d82a52ec 100644 --- a/zebra/zserv.h +++ b/zebra/zserv.h @@ -146,6 +146,7 @@ struct zserv { uint32_t v6_nh_watch_rem_cnt; uint32_t vxlan_sg_add_cnt; uint32_t vxlan_sg_del_cnt; + uint32_t error_cnt; time_t nh_reg_time; time_t nh_dereg_time; |
