Skip to content

Commit

Permalink
bgpd: Handle Addpath capability using dynamic capabilities
Browse files Browse the repository at this point in the history
Changing Addpath type, and or disabling RX (receiving) flag, we can do this
without tearing down the session, and using dynamic capabilities.

Signed-off-by: Donatas Abraitis <[email protected]>
  • Loading branch information
ton31337 committed Oct 3, 2023
1 parent 51255e1 commit 05cf9d0
Show file tree
Hide file tree
Showing 5 changed files with 246 additions and 14 deletions.
33 changes: 32 additions & 1 deletion bgpd/bgp_addpath.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

#include "bgp_addpath.h"
#include "bgp_route.h"
#include "bgp_open.h"
#include "bgp_packet.h"

static const struct bgp_addpath_strategy_names strat_names[BGP_ADDPATH_MAX] = {
{
Expand Down Expand Up @@ -359,6 +361,31 @@ void bgp_addpath_type_changed(struct bgp *bgp)
}
}

int bgp_addpath_capability_action(enum bgp_addpath_strat addpath_type,
uint8_t paths)
{
int action = CAPABILITY_ACTION_UNSET;

switch (addpath_type) {
case BGP_ADDPATH_ALL:
case BGP_ADDPATH_BEST_PER_AS:
action = CAPABILITY_ACTION_SET;
break;
case BGP_ADDPATH_BEST_SELECTED:
if (paths)
action = CAPABILITY_ACTION_SET;
else
action = CAPABILITY_ACTION_UNSET;
break;
case BGP_ADDPATH_NONE:
case BGP_ADDPATH_MAX:
action = CAPABILITY_ACTION_UNSET;
break;
}

return action;
}

/*
* Change the addpath type assigned to a peer, or peer group. In addition to
* adjusting the counts, peer sessions will be reset as needed to make the
Expand All @@ -373,6 +400,7 @@ void bgp_addpath_set_peer_type(struct peer *peer, afi_t afi, safi_t safi,
struct listnode *node, *nnode;
struct peer *tmp_peer;
struct peer_group *group;
int action = bgp_addpath_capability_action(addpath_type, paths);

if (safi == SAFI_LABELED_UNICAST)
safi = SAFI_UNICAST;
Expand Down Expand Up @@ -430,9 +458,12 @@ void bgp_addpath_set_peer_type(struct peer *peer, afi_t afi, safi_t safi,
}
}
} else {
peer_change_action(peer, afi, safi, peer_change_reset);
if (!CHECK_FLAG(peer->cap, PEER_CAP_DYNAMIC_RCV) &&
!CHECK_FLAG(peer->cap, PEER_CAP_DYNAMIC_ADV))
peer_change_action(peer, afi, safi, peer_change_reset);
}

bgp_capability_send(peer, afi, safi, CAPABILITY_CODE_ADDPATH, action);
}

/*
Expand Down
8 changes: 8 additions & 0 deletions bgpd/bgp_addpath.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@
#include "bgpd/bgp_table.h"
#include "lib/json.h"

struct bgp_addpath_capability {
uint16_t afi;
uint8_t safi;
uint8_t flags;
};

#define BGP_ADDPATH_TX_ID_FOR_DEFAULT_ORIGINATE 1

void bgp_addpath_init_bgp_data(struct bgp_addpath_bgp_data *d);
Expand Down Expand Up @@ -57,4 +63,6 @@ void bgp_addpath_update_ids(struct bgp *bgp, struct bgp_dest *dest, afi_t afi,
safi_t safi);

void bgp_addpath_type_changed(struct bgp *bgp);
extern int bgp_addpath_capability_action(enum bgp_addpath_strat addpath_type,
uint8_t paths);
#endif
179 changes: 177 additions & 2 deletions bgpd/bgp_packet.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "lib_errors.h"

#include "bgpd/bgpd.h"
#include "bgpd/bgp_addpath.h"
#include "bgpd/bgp_table.h"
#include "bgpd/bgp_dump.h"
#include "bgpd/bgp_bmp.h"
Expand Down Expand Up @@ -1216,6 +1217,8 @@ void bgp_capability_send(struct peer *peer, afi_t afi, safi_t safi,
unsigned long cap_len;
uint16_t len;
uint32_t gr_restart_time;
uint8_t addpath_afi_safi_count = 0;
bool adv_addpath_tx = false;
const char *capability = lookup_msg(capcode_str, capability_code,
"Unknown");

Expand Down Expand Up @@ -1379,11 +1382,91 @@ 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_ADDPATH:
SET_FLAG(peer->cap, PEER_CAP_ADDPATH_ADV);

FOREACH_AFI_SAFI (afi, safi) {
if (peer->afc[afi][safi]) {
addpath_afi_safi_count++;

/* Only advertise addpath TX if a feature that
* will use it is
* configured */
if (peer->addpath_type[afi][safi] !=
BGP_ADDPATH_NONE)
adv_addpath_tx = true;

/* If we have enabled labeled unicast, we MUST check
* against unicast SAFI because addpath IDs are
* allocated under unicast SAFI, the same as the RIB
* is managed in unicast SAFI.
*/
if (safi == SAFI_LABELED_UNICAST)
if (peer->addpath_type[afi][SAFI_UNICAST] !=
BGP_ADDPATH_NONE)
adv_addpath_tx = true;
}
}

stream_putc(s, action);
stream_putc(s, CAPABILITY_CODE_ADDPATH);
stream_putc(s, CAPABILITY_CODE_ADDPATH_LEN *
addpath_afi_safi_count);

FOREACH_AFI_SAFI (afi, safi) {
if (peer->afc[afi][safi]) {
bool adv_addpath_rx =
!CHECK_FLAG(peer->af_flags[afi][safi],
PEER_FLAG_DISABLE_ADDPATH_RX);
uint8_t flags = 0;

/* Convert AFI, SAFI to values for packet. */
bgp_map_afi_safi_int2iana(afi, safi, &pkt_afi,
&pkt_safi);

stream_putw(s, pkt_afi);
stream_putc(s, pkt_safi);

if (adv_addpath_rx) {
SET_FLAG(flags, BGP_ADDPATH_RX);
SET_FLAG(peer->af_cap[afi][safi],
PEER_CAP_ADDPATH_AF_RX_ADV);
} else {
UNSET_FLAG(peer->af_cap[afi][safi],
PEER_CAP_ADDPATH_AF_RX_ADV);
}

if (adv_addpath_tx) {
SET_FLAG(flags, BGP_ADDPATH_TX);
SET_FLAG(peer->af_cap[afi][safi],
PEER_CAP_ADDPATH_AF_TX_ADV);
if (safi == SAFI_LABELED_UNICAST)
SET_FLAG(peer->af_cap[afi]
[SAFI_UNICAST],
PEER_CAP_ADDPATH_AF_TX_ADV);
} else {
UNSET_FLAG(peer->af_cap[afi][safi],
PEER_CAP_ADDPATH_AF_TX_ADV);
}

stream_putc(s, flags);
}
}

if (bgp_debug_neighbor_events(peer))
zlog_debug("%pBP sending CAPABILITY has %s %s for afi/safi: %s/%s",
peer,
action == CAPABILITY_ACTION_SET
? "Advertising"
: "Removing",
capability, iana_afi2str(pkt_afi),
iana_safi2str(pkt_safi));

break;
case CAPABILITY_CODE_REFRESH:
case CAPABILITY_CODE_ORF:
case CAPABILITY_CODE_AS4:
case CAPABILITY_CODE_DYNAMIC:
case CAPABILITY_CODE_ADDPATH:
case CAPABILITY_CODE_ENHANCED_RR:
case CAPABILITY_CODE_FQDN:
case CAPABILITY_CODE_ENHE:
Expand Down Expand Up @@ -2878,6 +2961,96 @@ static int bgp_route_refresh_receive(struct peer_connection *connection,
return BGP_PACKET_NOOP;
}

static void bgp_dynamic_capability_addpath(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_ADDPATH_LEN) {
flog_warn(EC_BGP_CAPABILITY_INVALID_LENGTH,
"Add Path: Received invalid length %zu, non-multiple of 4",
len);
return;
}

SET_FLAG(peer->cap, PEER_CAP_ADDPATH_RCV);

while (data + CAPABILITY_CODE_ADDPATH_LEN <= end) {
afi_t afi;
safi_t safi;
iana_afi_t pkt_afi;
iana_safi_t pkt_safi;
struct bgp_addpath_capability bac;

memcpy(&bac, data, sizeof(bac));
pkt_afi = ntohs(bac.afi);
pkt_safi = safi_int2iana(bac.safi);

if (bgp_debug_neighbor_events(peer))
zlog_debug("%s OPEN has %s capability for afi/safi: %s/%s%s%s",
peer->host,
lookup_msg(capcode_str, hdr->code,
NULL),
iana_afi2str(pkt_afi),
iana_safi2str(pkt_safi),
(bac.flags & BGP_ADDPATH_RX)
? ", receive"
: "",
(bac.flags & BGP_ADDPATH_TX)
? ", transmit"
: "");

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 Addpath Attribute 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 AddPath capability for this AFI/SAFI",
peer->host,
iana_afi2str(pkt_afi),
iana_safi2str(pkt_safi));
continue;
}

if (CHECK_FLAG(bac.flags, BGP_ADDPATH_RX))
SET_FLAG(peer->af_cap[afi][safi],
PEER_CAP_ADDPATH_AF_RX_RCV);
else
UNSET_FLAG(peer->af_cap[afi][safi],
PEER_CAP_ADDPATH_AF_RX_RCV);

if (CHECK_FLAG(bac.flags, BGP_ADDPATH_TX))
SET_FLAG(peer->af_cap[afi][safi],
PEER_CAP_ADDPATH_AF_TX_RCV);
else
UNSET_FLAG(peer->af_cap[afi][safi],
PEER_CAP_ADDPATH_AF_TX_RCV);

data += CAPABILITY_CODE_ADDPATH_LEN;
}
} else {
FOREACH_AFI_SAFI (afi, safi) {
UNSET_FLAG(peer->af_cap[afi][safi],
PEER_CAP_ADDPATH_AF_RX_RCV);
UNSET_FLAG(peer->af_cap[afi][safi],
PEER_CAP_ADDPATH_AF_TX_RCV);
}

UNSET_FLAG(peer->cap, PEER_CAP_ADDPATH_RCV);
}
}

static void bgp_dynamic_capability_llgr(uint8_t *pnt, int action,
struct capability_header *hdr,
struct peer *peer)
Expand Down Expand Up @@ -3232,11 +3405,13 @@ static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt,
case CAPABILITY_CODE_LLGR:
bgp_dynamic_capability_llgr(pnt, action, hdr, peer);
break;
case CAPABILITY_CODE_ADDPATH:
bgp_dynamic_capability_addpath(pnt, action, hdr, peer);
break;
case CAPABILITY_CODE_REFRESH:
case CAPABILITY_CODE_ORF:
case CAPABILITY_CODE_AS4:
case CAPABILITY_CODE_DYNAMIC:
case CAPABILITY_CODE_ADDPATH:
case CAPABILITY_CODE_ENHANCED_RR:
case CAPABILITY_CODE_FQDN:
case CAPABILITY_CODE_ENHE:
Expand Down
38 changes: 28 additions & 10 deletions bgpd/bgp_vty.c
Original file line number Diff line number Diff line change
Expand Up @@ -8925,13 +8925,21 @@ DEFUN(neighbor_disable_addpath_rx,
struct peer *peer;
afi_t afi = bgp_node_afi(vty);
safi_t safi = bgp_node_safi(vty);
int ret;
int action;

peer = peer_and_group_lookup_vty(vty, peer_str);
if (!peer)
return CMD_WARNING_CONFIG_FAILED;

return peer_af_flag_set_vty(vty, peer_str, afi, safi,
PEER_FLAG_DISABLE_ADDPATH_RX);
action = bgp_addpath_capability_action(peer->addpath_type[afi][safi], 0);

ret = peer_af_flag_set_vty(vty, peer_str, afi, safi,
PEER_FLAG_DISABLE_ADDPATH_RX);

bgp_capability_send(peer, afi, safi, CAPABILITY_CODE_ADDPATH, action);

return ret;
}

DEFUN(no_neighbor_disable_addpath_rx,
Expand All @@ -8946,13 +8954,21 @@ DEFUN(no_neighbor_disable_addpath_rx,
struct peer *peer;
afi_t afi = bgp_node_afi(vty);
safi_t safi = bgp_node_safi(vty);
int ret;
int action;

peer = peer_and_group_lookup_vty(vty, peer_str);
if (!peer)
return CMD_WARNING_CONFIG_FAILED;

return peer_af_flag_unset_vty(vty, peer_str, afi, safi,
PEER_FLAG_DISABLE_ADDPATH_RX);
action = bgp_addpath_capability_action(peer->addpath_type[afi][safi], 0);

ret = peer_af_flag_unset_vty(vty, peer_str, afi, safi,
PEER_FLAG_DISABLE_ADDPATH_RX);

bgp_capability_send(peer, afi, safi, CAPABILITY_CODE_ADDPATH, action);

return ret;
}

DEFUN (neighbor_addpath_tx_all_paths,
Expand All @@ -8964,13 +8980,15 @@ DEFUN (neighbor_addpath_tx_all_paths,
{
int idx_peer = 1;
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;

bgp_addpath_set_peer_type(peer, bgp_node_afi(vty), bgp_node_safi(vty),
BGP_ADDPATH_ALL, 0);
bgp_addpath_set_peer_type(peer, afi, safi, BGP_ADDPATH_ALL, 0);

return CMD_SUCCESS;
}

Expand All @@ -8990,20 +9008,20 @@ DEFUN (no_neighbor_addpath_tx_all_paths,
{
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;

if (peer->addpath_type[bgp_node_afi(vty)][bgp_node_safi(vty)]
!= BGP_ADDPATH_ALL) {
if (peer->addpath_type[afi][safi] != BGP_ADDPATH_ALL) {
vty_out(vty,
"%% Peer not currently configured to transmit all paths.");
return CMD_WARNING_CONFIG_FAILED;
}

bgp_addpath_set_peer_type(peer, bgp_node_afi(vty), bgp_node_safi(vty),
BGP_ADDPATH_NONE, 0);
bgp_addpath_set_peer_type(peer, afi, safi, BGP_ADDPATH_NONE, 0);

return CMD_SUCCESS;
}
Expand Down
Loading

0 comments on commit 05cf9d0

Please sign in to comment.