Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bgpd: Handle ORF capability using dynamic capabilities #14614

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 8 additions & 9 deletions bgpd/bgp_open.c
Original file line number Diff line number Diff line change
Expand Up @@ -340,15 +340,14 @@ static void bgp_capability_orf_not_support(struct peer *peer, iana_afi_t afi,
peer->host, afi, safi, type, mode);
}

static const struct message orf_type_str[] = {
{ORF_TYPE_RESERVED, "Reserved"},
{ORF_TYPE_PREFIX, "Prefixlist"},
{0}};

static const struct message orf_mode_str[] = {{ORF_MODE_RECEIVE, "Receive"},
{ORF_MODE_SEND, "Send"},
{ORF_MODE_BOTH, "Both"},
{0}};
const struct message orf_type_str[] = { { ORF_TYPE_RESERVED, "Reserved" },
{ ORF_TYPE_PREFIX, "Prefixlist" },
{ 0 } };

const struct message orf_mode_str[] = { { ORF_MODE_RECEIVE, "Receive" },
{ ORF_MODE_SEND, "Send" },
{ ORF_MODE_BOTH, "Both" },
{ 0 } };

static int bgp_capability_orf_entry(struct peer *peer,
struct capability_header *hdr)
Expand Down
2 changes: 2 additions & 0 deletions bgpd/bgp_open.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,5 +108,7 @@ extern void bgp_capability_vty_out(struct vty *vty, struct peer *peer,
bool use_json, json_object *json_neigh);
extern as_t peek_for_as4_capability(struct peer *peer, uint16_t length);
extern const struct message capcode_str[];
extern const struct message orf_type_str[];
extern const struct message orf_mode_str[];

#endif /* _QUAGGA_BGP_OPEN_H */
195 changes: 193 additions & 2 deletions bgpd/bgp_packet.c
Original file line number Diff line number Diff line change
Expand Up @@ -1214,6 +1214,8 @@ void bgp_capability_send(struct peer *peer, afi_t afi, safi_t safi,
uint32_t gr_restart_time;
uint8_t addpath_afi_safi_count = 0;
bool adv_addpath_tx = false;
unsigned long number_of_orfs_p;
uint8_t number_of_orfs = 0;
const char *capability = lookup_msg(capcode_str, capability_code,
"Unknown");

Expand Down Expand Up @@ -1458,8 +1460,78 @@ void bgp_capability_send(struct peer *peer, afi_t afi, safi_t safi,
iana_safi2str(pkt_safi));

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

stream_putc(s, action);
stream_putc(s, CAPABILITY_CODE_ORF);
cap_len = stream_get_endp(s);
stream_putc(s, 0);

stream_putw(s, pkt_afi); /* Address Family Identifier */
stream_putc(s, 0); /* Reserved */
stream_putc(s,
pkt_safi); /* Subsequent Address Family Identifier */

number_of_orfs_p =
stream_get_endp(s); /* Number of ORFs pointer */
stream_putc(s, 0); /* Number of ORFs */

/* Address Prefix ORF */
if (CHECK_FLAG(peer->af_flags[afi][safi],
PEER_FLAG_ORF_PREFIX_SM) ||
CHECK_FLAG(peer->af_flags[afi][safi],
PEER_FLAG_ORF_PREFIX_RM)) {
stream_putc(s, ORF_TYPE_PREFIX);

if (CHECK_FLAG(peer->af_flags[afi][safi],
PEER_FLAG_ORF_PREFIX_SM) &&
CHECK_FLAG(peer->af_flags[afi][safi],
PEER_FLAG_ORF_PREFIX_RM)) {
SET_FLAG(peer->af_cap[afi][safi],
PEER_CAP_ORF_PREFIX_SM_ADV);
SET_FLAG(peer->af_cap[afi][safi],
PEER_CAP_ORF_PREFIX_RM_ADV);
stream_putc(s, ORF_MODE_BOTH);
} else if (CHECK_FLAG(peer->af_flags[afi][safi],
PEER_FLAG_ORF_PREFIX_SM)) {
SET_FLAG(peer->af_cap[afi][safi],
PEER_CAP_ORF_PREFIX_SM_ADV);
UNSET_FLAG(peer->af_cap[afi][safi],
PEER_CAP_ORF_PREFIX_RM_ADV);
stream_putc(s, ORF_MODE_SEND);
} else {
SET_FLAG(peer->af_cap[afi][safi],
PEER_CAP_ORF_PREFIX_RM_ADV);
UNSET_FLAG(peer->af_cap[afi][safi],
PEER_CAP_ORF_PREFIX_SM_ADV);
stream_putc(s, ORF_MODE_RECEIVE);
}
number_of_orfs++;
} else {
UNSET_FLAG(peer->af_cap[afi][safi],
PEER_CAP_ORF_PREFIX_SM_ADV);
UNSET_FLAG(peer->af_cap[afi][safi],
PEER_CAP_ORF_PREFIX_RM_ADV);
}

/* Total Number of ORFs. */
stream_putc_at(s, number_of_orfs_p, number_of_orfs);

len = stream_get_endp(s) - cap_len - 1;
stream_putc_at(s, cap_len, len);

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_AS4:
case CAPABILITY_CODE_DYNAMIC:
case CAPABILITY_CODE_ENHANCED_RR:
Expand Down Expand Up @@ -3044,6 +3116,123 @@ static void bgp_dynamic_capability_addpath(uint8_t *pnt, int action,
}
}

static void bgp_dynamic_capability_orf(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;

struct capability_mp_data mpc;
uint8_t num;
iana_afi_t pkt_afi;
afi_t afi;
iana_safi_t pkt_safi;
safi_t safi;
uint8_t type;
uint8_t mode;
uint16_t sm_cap = PEER_CAP_ORF_PREFIX_SM_RCV;
uint16_t rm_cap = PEER_CAP_ORF_PREFIX_RM_RCV;
int i;

if (data + CAPABILITY_CODE_ORF_LEN > end) {
flog_warn(EC_BGP_CAPABILITY_INVALID_LENGTH,
"ORF: Received invalid length %zu, less than %d", len,
CAPABILITY_CODE_ORF_LEN);
return;
}

/* ORF Entry header */
memcpy(&mpc, data, sizeof(mpc));
data += sizeof(mpc);
num = *data++;
pkt_afi = ntohs(mpc.afi);
pkt_safi = mpc.safi;

/* Convert AFI, SAFI to internal values, check. */
if (bgp_map_afi_safi_iana2int(pkt_afi, pkt_safi, &afi, &safi)) {
zlog_info("%pBP Addr-family %d/%d not supported. Ignoring the ORF capability",
peer, pkt_afi, pkt_safi);
return;
}

/* validate number field */
if (CAPABILITY_CODE_ORF_LEN + (num * 2) > hdr->length) {
zlog_info("%pBP ORF Capability entry length error, Cap length %u, num %u",
peer, hdr->length, num);
return;
}

if (action == CAPABILITY_ACTION_UNSET) {
UNSET_FLAG(peer->af_cap[afi][safi], sm_cap);
UNSET_FLAG(peer->af_cap[afi][safi], rm_cap);
return;
}

for (i = 0; i < num; i++) {
if (data + 1 > end) {
flog_err(EC_BGP_CAPABILITY_INVALID_LENGTH,
"%pBP ORF Capability entry length (type) error, Cap length %u, num %u",
peer, hdr->length, num);
return;
}
type = *data++;

if (data + 1 > end) {
flog_err(EC_BGP_CAPABILITY_INVALID_LENGTH,
"%pBP ORF Capability entry length (mode) error, Cap length %u, num %u",
peer, hdr->length, num);
return;
}
mode = *data++;

/* ORF Mode error check */
switch (mode) {
case ORF_MODE_BOTH:
case ORF_MODE_SEND:
case ORF_MODE_RECEIVE:
break;
default:
if (bgp_debug_neighbor_events(peer))
zlog_debug("%pBP Addr-family %d/%d has ORF type/mode %d/%d not supported",
peer, afi, safi, type, mode);
continue;
}

if (!((afi == AFI_IP && safi == SAFI_UNICAST) ||
(afi == AFI_IP && safi == SAFI_MULTICAST) ||
(afi == AFI_IP6 && safi == SAFI_UNICAST))) {
if (bgp_debug_neighbor_events(peer))
zlog_debug("%pBP Addr-family %d/%d unsupported AFI/SAFI received",
peer, afi, safi);
continue;
}

if (bgp_debug_neighbor_events(peer))
zlog_debug("%pBP OPEN has %s ORF capability as %s for afi/safi: %s/%s",
peer, lookup_msg(orf_type_str, type, NULL),
lookup_msg(orf_mode_str, mode, NULL),
iana_afi2str(pkt_afi),
iana_safi2str(pkt_safi));

switch (mode) {
case ORF_MODE_BOTH:
SET_FLAG(peer->af_cap[afi][safi], sm_cap);
SET_FLAG(peer->af_cap[afi][safi], rm_cap);
break;
case ORF_MODE_SEND:
SET_FLAG(peer->af_cap[afi][safi], sm_cap);
UNSET_FLAG(peer->af_cap[afi][safi], rm_cap);
break;
case ORF_MODE_RECEIVE:
SET_FLAG(peer->af_cap[afi][safi], rm_cap);
UNSET_FLAG(peer->af_cap[afi][safi], sm_cap);
break;
}
}
}

static void bgp_dynamic_capability_llgr(uint8_t *pnt, int action,
struct capability_header *hdr,
struct peer *peer)
Expand Down Expand Up @@ -3401,8 +3590,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_REFRESH:
case CAPABILITY_CODE_ORF:
bgp_dynamic_capability_orf(pnt, action, hdr, peer);
break;
case CAPABILITY_CODE_REFRESH:
case CAPABILITY_CODE_AS4:
case CAPABILITY_CODE_DYNAMIC:
case CAPABILITY_CODE_ENHANCED_RR:
Expand Down
70 changes: 48 additions & 22 deletions bgpd/bgp_vty.c
Original file line number Diff line number Diff line change
Expand Up @@ -5831,24 +5831,37 @@ DEFUN (neighbor_capability_orf_prefix,
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, peer_str);
if (!peer)
return CMD_WARNING_CONFIG_FAILED;

if (strmatch(argv[idx_send_recv]->text, "send"))
return peer_af_flag_set_vty(vty, peer_str, afi, safi,
PEER_FLAG_ORF_PREFIX_SM);
if (strmatch(argv[idx_send_recv]->text, "send")) {
ret = peer_af_flag_set_vty(vty, peer_str, afi, safi,
PEER_FLAG_ORF_PREFIX_SM);
bgp_capability_send(peer, afi, safi, CAPABILITY_CODE_ORF,
CAPABILITY_ACTION_SET);
return ret;
}

if (strmatch(argv[idx_send_recv]->text, "receive"))
return peer_af_flag_set_vty(vty, peer_str, afi, safi,
PEER_FLAG_ORF_PREFIX_RM);
if (strmatch(argv[idx_send_recv]->text, "receive")) {
ret = peer_af_flag_set_vty(vty, peer_str, afi, safi,
PEER_FLAG_ORF_PREFIX_RM);
bgp_capability_send(peer, afi, safi, CAPABILITY_CODE_ORF,
CAPABILITY_ACTION_SET);
return ret;
}

if (strmatch(argv[idx_send_recv]->text, "both"))
return peer_af_flag_set_vty(vty, peer_str, afi, safi,
PEER_FLAG_ORF_PREFIX_SM)
| peer_af_flag_set_vty(vty, peer_str, afi, safi,
PEER_FLAG_ORF_PREFIX_RM);
if (strmatch(argv[idx_send_recv]->text, "both")) {
ret = peer_af_flag_set_vty(vty, peer_str, afi, safi,
PEER_FLAG_ORF_PREFIX_SM) |
peer_af_flag_set_vty(vty, peer_str, afi, safi,
PEER_FLAG_ORF_PREFIX_RM);
bgp_capability_send(peer, afi, safi, CAPABILITY_CODE_ORF,
CAPABILITY_ACTION_SET);
return ret;
}

return CMD_WARNING_CONFIG_FAILED;
}
Expand Down Expand Up @@ -5883,24 +5896,37 @@ DEFUN (no_neighbor_capability_orf_prefix,
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, peer_str);
if (!peer)
return CMD_WARNING_CONFIG_FAILED;

if (strmatch(argv[idx_send_recv]->text, "send"))
return peer_af_flag_unset_vty(vty, peer_str, afi, safi,
PEER_FLAG_ORF_PREFIX_SM);
if (strmatch(argv[idx_send_recv]->text, "send")) {
ret = peer_af_flag_unset_vty(vty, peer_str, afi, safi,
PEER_FLAG_ORF_PREFIX_SM);
bgp_capability_send(peer, afi, safi, CAPABILITY_CODE_ORF,
CAPABILITY_ACTION_UNSET);
return ret;
}

if (strmatch(argv[idx_send_recv]->text, "receive"))
return peer_af_flag_unset_vty(vty, peer_str, afi, safi,
PEER_FLAG_ORF_PREFIX_RM);
if (strmatch(argv[idx_send_recv]->text, "receive")) {
ret = peer_af_flag_unset_vty(vty, peer_str, afi, safi,
PEER_FLAG_ORF_PREFIX_RM);
bgp_capability_send(peer, afi, safi, CAPABILITY_CODE_ORF,
CAPABILITY_ACTION_UNSET);
return ret;
}

if (strmatch(argv[idx_send_recv]->text, "both"))
return peer_af_flag_unset_vty(vty, peer_str, afi, safi,
PEER_FLAG_ORF_PREFIX_SM)
| peer_af_flag_unset_vty(vty, peer_str, afi, safi,
PEER_FLAG_ORF_PREFIX_RM);
if (strmatch(argv[idx_send_recv]->text, "both")) {
ret = peer_af_flag_unset_vty(vty, peer_str, afi, safi,
PEER_FLAG_ORF_PREFIX_SM) |
peer_af_flag_unset_vty(vty, peer_str, afi, safi,
PEER_FLAG_ORF_PREFIX_RM);
bgp_capability_send(peer, afi, safi, CAPABILITY_CODE_ORF,
CAPABILITY_ACTION_UNSET);
return ret;
}

return CMD_WARNING_CONFIG_FAILED;
}
Expand Down
22 changes: 22 additions & 0 deletions bgpd/bgpd.c
Original file line number Diff line number Diff line change
Expand Up @@ -4979,6 +4979,16 @@ static int peer_af_flag_modify(struct peer *peer, afi_t afi, safi_t safi,
else if (flag == PEER_FLAG_ORF_PREFIX_RM)
peer->last_reset = PEER_DOWN_CAPABILITY_CHANGE;

/* We should not reset the session if
* dynamic capability is enabled and we
* are changing the ORF prefix flags.
*/
if ((CHECK_FLAG(peer->cap, PEER_CAP_DYNAMIC_RCV) &&
CHECK_FLAG(peer->cap, PEER_CAP_DYNAMIC_ADV)) &&
(flag == PEER_FLAG_ORF_PREFIX_RM ||
flag == PEER_FLAG_ORF_PREFIX_SM))
action.type = peer_change_none;

peer_change_action(peer, afi, safi, action.type);
}
}
Expand Down Expand Up @@ -5039,6 +5049,18 @@ static int peer_af_flag_modify(struct peer *peer, afi_t afi, safi_t safi,
member->last_reset =
PEER_DOWN_CAPABILITY_CHANGE;

/* We should not reset the session if
* dynamic capability is enabled and we
* are changing the ORF prefix flags.
*/
if ((CHECK_FLAG(peer->cap,
PEER_CAP_DYNAMIC_RCV) &&
CHECK_FLAG(peer->cap,
PEER_CAP_DYNAMIC_ADV)) &&
(flag == PEER_FLAG_ORF_PREFIX_RM ||
flag == PEER_FLAG_ORF_PREFIX_SM))
action.type = peer_change_none;

peer_change_action(member, afi, safi,
action.type);
}
Expand Down
2 changes: 2 additions & 0 deletions tests/topotests/bgp_dynamic_capability/r1/frr.conf
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,5 @@ router bgp 65001
neighbor 192.168.1.2 addpath-tx-all-paths
exit-address-family
!
ip prefix-list r2 seq 5 permit 10.10.10.10/32
!
Loading