Merge pull request #9880 from louis-oui/maximum-prefix-out

bgpd: fixes maximum prefix out
This commit is contained in:
Donatas Abraitis 2022-01-27 08:49:31 +02:00 committed by GitHub
commit 6766acddbf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 250 additions and 31 deletions

View File

@ -95,6 +95,9 @@ static void bgp_conditional_adv_routes(struct peer *peer, afi_t afi,
if (!subgrp)
return;
subgrp->pscount = 0;
SET_FLAG(subgrp->sflags, SUBGRP_STATUS_TABLE_REPARSING);
if (BGP_DEBUG(update, UPDATE_OUT))
zlog_debug("%s: %s routes to/from %s for %s", __func__,
update_type == ADVERTISE ? "Advertise" : "Withdraw",
@ -162,6 +165,7 @@ static void bgp_conditional_adv_routes(struct peer *peer, afi_t afi,
}
}
}
UNSET_FLAG(subgrp->sflags, SUBGRP_STATUS_TABLE_REPARSING);
}
/* Handler of conditional advertisement timer event.

View File

@ -1866,6 +1866,17 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi,
piattr = bgp_path_info_mpath_count(pi) ? bgp_path_info_mpath_attr(pi)
: pi->attr;
if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_OUT) &&
peer->pmax_out[afi][safi] != 0 &&
subgrp->pscount >= 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 (%u)",
peer->host, peer->pmax_out[afi][safi]);
}
return false;
}
#ifdef ENABLE_BGP_VNC
if (((afi == AFI_IP) || (afi == AFI_IP6)) && (safi == SAFI_MPLS_VPN)
&& ((pi->type == ZEBRA_ROUTE_BGP_DIRECT)

View File

@ -306,6 +306,7 @@ static void *updgrp_hash_alloc(void *p)
* 14. encoding both global and link-local nexthop?
* 15. If peer is configured to be a lonesoul, peer ip address
* 16. Local-as should match, if configured.
* 17. maximum-prefix-out
* )
*/
static unsigned int updgrp_hash_key_make(const void *p)
@ -340,6 +341,7 @@ static unsigned int updgrp_hash_key_make(const void *p)
key = jhash_1word(peer->v_routeadv, key);
key = jhash_1word(peer->change_local_as, key);
key = jhash_1word(peer->max_packet_size, key);
key = jhash_1word(peer->pmax_out[afi][safi], key);
if (peer->group)
key = jhash_1word(jhash(peer->group->name,
@ -453,6 +455,9 @@ static bool updgrp_hash_cmp(const void *p1, const void *p2)
if (pe1->change_local_as != pe2->change_local_as)
return false;
if (pe1->pmax_out[afi][safi] != pe2->pmax_out[afi][safi])
return false;
/* flags like route reflector client */
if ((flags1 & PEER_UPDGRP_AF_FLAGS) != (flags2 & PEER_UPDGRP_AF_FLAGS))
return false;

View File

@ -204,6 +204,9 @@ struct update_subgroup {
/* send prefix count */
uint32_t scount;
/* send prefix count prior to packet update */
uint32_t pscount;
/* announcement attribute hash */
struct hash *hash;
@ -248,6 +251,7 @@ struct update_subgroup {
uint16_t sflags;
#define SUBGRP_STATUS_DEFAULT_ORIGINATE (1 << 0)
#define SUBGRP_STATUS_FORCE_UPDATES (1 << 1)
#define SUBGRP_STATUS_TABLE_REPARSING (1 << 2)
uint16_t flags;
#define SUBGRP_FLAG_NEEDS_REFRESH (1 << 0)

View File

@ -481,13 +481,18 @@ void bgp_adj_out_set_subgroup(struct bgp_dest *dest,
dest, subgrp,
bgp_addpath_id_for_peer(peer, afi, safi, &path->tx_addpath));
if (!adj) {
if (adj) {
if (CHECK_FLAG(subgrp->sflags, SUBGRP_STATUS_TABLE_REPARSING))
subgrp->pscount++;
} else {
adj = bgp_adj_out_alloc(
subgrp, dest,
bgp_addpath_id_for_peer(peer, afi, safi,
&path->tx_addpath));
if (!adj)
return;
subgrp->pscount++;
}
/* Check if we are sending the same route. This is needed to
@ -607,6 +612,8 @@ void bgp_adj_out_unset_subgroup(struct bgp_dest *dest,
/* Free allocated information. */
adj_free(adj);
}
if (!CHECK_FLAG(subgrp->sflags, SUBGRP_STATUS_TABLE_REPARSING))
subgrp->pscount--;
}
subgrp->version = MAX(subgrp->version, dest->version);
@ -669,6 +676,9 @@ void subgroup_announce_table(struct update_subgroup *subgrp,
PEER_FLAG_DEFAULT_ORIGINATE))
subgroup_default_originate(subgrp, 0);
subgrp->pscount = 0;
SET_FLAG(subgrp->sflags, SUBGRP_STATUS_TABLE_REPARSING);
for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) {
const struct prefix *dest_p = bgp_dest_get_prefix(dest);
@ -722,6 +732,7 @@ void subgroup_announce_table(struct update_subgroup *subgrp,
}
}
}
UNSET_FLAG(subgrp->sflags, SUBGRP_STATUS_TABLE_REPARSING);
/*
* We walked through the whole table -- make sure our version number

View File

@ -709,21 +709,6 @@ 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 (%u)",
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 =
@ -876,7 +861,6 @@ 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);
}

View File

@ -7666,6 +7666,7 @@ DEFUN(neighbor_maximum_prefix_out,
"Maximum number of prefixes to be sent to this peer\n"
"Maximum no. of prefix limit\n")
{
int ret;
int idx_peer = 1;
int idx_number = 3;
struct peer *peer;
@ -7679,20 +7680,21 @@ DEFUN(neighbor_maximum_prefix_out,
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;
ret = peer_maximum_prefix_out_set(peer, afi, safi, max);
return CMD_SUCCESS;
return bgp_vty_return(vty, ret);
}
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 neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix-out [(1-4294967295)]",
NO_STR
NEIGHBOR_STR
NEIGHBOR_ADDR_STR2
"Maximum number of prefixes to be sent to this peer\n")
"Maximum number of prefixes to be sent to this peer\n"
"Maximum no. of prefix limit\n")
{
int ret;
int idx_peer = 2;
struct peer *peer;
afi_t afi = bgp_node_afi(vty);
@ -7702,10 +7704,9 @@ DEFUN(no_neighbor_maximum_prefix_out,
if (!peer)
return CMD_WARNING_CONFIG_FAILED;
UNSET_FLAG(peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_OUT);
peer->pmax_out[afi][safi] = 0;
ret = peer_maximum_prefix_out_unset(peer, afi, safi);
return CMD_SUCCESS;
return bgp_vty_return(vty, ret);
}
/* Maximum number of prefix configuration. Prefix count is different

View File

@ -2012,6 +2012,10 @@ static void peer_group2peer_config_copy_af(struct peer_group *group,
PEER_ATTR_INHERIT(peer, group, pmax_restart[afi][safi]);
}
/* maximum-prefix-out */
if (!CHECK_FLAG(pflags_ovrd, PEER_FLAG_MAX_PREFIX_OUT))
PEER_ATTR_INHERIT(peer, group, pmax_out[afi][safi]);
/* allowas-in */
if (!CHECK_FLAG(pflags_ovrd, PEER_FLAG_ALLOWAS_IN))
PEER_ATTR_INHERIT(peer, group, allowas_in[afi][safi]);
@ -4220,6 +4224,7 @@ static const struct peer_flag_action peer_af_flag_action_list[] = {
{PEER_FLAG_MAX_PREFIX, 0, peer_change_none},
{PEER_FLAG_MAX_PREFIX_WARNING, 0, peer_change_none},
{PEER_FLAG_MAX_PREFIX_FORCE, 0, peer_change_none},
{PEER_FLAG_MAX_PREFIX_OUT, 0, peer_change_none},
{PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED, 0, peer_change_reset_out},
{PEER_FLAG_FORCE_NEXTHOP_SELF, 1, peer_change_reset_out},
{PEER_FLAG_REMOVE_PRIVATE_AS_ALL, 1, peer_change_reset_out},
@ -7319,6 +7324,96 @@ int peer_maximum_prefix_unset(struct peer *peer, afi_t afi, safi_t safi)
return 0;
}
void peer_maximum_prefix_out_refresh_routes(struct peer *peer, afi_t afi,
safi_t safi)
{
update_group_adjust_peer(peer_af_find(peer, afi, safi));
if (peer_established(peer))
bgp_announce_route(peer, afi, safi, false);
}
int peer_maximum_prefix_out_set(struct peer *peer, afi_t afi, safi_t safi,
uint32_t max)
{
struct peer *member;
struct listnode *node, *nnode;
/* Set flag on peer and peer-group member if any */
peer_af_flag_set(peer, afi, safi, PEER_FLAG_MAX_PREFIX_OUT);
/* Set configuration on peer. */
peer->pmax_out[afi][safi] = max;
/* Check if handling a regular peer. */
if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
/* Skip peer-group mechanics for regular peers. */
peer_maximum_prefix_out_refresh_routes(peer, afi, safi);
return 0;
}
/*
* Set flag and configuration on all peer-group members, unless they
* are explicitely overriding peer-group configuration.
*/
for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
/* Skip peers with overridden configuration. */
if (CHECK_FLAG(member->af_flags_override[afi][safi],
PEER_FLAG_MAX_PREFIX_OUT))
continue;
/* Set configuration on peer-group member. */
member->pmax_out[afi][safi] = max;
peer_maximum_prefix_out_refresh_routes(member, afi, safi);
}
return 0;
}
int peer_maximum_prefix_out_unset(struct peer *peer, afi_t afi, safi_t safi)
{
struct peer *member;
struct listnode *node;
/* Inherit configuration from peer-group if peer is member. */
if (peer_group_active(peer)) {
peer_af_flag_inherit(peer, afi, safi, PEER_FLAG_MAX_PREFIX_OUT);
PEER_ATTR_INHERIT(peer, peer->group, pmax_out[afi][safi]);
peer_maximum_prefix_out_refresh_routes(peer, afi, safi);
return 0;
}
/* Remove flag and configuration from peer. */
peer_af_flag_unset(peer, afi, safi, PEER_FLAG_MAX_PREFIX_OUT);
peer->pmax_out[afi][safi] = 0;
/* Check if handling a regular peer. */
if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
/* Skip peer-group mechanics for regular peers. */
peer_maximum_prefix_out_refresh_routes(peer, afi, safi);
return 0;
}
/*
* Remove flag and configuration from all peer-group members, unless
* they are explicitely overriding peer-group configuration.
*/
for (ALL_LIST_ELEMENTS_RO(peer->group->peer, node, member)) {
/* Skip peers with overridden configuration. */
if (CHECK_FLAG(member->af_flags_override[afi][safi],
PEER_FLAG_MAX_PREFIX_OUT))
continue;
/* Remove flag and configuration on peer-group member.
*/
UNSET_FLAG(member->af_flags[afi][safi],
PEER_FLAG_MAX_PREFIX_OUT);
member->pmax_out[afi][safi] = 0;
peer_maximum_prefix_out_refresh_routes(member, afi, safi);
}
return 0;
}
int is_ebgp_multihop_configured(struct peer *peer)
{
struct peer_group *group;

View File

@ -2207,6 +2207,13 @@ extern int peer_maximum_prefix_set(struct peer *, afi_t, safi_t, uint32_t,
uint8_t, int, uint16_t, bool force);
extern int peer_maximum_prefix_unset(struct peer *, afi_t, safi_t);
extern void peer_maximum_prefix_out_refresh_routes(struct peer *peer, afi_t afi,
safi_t safi);
extern int peer_maximum_prefix_out_set(struct peer *peer, afi_t afi,
safi_t safi, uint32_t max);
extern int peer_maximum_prefix_out_unset(struct peer *peer, afi_t afi,
safi_t safi);
extern int peer_clear(struct peer *, struct listnode **);
extern int peer_clear_soft(struct peer *, afi_t, safi_t, enum bgp_clear_type);

View File

@ -80,22 +80,119 @@ def test_bgp_maximum_prefix_out():
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
router = tgen.gears["r2"]
router1 = tgen.gears["r1"]
router2 = tgen.gears["r2"]
def _bgp_converge(router):
# format (router to configure, command, expected received prefixes on r2)
tests = [
# test of the initial config
(None, 2),
# modifying the max-prefix-out value
(
"router bgp\n address-family ipv4\n neighbor 192.168.255.1 maximum-prefix-out 4",
4,
),
# removing the max-prefix-out value
(
"router bgp\n address-family ipv4\n no neighbor 192.168.255.1 maximum-prefix-out",
6,
),
# setting a max-prefix-out value
(
"router bgp\n address-family ipv4\n neighbor 192.168.255.1 maximum-prefix-out 3",
3,
),
# setting a max-prefix-out value - higher than the total number of prefix
(
"router bgp\n address-family ipv4\n neighbor 192.168.255.1 maximum-prefix-out 8",
6,
),
# adding a new prefix
("router bgp\n int lo\n ip address 172.16.255.249/32", 7),
# setting a max-prefix-out value - lower than the total number of prefix
(
"router bgp\n address-family ipv4\n neighbor 192.168.255.1 maximum-prefix-out 1",
1,
),
# adding a new prefix
("router bgp\n int lo\n ip address 172.16.255.248/32", 1),
# removing the max-prefix-out value
(
"router bgp\n address-family ipv4\n no neighbor 192.168.255.1 maximum-prefix-out 1",
8,
),
# test setting the existing neighbor into a peer-group with a max-prefix-out value
(
"""
router bgp
neighbor test peer-group
neighbor test remote-as 65002
neighbor test timers 3 10
address-family ipv4
neighbor test maximum-prefix-out 3
!
neighbor 192.168.255.1 peer-group test
""",
3,
),
# max-prefix-out value of the neighbor must take the precedence
(
"router bgp\n address-family ipv4\n neighbor 192.168.255.1 maximum-prefix-out 4",
4,
),
(
"router bgp\n address-family ipv4\n no neighbor 192.168.255.1 maximum-prefix-out",
3,
),
(
"""
router bgp
no neighbor 192.168.255.1 peer-group test
neighbor 192.168.255.1 remote-as 65002
neighbor 192.168.255.1 timers 3 10
""",
8,
),
(
"router bgp\n address-family ipv4\n neighbor 192.168.255.1 maximum-prefix-out 5",
5,
),
# test setting the existing neighbor with a max-pref-out value into a peer-group with a max-pref-out value
("router bgp\n neighbor 192.168.255.1 peer-group test", 5),
(
"router bgp\n address-family ipv4\n no neighbor 192.168.255.1 maximum-prefix-out 5",
3,
),
]
def _bgp_converge(router, nb_prefixes):
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}},
"addressFamilyInfo": {
"ipv4Unicast": {"acceptedPrefixCounter": nb_prefixes}
},
}
}
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)
for test in tests:
cfg, exp_prfxs = test
if cfg:
cmd = (
"""
configure terminal
%s
"""
% cfg
)
router1.vtysh_cmd(cmd)
assert result is None, 'Failed bgp convergence in "{}"'.format(router)
test_func = functools.partial(_bgp_converge, router2, exp_prfxs)
success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
assert result is None, 'Failed bgp convergence in "{}"'.format(router2)
if __name__ == "__main__":