diff --git a/bgpd/bgp_addpath.h b/bgpd/bgp_addpath.h index d562000e3000..b19e63c946ac 100644 --- a/bgpd/bgp_addpath.h +++ b/bgpd/bgp_addpath.h @@ -21,6 +21,12 @@ struct bgp_addpath_capability { uint8_t flags; }; +struct bgp_paths_limit_capability { + uint16_t afi; + uint8_t safi; + uint16_t paths_limit; +}; + #define BGP_ADDPATH_TX_ID_FOR_DEFAULT_ORIGINATE 1 void bgp_addpath_init_bgp_data(struct bgp_addpath_bgp_data *d); diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c index 234dbb0715a5..657c7e22d7e4 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -260,6 +260,8 @@ static struct peer *peer_xfer_conn(struct peer *from_peer) peer->afc_recv[afi][safi] = from_peer->afc_recv[afi][safi]; peer->orf_plist[afi][safi] = from_peer->orf_plist[afi][safi]; peer->llgr[afi][safi] = from_peer->llgr[afi][safi]; + peer->addpath_paths_limit[afi][safi] = + from_peer->addpath_paths_limit[afi][safi]; } if (bgp_getsockname(peer) < 0) { diff --git a/bgpd/bgp_open.c b/bgpd/bgp_open.c index 154efdedaf35..7df918f65217 100644 --- a/bgpd/bgp_open.c +++ b/bgpd/bgp_open.c @@ -42,6 +42,7 @@ const struct message capcode_str[] = { { CAPABILITY_CODE_LLGR, "Long-lived BGP Graceful Restart" }, { CAPABILITY_CODE_ROLE, "Role" }, { CAPABILITY_CODE_SOFT_VERSION, "Software Version" }, + { CAPABILITY_CODE_PATHS_LIMIT, "Paths-Limit" }, { 0 } }; @@ -61,6 +62,7 @@ static const size_t cap_minsizes[] = { [CAPABILITY_CODE_LLGR] = CAPABILITY_CODE_LLGR_LEN, [CAPABILITY_CODE_ROLE] = CAPABILITY_CODE_ROLE_LEN, [CAPABILITY_CODE_SOFT_VERSION] = CAPABILITY_CODE_SOFT_VERSION_LEN, + [CAPABILITY_CODE_PATHS_LIMIT] = CAPABILITY_CODE_PATHS_LIMIT_LEN, }; /* value the capability must be a multiple of. @@ -83,6 +85,7 @@ static const size_t cap_modsizes[] = { [CAPABILITY_CODE_LLGR] = 1, [CAPABILITY_CODE_ROLE] = 1, [CAPABILITY_CODE_SOFT_VERSION] = 1, + [CAPABILITY_CODE_PATHS_LIMIT] = 5, }; /* BGP-4 Multiprotocol Extentions lead us to the complex world. We can @@ -739,6 +742,62 @@ static int bgp_capability_addpath(struct peer *peer, return 0; } +static int bgp_capability_paths_limit(struct peer *peer, + struct capability_header *hdr) +{ + struct stream *s = BGP_INPUT(peer); + size_t end = stream_get_getp(s) + hdr->length; + + if (hdr->length % CAPABILITY_CODE_PATHS_LIMIT_LEN) { + flog_warn(EC_BGP_CAPABILITY_INVALID_LENGTH, + "Paths-Limit: Received invalid length %d, non-multiple of %d", + hdr->length, CAPABILITY_CODE_PATHS_LIMIT_LEN); + return -1; + } + + if (!CHECK_FLAG(peer->cap, PEER_CAP_ADDPATH_RCV)) { + flog_warn(EC_BGP_CAPABILITY_INVALID_DATA, + "Paths-Limit: Received Paths-Limit capability without Add-Path capability"); + return -1; + } + + SET_FLAG(peer->cap, PEER_CAP_PATHS_LIMIT_RCV); + + while (stream_get_getp(s) + CAPABILITY_CODE_PATHS_LIMIT_LEN <= end) { + afi_t afi; + safi_t safi; + iana_afi_t pkt_afi = stream_getw(s); + iana_safi_t pkt_safi = stream_getc(s); + uint16_t paths_limit = stream_getw(s); + + if (bgp_debug_neighbor_events(peer)) + zlog_debug("%s OPEN has %s capability for afi/safi: %s/%s limit: %u", + peer->host, + lookup_msg(capcode_str, hdr->code, NULL), + iana_afi2str(pkt_afi), + iana_safi2str(pkt_safi), paths_limit); + + if (bgp_map_afi_safi_iana2int(pkt_afi, pkt_safi, &afi, &safi)) { + if (bgp_debug_neighbor_events(peer)) + zlog_debug("%s Addr-family %s/%s(afi/safi) not supported. Ignore the Paths-Limit capability for this AFI/SAFI", + peer->host, iana_afi2str(pkt_afi), + iana_safi2str(pkt_safi)); + continue; + } else if (!peer->afc[afi][safi]) { + if (bgp_debug_neighbor_events(peer)) + zlog_debug("%s Addr-family %s/%s(afi/safi) not enabled. Ignore the Paths-Limit capability for this AFI/SAFI", + peer->host, iana_afi2str(pkt_afi), + iana_safi2str(pkt_safi)); + continue; + } + + SET_FLAG(peer->af_cap[afi][safi], PEER_CAP_PATHS_LIMIT_AF_RCV); + peer->addpath_paths_limit[afi][safi].receive = paths_limit; + } + + return 0; +} + static int bgp_capability_enhe(struct peer *peer, struct capability_header *hdr) { struct stream *s = BGP_INPUT(peer); @@ -1012,6 +1071,7 @@ static int bgp_capability_parse(struct peer *peer, size_t length, case CAPABILITY_CODE_EXT_MESSAGE: case CAPABILITY_CODE_ROLE: case CAPABILITY_CODE_SOFT_VERSION: + case CAPABILITY_CODE_PATHS_LIMIT: /* Check length. */ if (caphdr.length < cap_minsizes[caphdr.code]) { zlog_info( @@ -1113,6 +1173,9 @@ static int bgp_capability_parse(struct peer *peer, size_t length, case CAPABILITY_CODE_SOFT_VERSION: ret = bgp_capability_software_version(peer, &caphdr); break; + case CAPABILITY_CODE_PATHS_LIMIT: + ret = bgp_capability_paths_limit(peer, &caphdr); + break; default: if (caphdr.code > 128) { /* We don't send Notification for unknown vendor @@ -1874,6 +1937,31 @@ uint16_t bgp_open_capability(struct stream *s, struct peer *peer, } } + /* Paths-Limit capability */ + SET_FLAG(peer->cap, PEER_CAP_PATHS_LIMIT_ADV); + stream_putc(s, BGP_OPEN_OPT_CAP); + ext_opt_params ? stream_putw(s, (CAPABILITY_CODE_PATHS_LIMIT_LEN * + afi_safi_count) + + 2) + : stream_putc(s, (CAPABILITY_CODE_PATHS_LIMIT_LEN * + afi_safi_count) + + 2); + stream_putc(s, CAPABILITY_CODE_PATHS_LIMIT); + stream_putc(s, CAPABILITY_CODE_PATHS_LIMIT_LEN * afi_safi_count); + + FOREACH_AFI_SAFI (afi, safi) { + if (!peer->afc[afi][safi]) + continue; + + bgp_map_afi_safi_int2iana(afi, safi, &pkt_afi, &pkt_safi); + + stream_putw(s, pkt_afi); + stream_putc(s, pkt_safi); + stream_putw(s, peer->addpath_paths_limit[afi][safi].send); + + SET_FLAG(peer->af_cap[afi][safi], PEER_CAP_PATHS_LIMIT_AF_ADV); + } + /* ORF capability. */ FOREACH_AFI_SAFI (afi, safi) { if (CHECK_FLAG(peer->af_flags[afi][safi], diff --git a/bgpd/bgp_open.h b/bgpd/bgp_open.h index 34f4b7619edc..a01e49ceba51 100644 --- a/bgpd/bgp_open.h +++ b/bgpd/bgp_open.h @@ -53,6 +53,7 @@ struct graceful_restart_af { #define CAPABILITY_CODE_ENHE 5 /* Extended Next Hop Encoding */ #define CAPABILITY_CODE_EXT_MESSAGE 6 /* Extended Message Support */ #define CAPABILITY_CODE_ROLE 9 /* Role Capability */ +#define CAPABILITY_CODE_PATHS_LIMIT 76 /* Paths Limit Capability */ /* Capability Length */ #define CAPABILITY_CODE_MP_LEN 4 @@ -61,6 +62,7 @@ struct graceful_restart_af { #define CAPABILITY_CODE_RESTART_LEN 2 /* Receiving only case */ #define CAPABILITY_CODE_AS4_LEN 4 #define CAPABILITY_CODE_ADDPATH_LEN 4 +#define CAPABILITY_CODE_PATHS_LIMIT_LEN 5 #define CAPABILITY_CODE_ENHE_LEN 6 /* NRLI AFI = 2, SAFI = 2, Nexthop AFI = 2 */ #define CAPABILITY_CODE_MIN_FQDN_LEN 2 #define CAPABILITY_CODE_ENHANCED_LEN 0 diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index 6a290962b66b..29c52e78153a 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -1461,6 +1461,49 @@ void bgp_capability_send(struct peer *peer, afi_t afi, safi_t safi, capability, iana_afi2str(pkt_afi), iana_safi2str(pkt_safi)); + break; + case CAPABILITY_CODE_PATHS_LIMIT: + SET_FLAG(peer->cap, PEER_CAP_PATHS_LIMIT_ADV); + + FOREACH_AFI_SAFI (afi, safi) { + if (!peer->afc[afi][safi]) + continue; + + addpath_afi_safi_count++; + } + + stream_putc(s, action); + stream_putc(s, CAPABILITY_CODE_PATHS_LIMIT); + stream_putc(s, CAPABILITY_CODE_PATHS_LIMIT_LEN * + addpath_afi_safi_count); + + FOREACH_AFI_SAFI (afi, safi) { + if (!peer->afc[afi][safi]) + continue; + + bgp_map_afi_safi_int2iana(afi, safi, &pkt_afi, + &pkt_safi); + + stream_putw(s, pkt_afi); + stream_putc(s, pkt_safi); + stream_putw(s, + peer->addpath_paths_limit[afi][safi].send); + + SET_FLAG(peer->af_cap[afi][safi], + PEER_CAP_PATHS_LIMIT_AF_ADV); + + if (bgp_debug_neighbor_events(peer)) + zlog_debug("%pBP sending CAPABILITY has %s %s for afi/safi: %s/%s, limit: %u", + peer, + action == CAPABILITY_ACTION_SET + ? "Advertising" + : "Removing", + capability, iana_afi2str(pkt_afi), + iana_safi2str(pkt_safi), + peer->addpath_paths_limit[afi][safi] + .send); + } + break; case CAPABILITY_CODE_ORF: /* Convert AFI, SAFI to values for packet. */ @@ -3169,6 +3212,85 @@ static void bgp_dynamic_capability_addpath(uint8_t *pnt, int action, } } +static void bgp_dynamic_capability_paths_limit(uint8_t *pnt, int action, + struct capability_header *hdr, + struct peer *peer) +{ + uint8_t *data = pnt + 3; + uint8_t *end = data + hdr->length; + size_t len = end - data; + afi_t afi; + safi_t safi; + + if (action == CAPABILITY_ACTION_SET) { + if (len % CAPABILITY_CODE_PATHS_LIMIT_LEN) { + flog_warn(EC_BGP_CAPABILITY_INVALID_LENGTH, + "Paths-Limit: Received invalid length %zu, non-multiple of %d", + len, CAPABILITY_CODE_PATHS_LIMIT_LEN); + return; + } + + if (!CHECK_FLAG(peer->cap, PEER_CAP_ADDPATH_RCV)) { + flog_warn(EC_BGP_CAPABILITY_INVALID_DATA, + "Paths-Limit: Received Paths-Limit capability without Add-Path capability"); + goto ignore; + } + + SET_FLAG(peer->cap, PEER_CAP_PATHS_LIMIT_RCV); + + while (data + CAPABILITY_CODE_PATHS_LIMIT_LEN <= end) { + afi_t afi; + safi_t safi; + iana_afi_t pkt_afi; + iana_safi_t pkt_safi; + struct bgp_paths_limit_capability bpl = {}; + + memcpy(&bpl, data, sizeof(bpl)); + pkt_afi = ntohs(bpl.afi); + pkt_safi = safi_int2iana(bpl.safi); + + if (bgp_debug_neighbor_events(peer)) + zlog_debug("%s OPEN has %s capability for afi/safi: %s/%s limit: %u", + peer->host, + lookup_msg(capcode_str, hdr->code, + NULL), + iana_afi2str(pkt_afi), + iana_safi2str(pkt_safi), + bpl.paths_limit); + + if (bgp_map_afi_safi_iana2int(pkt_afi, pkt_safi, &afi, + &safi)) { + if (bgp_debug_neighbor_events(peer)) + zlog_debug("%s Addr-family %s/%s(afi/safi) not supported. Ignore the Paths-Limit capability for this AFI/SAFI", + peer->host, + iana_afi2str(pkt_afi), + iana_safi2str(pkt_safi)); + goto ignore; + } else if (!peer->afc[afi][safi]) { + if (bgp_debug_neighbor_events(peer)) + zlog_debug("%s Addr-family %s/%s(afi/safi) not enabled. Ignore the Paths-Limit capability for this AFI/SAFI", + peer->host, + iana_afi2str(pkt_afi), + iana_safi2str(pkt_safi)); + goto ignore; + } + + SET_FLAG(peer->af_cap[afi][safi], + PEER_CAP_PATHS_LIMIT_AF_RCV); + peer->addpath_paths_limit[afi][safi].receive = + bpl.paths_limit; +ignore: + data += CAPABILITY_CODE_PATHS_LIMIT_LEN; + } + } else { + FOREACH_AFI_SAFI (afi, safi) + UNSET_FLAG(peer->af_cap[afi][safi], + PEER_CAP_PATHS_LIMIT_AF_RCV); + + UNSET_FLAG(peer->cap, PEER_CAP_PATHS_LIMIT_RCV); + } +} + static void bgp_dynamic_capability_orf(uint8_t *pnt, int action, struct capability_header *hdr, struct peer *peer) @@ -3722,6 +3844,10 @@ static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt, case CAPABILITY_CODE_ADDPATH: bgp_dynamic_capability_addpath(pnt, action, hdr, peer); break; + case CAPABILITY_CODE_PATHS_LIMIT: + bgp_dynamic_capability_paths_limit(pnt, action, hdr, + peer); + break; case CAPABILITY_CODE_ORF: bgp_dynamic_capability_orf(pnt, action, hdr, peer); break; diff --git a/bgpd/bgp_updgrp.c b/bgpd/bgp_updgrp.c index d13515af6f4b..c522865ebabb 100644 --- a/bgpd/bgp_updgrp.c +++ b/bgpd/bgp_updgrp.c @@ -145,6 +145,8 @@ static void conf_copy(struct peer *dst, struct peer *src, afi_t afi, dst->addpath_type[afi][safi] = src->addpath_type[afi][safi]; dst->addpath_best_selected[afi][safi] = src->addpath_best_selected[afi][safi]; + dst->addpath_paths_limit[afi][safi] = + src->addpath_paths_limit[afi][safi]; dst->local_as = src->local_as; dst->change_local_as = src->change_local_as; dst->shared_network = src->shared_network; @@ -348,6 +350,8 @@ static unsigned int updgrp_hash_key_make(const void *p) key = jhash_1word((flags & PEER_UPDGRP_AF_FLAGS), key); key = jhash_1word((uint32_t)peer->addpath_type[afi][safi], key); key = jhash_1word(peer->addpath_best_selected[afi][safi], key); + key = jhash_1word(peer->addpath_paths_limit[afi][safi].receive, key); + key = jhash_1word(peer->addpath_paths_limit[afi][safi].send, key); key = jhash_1word((peer->cap & PEER_UPDGRP_CAP_FLAGS), key); key = jhash_1word((peer->af_cap[afi][safi] & PEER_UPDGRP_AF_CAP_FLAGS), key); @@ -461,6 +465,9 @@ static unsigned int updgrp_hash_key_make(const void *p) PEER_UPDGRP_AF_CAP_FLAGS), peer->v_routeadv, peer->change_local_as, peer->as_path_loop_detection); + zlog_debug("%pBP Update Group Hash: addpath paths-limit: (send %u, receive %u)", + peer, peer->addpath_paths_limit[afi][safi].send, + peer->addpath_paths_limit[afi][safi].receive); zlog_debug( "%pBP Update Group Hash: max packet size: %u pmax_out: %u Peer Group: %s rmap out: %s", peer, peer->max_packet_size, peer->pmax_out[afi][safi], diff --git a/bgpd/bgp_updgrp_adv.c b/bgpd/bgp_updgrp_adv.c index 7ecebe3020cf..cc039e3e11f5 100644 --- a/bgpd/bgp_updgrp_adv.c +++ b/bgpd/bgp_updgrp_adv.c @@ -97,13 +97,19 @@ subgrp_announce_addpath_best_selected(struct bgp_dest *dest, enum bgp_path_selection_reason reason; char pfx_buf[PREFIX2STR_BUFFER] = {}; int paths_eq = 0; - int best_path_count = 0; struct list *list = list_new(); struct bgp_path_info *pi = NULL; + uint16_t paths_count = 0; + uint16_t paths_limit = peer->addpath_paths_limit[afi][safi].receive; if (peer->addpath_type[afi][safi] == BGP_ADDPATH_BEST_SELECTED) { - while (best_path_count++ < - peer->addpath_best_selected[afi][safi]) { + paths_limit = + paths_limit + ? MIN(paths_limit, + peer->addpath_best_selected[afi][safi]) + : peer->addpath_best_selected[afi][safi]; + + while (paths_count++ < paths_limit) { struct bgp_path_info *exist = NULL; for (pi = bgp_dest_get_bgp_path_info(dest); pi; @@ -139,8 +145,26 @@ subgrp_announce_addpath_best_selected(struct bgp_dest *dest, subgroup_process_announce_selected( subgrp, NULL, dest, afi, safi, id); } else { - subgroup_process_announce_selected(subgrp, pi, dest, - afi, safi, id); + /* No Paths-Limit involved */ + if (!paths_limit) { + subgroup_process_announce_selected(subgrp, pi, + dest, afi, + safi, id); + continue; + } + + /* If we have Paths-Limit capability, we MUST + * not send more than the number of paths expected + * by the peer. + */ + if (paths_count++ < paths_limit) + subgroup_process_announce_selected(subgrp, pi, + dest, afi, + safi, id); + else + subgroup_process_announce_selected(subgrp, NULL, + dest, afi, + safi, id); } } diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 2a917155362b..1b6638ee8afd 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -9141,6 +9141,63 @@ DEFPY( return CMD_SUCCESS; } +DEFPY (neighbor_addpath_paths_limit, + neighbor_addpath_paths_limit_cmd, + "neighbor $neighbor addpath-rx-paths-limit (1-65535)$paths_limit", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Paths Limit for Addpath to receive from the peer\n" + "Maximum number of paths\n") +{ + struct peer *peer; + afi_t afi = bgp_node_afi(vty); + safi_t safi = bgp_node_safi(vty); + int ret; + + peer = peer_and_group_lookup_vty(vty, neighbor); + if (!peer) + return CMD_WARNING_CONFIG_FAILED; + + ret = peer_af_flag_set_vty(vty, neighbor, afi, safi, + PEER_FLAG_ADDPATH_RX_PATHS_LIMIT); + + peer->addpath_paths_limit[afi][safi].send = paths_limit; + + bgp_capability_send(peer, afi, safi, CAPABILITY_CODE_PATHS_LIMIT, + CAPABILITY_ACTION_SET); + + return ret; +} + +DEFPY (no_neighbor_addpath_paths_limit, + no_neighbor_addpath_paths_limit_cmd, + "no neighbor $neighbor addpath-rx-paths-limit [(1-65535)]", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Paths Limit for Addpath to receive from the peer\n" + "Maximum number of paths\n") +{ + struct peer *peer; + afi_t afi = bgp_node_afi(vty); + safi_t safi = bgp_node_safi(vty); + int ret; + + peer = peer_and_group_lookup_vty(vty, neighbor); + if (!peer) + return CMD_WARNING_CONFIG_FAILED; + + ret = peer_af_flag_unset_vty(vty, neighbor, afi, safi, + PEER_FLAG_ADDPATH_RX_PATHS_LIMIT); + + peer->addpath_paths_limit[afi][safi].send = 0; + + bgp_capability_send(peer, afi, safi, CAPABILITY_CODE_PATHS_LIMIT, + CAPABILITY_ACTION_UNSET); + + return ret; +} + DEFPY( no_neighbor_aspath_loop_detection, no_neighbor_aspath_loop_detection_cmd, @@ -14089,6 +14146,86 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, json_add); } + /* Paths-Limit */ + if (CHECK_FLAG(p->cap, PEER_CAP_PATHS_LIMIT_RCV) || + CHECK_FLAG(p->cap, PEER_CAP_PATHS_LIMIT_ADV)) { + json_object *json_add = NULL; + const char *print_store; + + json_add = json_object_new_object(); + + FOREACH_AFI_SAFI (afi, safi) { + json_object *json_sub = NULL; + + json_sub = json_object_new_object(); + print_store = get_afi_safi_str(afi, safi, + true); + + if (CHECK_FLAG(p->af_cap[afi][safi], + PEER_CAP_PATHS_LIMIT_AF_ADV) || + CHECK_FLAG(p->af_cap[afi][safi], + PEER_CAP_PATHS_LIMIT_AF_RCV)) { + if (CHECK_FLAG(p->af_cap[afi][safi], + PEER_CAP_PATHS_LIMIT_AF_ADV) && + CHECK_FLAG(p->af_cap[afi][safi], + PEER_CAP_PATHS_LIMIT_AF_RCV)) { + json_object_boolean_true_add( + json_sub, + "advertisedAndReceived"); + json_object_int_add( + json_sub, + "advertisedPathsLimit", + p->addpath_paths_limit + [afi][safi] + .send); + json_object_int_add( + json_sub, + "receivedPathsLimit", + p->addpath_paths_limit + [afi][safi] + .receive); + } else if (CHECK_FLAG(p->af_cap[afi] + [safi], + PEER_CAP_PATHS_LIMIT_AF_ADV)) { + json_object_boolean_true_add( + json_sub, + "advertised"); + json_object_int_add( + json_sub, + "advertisedPathsLimit", + p->addpath_paths_limit + [afi][safi] + .send); + } else if (CHECK_FLAG(p->af_cap[afi] + [safi], + PEER_CAP_PATHS_LIMIT_AF_RCV)) { + json_object_boolean_true_add( + json_sub, + "received"); + json_object_int_add( + json_sub, + "receivedPathsLimit", + p->addpath_paths_limit + [afi][safi] + .receive); + } + } + + if (CHECK_FLAG(p->af_cap[afi][safi], + PEER_CAP_PATHS_LIMIT_AF_ADV) || + CHECK_FLAG(p->af_cap[afi][safi], + PEER_CAP_PATHS_LIMIT_AF_RCV)) + json_object_object_add(json_add, + print_store, + json_sub); + else + json_object_free(json_sub); + } + + json_object_object_add(json_cap, "pathsLimit", + json_add); + } + /* Dynamic */ if (CHECK_FLAG(p->cap, PEER_CAP_DYNAMIC_RCV) || CHECK_FLAG(p->cap, PEER_CAP_DYNAMIC_ADV)) { @@ -14542,6 +14679,47 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, } } + /* Paths-Limit */ + if (CHECK_FLAG(p->cap, PEER_CAP_PATHS_LIMIT_RCV) || + CHECK_FLAG(p->cap, PEER_CAP_PATHS_LIMIT_ADV)) { + vty_out(vty, " Paths-Limit:\n"); + + FOREACH_AFI_SAFI (afi, safi) { + if (CHECK_FLAG(p->af_cap[afi][safi], + PEER_CAP_PATHS_LIMIT_AF_ADV) || + CHECK_FLAG(p->af_cap[afi][safi], + PEER_CAP_PATHS_LIMIT_AF_RCV)) { + vty_out(vty, " %s: ", + get_afi_safi_str(afi, + safi, + false)); + + if (CHECK_FLAG(p->af_cap[afi][safi], + PEER_CAP_PATHS_LIMIT_AF_ADV)) + vty_out(vty, + "advertised (%u)", + p->addpath_paths_limit + [afi][safi] + .send); + + if (CHECK_FLAG(p->af_cap[afi][safi], + PEER_CAP_PATHS_LIMIT_AF_RCV)) + vty_out(vty, + "%sreceived (%u)", + CHECK_FLAG(p->af_cap[afi] + [safi], + PEER_CAP_PATHS_LIMIT_AF_ADV) + ? " and " + : "", + p->addpath_paths_limit + [afi][safi] + .receive); + + vty_out(vty, "\n"); + } + } + } + /* Dynamic */ if (CHECK_FLAG(p->cap, PEER_CAP_DYNAMIC_RCV) || CHECK_FLAG(p->cap, PEER_CAP_DYNAMIC_ADV)) { @@ -18320,6 +18498,11 @@ static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp, if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_DISABLE_ADDPATH_RX)) vty_out(vty, " neighbor %s disable-addpath-rx\n", addr); + if (CHECK_FLAG(peer->af_flags[afi][safi], + PEER_FLAG_ADDPATH_RX_PATHS_LIMIT)) + vty_out(vty, " neighbor %s addpath-rx-paths-limit %u\n", addr, + peer->addpath_paths_limit[afi][safi].send); + /* ORF capability. */ if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_ORF_PREFIX_SM) || peergroup_af_flag_check(peer, afi, safi, @@ -20466,6 +20649,26 @@ void bgp_vty_init(void) install_element(BGP_VPNV6_NODE, &no_neighbor_addpath_tx_bestpath_per_as_cmd); + /* "neighbor addpath-rx-paths-limit" commands.*/ + install_element(BGP_NODE, &neighbor_addpath_paths_limit_cmd); + install_element(BGP_NODE, &no_neighbor_addpath_paths_limit_cmd); + install_element(BGP_IPV4_NODE, &neighbor_addpath_paths_limit_cmd); + install_element(BGP_IPV4_NODE, &no_neighbor_addpath_paths_limit_cmd); + install_element(BGP_IPV4M_NODE, &neighbor_addpath_paths_limit_cmd); + install_element(BGP_IPV4M_NODE, &no_neighbor_addpath_paths_limit_cmd); + install_element(BGP_IPV4L_NODE, &neighbor_addpath_paths_limit_cmd); + install_element(BGP_IPV4L_NODE, &no_neighbor_addpath_paths_limit_cmd); + install_element(BGP_IPV6_NODE, &neighbor_addpath_paths_limit_cmd); + install_element(BGP_IPV6_NODE, &no_neighbor_addpath_paths_limit_cmd); + install_element(BGP_IPV6M_NODE, &neighbor_addpath_paths_limit_cmd); + install_element(BGP_IPV6M_NODE, &no_neighbor_addpath_paths_limit_cmd); + install_element(BGP_IPV6L_NODE, &neighbor_addpath_paths_limit_cmd); + install_element(BGP_IPV6L_NODE, &no_neighbor_addpath_paths_limit_cmd); + install_element(BGP_VPNV4_NODE, &neighbor_addpath_paths_limit_cmd); + install_element(BGP_VPNV4_NODE, &no_neighbor_addpath_paths_limit_cmd); + install_element(BGP_VPNV6_NODE, &neighbor_addpath_paths_limit_cmd); + install_element(BGP_VPNV6_NODE, &no_neighbor_addpath_paths_limit_cmd); + /* "neighbor sender-as-path-loop-detection" commands. */ install_element(BGP_NODE, &neighbor_aspath_loop_detection_cmd); install_element(BGP_NODE, &no_neighbor_aspath_loop_detection_cmd); diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 90ac529f8cfb..1ffab3ac289d 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -1523,6 +1523,8 @@ struct peer *peer_new(struct bgp *bgp) PEER_FLAG_SEND_LARGE_COMMUNITY); peer->addpath_type[afi][safi] = BGP_ADDPATH_NONE; peer->addpath_best_selected[afi][safi] = 0; + peer->addpath_paths_limit[afi][safi].receive = 0; + peer->addpath_paths_limit[afi][safi].send = 0; peer->soo[afi][safi] = NULL; } @@ -1613,6 +1615,8 @@ void peer_xfer_config(struct peer *peer_dst, struct peer *peer_src) peer_dst->weight[afi][safi] = peer_src->weight[afi][safi]; peer_dst->addpath_type[afi][safi] = peer_src->addpath_type[afi][safi]; + peer_dst->addpath_paths_limit[afi][safi] = + peer_src->addpath_paths_limit[afi][safi]; } for (afidx = BGP_AF_START; afidx < BGP_AF_MAX; afidx++) { @@ -4604,6 +4608,7 @@ static const struct peer_flag_action peer_af_flag_action_list[] = { {PEER_FLAG_DISABLE_ADDPATH_RX, 0, peer_change_none}, {PEER_FLAG_SOO, 0, peer_change_reset}, {PEER_FLAG_ACCEPT_OWN, 0, peer_change_reset}, + {PEER_FLAG_ADDPATH_RX_PATHS_LIMIT, 0, peer_change_none}, {0, 0, 0}}; /* Proper action set. */ diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 385a2f01e889..1efcd48869c1 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -1140,6 +1140,11 @@ struct llgr_info { uint8_t flags; }; +struct addpath_paths_limit { + uint16_t send; + uint16_t receive; +}; + struct peer_connection { struct peer *peer; @@ -1333,6 +1338,8 @@ struct peer { #define PEER_CAP_ROLE_RCV (1ULL << 26) /* role received */ #define PEER_CAP_SOFT_VERSION_ADV (1ULL << 27) #define PEER_CAP_SOFT_VERSION_RCV (1ULL << 28) +#define PEER_CAP_PATHS_LIMIT_ADV (1U << 29) +#define PEER_CAP_PATHS_LIMIT_RCV (1U << 30) /* Capability flags (reset in bgp_stop) */ uint32_t af_cap[AFI_MAX][SAFI_MAX]; @@ -1351,6 +1358,8 @@ struct peer { #define PEER_CAP_ENHE_AF_NEGO (1U << 14) /* Extended nexthop afi/safi negotiated */ #define PEER_CAP_LLGR_AF_ADV (1U << 15) #define PEER_CAP_LLGR_AF_RCV (1U << 16) +#define PEER_CAP_PATHS_LIMIT_AF_ADV (1U << 17) +#define PEER_CAP_PATHS_LIMIT_AF_RCV (1U << 18) /* Global configuration flags. */ /* @@ -1526,6 +1535,7 @@ struct peer { #define PEER_FLAG_MAX_PREFIX_FORCE (1ULL << 26) #define PEER_FLAG_DISABLE_ADDPATH_RX (1ULL << 27) #define PEER_FLAG_SOO (1ULL << 28) +#define PEER_FLAG_ADDPATH_RX_PATHS_LIMIT (1ULL << 29) #define PEER_FLAG_ACCEPT_OWN (1ULL << 63) enum bgp_addpath_strat addpath_type[AFI_MAX][SAFI_MAX]; @@ -1843,6 +1853,9 @@ struct peer { /* Add-Path Best selected paths number to advertise */ uint8_t addpath_best_selected[AFI_MAX][SAFI_MAX]; + /* Add-Path Paths-Limit */ + struct addpath_paths_limit addpath_paths_limit[AFI_MAX][SAFI_MAX]; + QOBJ_FIELDS; }; DECLARE_QOBJ_TYPE(peer);