Merge pull request #12727 from opensourcerouting/feature/bgp_software_version_capability

bgpd: Software Version Capability
This commit is contained in:
Donald Sharp 2023-02-17 08:18:22 -05:00 committed by GitHub
commit a15b0b1024
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 369 additions and 6 deletions

View File

@ -297,6 +297,15 @@ static struct peer *peer_xfer_conn(struct peer *from_peer)
from_peer->domainname = NULL; from_peer->domainname = NULL;
} }
if (peer->soft_version) {
XFREE(MTYPE_BGP_SOFT_VERSION, peer->soft_version);
peer->soft_version = NULL;
}
if (from_peer->soft_version) {
peer->soft_version = from_peer->soft_version;
from_peer->soft_version = NULL;
}
FOREACH_AFI_SAFI (afi, safi) { FOREACH_AFI_SAFI (afi, safi) {
peer->af_sflags[afi][safi] = from_peer->af_sflags[afi][safi]; peer->af_sflags[afi][safi] = from_peer->af_sflags[afi][safi];
peer->af_cap[afi][safi] = from_peer->af_cap[afi][safi]; peer->af_cap[afi][safi] = from_peer->af_cap[afi][safi];

View File

@ -139,3 +139,5 @@ DEFINE_MTYPE(BGPD, BGP_SRV6_FUNCTION, "BGP srv6 function");
DEFINE_MTYPE(BGPD, EVPN_REMOTE_IP, "BGP EVPN Remote IP hash entry"); DEFINE_MTYPE(BGPD, EVPN_REMOTE_IP, "BGP EVPN Remote IP hash entry");
DEFINE_MTYPE(BGPD, BGP_NOTIFICATION, "BGP Notification Message"); DEFINE_MTYPE(BGPD, BGP_NOTIFICATION, "BGP Notification Message");
DEFINE_MTYPE(BGPD, BGP_SOFT_VERSION, "Software Version");

View File

@ -138,4 +138,6 @@ DECLARE_MTYPE(EVPN_REMOTE_IP);
DECLARE_MTYPE(BGP_NOTIFICATION); DECLARE_MTYPE(BGP_NOTIFICATION);
DECLARE_MTYPE(BGP_SOFT_VERSION);
#endif /* _QUAGGA_BGP_MEMORY_H */ #endif /* _QUAGGA_BGP_MEMORY_H */

View File

@ -59,6 +59,7 @@ static const struct message capcode_str[] = {
{CAPABILITY_CODE_EXT_MESSAGE, "BGP Extended Message"}, {CAPABILITY_CODE_EXT_MESSAGE, "BGP Extended Message"},
{CAPABILITY_CODE_LLGR, "Long-lived BGP Graceful Restart"}, {CAPABILITY_CODE_LLGR, "Long-lived BGP Graceful Restart"},
{CAPABILITY_CODE_ROLE, "Role"}, {CAPABILITY_CODE_ROLE, "Role"},
{CAPABILITY_CODE_SOFT_VERSION, "Software Version"},
{0}}; {0}};
/* Minimum sizes for length field of each cap (so not inc. the header) */ /* Minimum sizes for length field of each cap (so not inc. the header) */
@ -79,6 +80,7 @@ static const size_t cap_minsizes[] = {
[CAPABILITY_CODE_EXT_MESSAGE] = CAPABILITY_CODE_EXT_MESSAGE_LEN, [CAPABILITY_CODE_EXT_MESSAGE] = CAPABILITY_CODE_EXT_MESSAGE_LEN,
[CAPABILITY_CODE_LLGR] = CAPABILITY_CODE_LLGR_LEN, [CAPABILITY_CODE_LLGR] = CAPABILITY_CODE_LLGR_LEN,
[CAPABILITY_CODE_ROLE] = CAPABILITY_CODE_ROLE_LEN, [CAPABILITY_CODE_ROLE] = CAPABILITY_CODE_ROLE_LEN,
[CAPABILITY_CODE_SOFT_VERSION] = CAPABILITY_CODE_SOFT_VERSION_LEN,
}; };
/* value the capability must be a multiple of. /* value the capability must be a multiple of.
@ -103,6 +105,7 @@ static const size_t cap_modsizes[] = {
[CAPABILITY_CODE_EXT_MESSAGE] = 1, [CAPABILITY_CODE_EXT_MESSAGE] = 1,
[CAPABILITY_CODE_LLGR] = 1, [CAPABILITY_CODE_LLGR] = 1,
[CAPABILITY_CODE_ROLE] = 1, [CAPABILITY_CODE_ROLE] = 1,
[CAPABILITY_CODE_SOFT_VERSION] = 1,
}; };
/* BGP-4 Multiprotocol Extentions lead us to the complex world. We can /* BGP-4 Multiprotocol Extentions lead us to the complex world. We can
@ -921,6 +924,41 @@ static int bgp_capability_role(struct peer *peer, struct capability_header *hdr)
return 0; return 0;
} }
static int bgp_capability_software_version(struct peer *peer,
struct capability_header *hdr)
{
struct stream *s = BGP_INPUT(peer);
char str[BGP_MAX_SOFT_VERSION + 1];
size_t end = stream_get_getp(s) + hdr->length;
uint8_t len;
SET_FLAG(peer->cap, PEER_CAP_SOFT_VERSION_RCV);
len = stream_getc(s);
if (stream_get_getp(s) + len > end) {
flog_warn(
EC_BGP_CAPABILITY_INVALID_DATA,
"%s: Received malformed Software Version capability from peer %s",
__func__, peer->host);
return -1;
}
if (len) {
stream_get(str, s, len);
str[len] = '\0';
XFREE(MTYPE_BGP_SOFT_VERSION, peer->soft_version);
peer->soft_version = XSTRDUP(MTYPE_BGP_SOFT_VERSION, str);
if (bgp_debug_neighbor_events(peer))
zlog_debug("%s sent Software Version: %s", peer->host,
peer->soft_version);
}
return 0;
}
/** /**
* Parse given capability. * Parse given capability.
* XXX: This is reading into a stream, but not using stream API * XXX: This is reading into a stream, but not using stream API
@ -989,6 +1027,7 @@ static int bgp_capability_parse(struct peer *peer, size_t length,
case CAPABILITY_CODE_ENHANCED_RR: case CAPABILITY_CODE_ENHANCED_RR:
case CAPABILITY_CODE_EXT_MESSAGE: case CAPABILITY_CODE_EXT_MESSAGE:
case CAPABILITY_CODE_ROLE: case CAPABILITY_CODE_ROLE:
case CAPABILITY_CODE_SOFT_VERSION:
/* Check length. */ /* Check length. */
if (caphdr.length < cap_minsizes[caphdr.code]) { if (caphdr.length < cap_minsizes[caphdr.code]) {
zlog_info( zlog_info(
@ -1089,6 +1128,9 @@ static int bgp_capability_parse(struct peer *peer, size_t length,
case CAPABILITY_CODE_ROLE: case CAPABILITY_CODE_ROLE:
ret = bgp_capability_role(peer, &caphdr); ret = bgp_capability_role(peer, &caphdr);
break; break;
case CAPABILITY_CODE_SOFT_VERSION:
ret = bgp_capability_software_version(peer, &caphdr);
break;
default: default:
if (caphdr.code > 128) { if (caphdr.code > 128) {
/* We don't send Notification for unknown vendor /* We don't send Notification for unknown vendor
@ -1928,6 +1970,50 @@ uint16_t bgp_open_capability(struct stream *s, struct peer *peer,
bgp_peer_send_gr_capability(s, peer, ext_opt_params); bgp_peer_send_gr_capability(s, peer, ext_opt_params);
bgp_peer_send_llgr_capability(s, peer, ext_opt_params); bgp_peer_send_llgr_capability(s, peer, ext_opt_params);
/* Software Version capability
* An implementation is REQUIRED Extended Optional Parameters
* Length for BGP OPEN Message support as defined in [RFC9072].
* The inclusion of the Software Version Capability is OPTIONAL.
* If an implementation supports the inclusion of the capability,
* the implementation MUST include a configuration switch to enable
* or disable its use, and that switch MUST be off by default.
*/
if (peergroup_flag_check(peer, PEER_FLAG_CAPABILITY_SOFT_VERSION) ||
peer->sort == BGP_PEER_IBGP) {
SET_FLAG(peer->cap, PEER_CAP_SOFT_VERSION_ADV);
stream_putc(s, BGP_OPEN_OPT_CAP);
rcapp = stream_get_endp(s);
ext_opt_params ? stream_putw(s, 0)
: stream_putc(s, 0); /* Capability Length */
stream_putc(s, CAPABILITY_CODE_SOFT_VERSION);
capp = stream_get_endp(s);
stream_putc(s, 0); /* dummy placeholder len */
/* The Capability Length SHOULD be no greater than 64.
* This is the limit to allow other capabilities as much
* space as they require.
*/
len = strlen(cmd_software_version_get());
if (len > BGP_MAX_SOFT_VERSION)
len = BGP_MAX_SOFT_VERSION;
stream_putc(s, len);
stream_put(s, cmd_software_version_get(), len);
/* Software Version capability Len. */
len = stream_get_endp(s) - rcapp - 1;
ext_opt_params ? stream_putw_at(s, rcapp, len - 1)
: stream_putc_at(s, rcapp, len);
/* Total Capability Len. */
len = stream_get_endp(s) - capp - 1;
stream_putc_at(s, capp, len);
if (bgp_debug_neighbor_events(peer))
zlog_debug("%s Sending Software Version cap, value: %s",
peer->host, cmd_software_version_get());
}
/* Total Opt Parm Len. */ /* Total Opt Parm Len. */
len = stream_get_endp(s) - cp - 1; len = stream_get_endp(s) - cp - 1;

View File

@ -52,6 +52,7 @@ struct graceful_restart_af {
#define CAPABILITY_CODE_ENHANCED_RR 70 /* Enhanced Route Refresh capability */ #define CAPABILITY_CODE_ENHANCED_RR 70 /* Enhanced Route Refresh capability */
#define CAPABILITY_CODE_LLGR 71 /* Long-lived Graceful Restart */ #define CAPABILITY_CODE_LLGR 71 /* Long-lived Graceful Restart */
#define CAPABILITY_CODE_FQDN 73 /* Advertise hostname capability */ #define CAPABILITY_CODE_FQDN 73 /* Advertise hostname capability */
#define CAPABILITY_CODE_SOFT_VERSION 75 /* Software Version capability */
#define CAPABILITY_CODE_ENHE 5 /* Extended Next Hop Encoding */ #define CAPABILITY_CODE_ENHE 5 /* Extended Next Hop Encoding */
#define CAPABILITY_CODE_REFRESH_OLD 128 /* Route Refresh Capability(cisco) */ #define CAPABILITY_CODE_REFRESH_OLD 128 /* Route Refresh Capability(cisco) */
#define CAPABILITY_CODE_ORF_OLD 130 /* Cooperative Route Filtering Capability(cisco) */ #define CAPABILITY_CODE_ORF_OLD 130 /* Cooperative Route Filtering Capability(cisco) */
@ -72,6 +73,7 @@ struct graceful_restart_af {
#define CAPABILITY_CODE_ORF_LEN 5 #define CAPABILITY_CODE_ORF_LEN 5
#define CAPABILITY_CODE_EXT_MESSAGE_LEN 0 /* Extended Message Support */ #define CAPABILITY_CODE_EXT_MESSAGE_LEN 0 /* Extended Message Support */
#define CAPABILITY_CODE_ROLE_LEN 1 #define CAPABILITY_CODE_ROLE_LEN 1
#define CAPABILITY_CODE_SOFT_VERSION_LEN 1
/* Cooperative Route Filtering Capability. */ /* Cooperative Route Filtering Capability. */

View File

@ -1685,6 +1685,13 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size)
peer->v_keepalive = peer->bgp->default_keepalive; peer->v_keepalive = peer->bgp->default_keepalive;
} }
/* If another side disabled sending Software Version capability,
* we MUST drop the previous from showing in the outputs to avoid
* stale information and due to security reasons.
*/
if (peer->soft_version)
XFREE(MTYPE_BGP_SOFT_VERSION, peer->soft_version);
/* Open option part parse. */ /* Open option part parse. */
if (optlen != 0) { if (optlen != 0) {
if (bgp_open_option_parse(peer, optlen, &mp_capability) < 0) if (bgp_open_option_parse(peer, optlen, &mp_capability) < 0)

View File

@ -5575,6 +5575,30 @@ DEFUN (no_neighbor_capability_enhe,
PEER_FLAG_CAPABILITY_ENHE); PEER_FLAG_CAPABILITY_ENHE);
} }
/* neighbor capability software-version */
DEFPY(neighbor_capability_software_version,
neighbor_capability_software_version_cmd,
"[no$no] neighbor <A.B.C.D|X:X::X:X|WORD>$neighbor capability software-version",
NO_STR
NEIGHBOR_STR
NEIGHBOR_ADDR_STR2
"Advertise capability to the peer\n"
"Advertise Software Version capability to the peer\n")
{
struct peer *peer;
peer = peer_and_group_lookup_vty(vty, neighbor);
if (peer && peer->conf_if)
return CMD_SUCCESS;
if (no)
return peer_flag_unset_vty(vty, neighbor,
PEER_FLAG_CAPABILITY_SOFT_VERSION);
else
return peer_flag_set_vty(vty, neighbor,
PEER_FLAG_CAPABILITY_SOFT_VERSION);
}
static int peer_af_flag_modify_vty(struct vty *vty, const char *peer_str, static int peer_af_flag_modify_vty(struct vty *vty, const char *peer_str,
afi_t afi, safi_t safi, uint32_t flag, afi_t afi, safi_t safi, uint32_t flag,
int set) int set)
@ -10833,6 +10857,9 @@ static void bgp_show_peer_reset(struct vty * vty, struct peer *peer,
peer_down_str[(int)peer->last_reset]); peer_down_str[(int)peer->last_reset]);
json_object_int_add(json_peer, "lastResetCode", json_object_int_add(json_peer, "lastResetCode",
peer->last_reset); peer->last_reset);
json_object_string_add(json_peer, "softwareVersion",
peer->soft_version ? peer->soft_version
: "n/a");
} else { } else {
if (peer->last_reset == PEER_DOWN_NOTIFY_SEND if (peer->last_reset == PEER_DOWN_NOTIFY_SEND
|| peer->last_reset == PEER_DOWN_NOTIFY_RECEIVED) { || peer->last_reset == PEER_DOWN_NOTIFY_RECEIVED) {
@ -10851,8 +10878,10 @@ static void bgp_show_peer_reset(struct vty * vty, struct peer *peer,
BGP_NOTIFY_CEASE_HARD_RESET) BGP_NOTIFY_CEASE_HARD_RESET)
: ""); : "");
} else { } else {
vty_out(vty, " %s\n", vty_out(vty, " %s (%s)\n",
peer_down_str[(int)peer->last_reset]); peer_down_str[(int)peer->last_reset],
peer->soft_version ? peer->soft_version
: "n/a");
} }
} }
} }
@ -13852,6 +13881,27 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json,
json_object_object_add(json_cap, "hostName", json_object_object_add(json_cap, "hostName",
json_hname); json_hname);
/* Software Version capability */
json_object *json_soft_version = NULL;
json_soft_version = json_object_new_object();
if (CHECK_FLAG(p->cap, PEER_CAP_SOFT_VERSION_ADV))
json_object_string_add(
json_soft_version,
"advertisedSoftwareVersion",
cmd_software_version_get());
if (CHECK_FLAG(p->cap, PEER_CAP_SOFT_VERSION_RCV))
json_object_string_add(
json_soft_version,
"receivedSoftwareVersion",
p->soft_version ? p->soft_version
: "n/a");
json_object_object_add(json_cap, "softwareVersion",
json_soft_version);
/* Graceful Restart */ /* Graceful Restart */
if (CHECK_FLAG(p->cap, PEER_CAP_RESTART_RCV) || if (CHECK_FLAG(p->cap, PEER_CAP_RESTART_RCV) ||
CHECK_FLAG(p->cap, PEER_CAP_RESTART_ADV)) { CHECK_FLAG(p->cap, PEER_CAP_RESTART_ADV)) {
@ -14227,6 +14277,25 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json,
vty_out(vty, "\n"); vty_out(vty, "\n");
/* Software Version capability */
vty_out(vty, " Version Capability:");
if (CHECK_FLAG(p->cap, PEER_CAP_SOFT_VERSION_ADV)) {
vty_out(vty,
" advertised software version (%s)",
cmd_software_version_get());
} else
vty_out(vty, " not advertised");
if (CHECK_FLAG(p->cap, PEER_CAP_SOFT_VERSION_RCV)) {
vty_out(vty, " received software version (%s)",
p->soft_version ? p->soft_version
: "n/a");
} else
vty_out(vty, " not received");
vty_out(vty, "\n");
/* Graceful Restart */ /* Graceful Restart */
if (CHECK_FLAG(p->cap, PEER_CAP_RESTART_RCV) || if (CHECK_FLAG(p->cap, PEER_CAP_RESTART_RCV) ||
CHECK_FLAG(p->cap, PEER_CAP_RESTART_ADV)) { CHECK_FLAG(p->cap, PEER_CAP_RESTART_ADV)) {
@ -16967,7 +17036,7 @@ static void bgp_config_write_redistribute(struct vty *vty, struct bgp *bgp,
/* peer-group helpers for config-write */ /* peer-group helpers for config-write */
static bool peergroup_flag_check(struct peer *peer, uint64_t flag) bool peergroup_flag_check(struct peer *peer, uint64_t flag)
{ {
if (!peer_group_active(peer)) { if (!peer_group_active(peer)) {
if (CHECK_FLAG(peer->flags_invert, flag)) if (CHECK_FLAG(peer->flags_invert, flag))
@ -17507,6 +17576,11 @@ static void bgp_config_write_peer_global(struct vty *vty, struct bgp *bgp,
addr); addr);
} }
/* capability software-version */
if (peergroup_flag_check(peer, PEER_FLAG_CAPABILITY_SOFT_VERSION))
vty_out(vty, " neighbor %s capability software-version\n",
addr);
/* dont-capability-negotiation */ /* dont-capability-negotiation */
if (peergroup_flag_check(peer, PEER_FLAG_DONT_CAPABILITY)) if (peergroup_flag_check(peer, PEER_FLAG_DONT_CAPABILITY))
vty_out(vty, " neighbor %s dont-capability-negotiate\n", addr); vty_out(vty, " neighbor %s dont-capability-negotiate\n", addr);
@ -19677,6 +19751,9 @@ void bgp_vty_init(void)
install_element(BGP_NODE, &neighbor_capability_enhe_cmd); install_element(BGP_NODE, &neighbor_capability_enhe_cmd);
install_element(BGP_NODE, &no_neighbor_capability_enhe_cmd); install_element(BGP_NODE, &no_neighbor_capability_enhe_cmd);
/* "neighbor capability software-version" commands.*/
install_element(BGP_NODE, &neighbor_capability_software_version_cmd);
/* "neighbor capability orf prefix-list" commands.*/ /* "neighbor capability orf prefix-list" commands.*/
install_element(BGP_NODE, &neighbor_capability_orf_prefix_hidden_cmd); install_element(BGP_NODE, &neighbor_capability_orf_prefix_hidden_cmd);
install_element(BGP_NODE, install_element(BGP_NODE,

View File

@ -181,5 +181,6 @@ int bgp_vty_find_and_parse_bgp(struct vty *vty, struct cmd_token **argv,
extern int bgp_show_summary_vty(struct vty *vty, const char *name, afi_t afi, extern int bgp_show_summary_vty(struct vty *vty, const char *name, afi_t afi,
safi_t safi, const char *neighbor, int as_type, safi_t safi, const char *neighbor, int as_type,
as_t as, uint16_t show_flags); as_t as, uint16_t show_flags);
extern bool peergroup_flag_check(struct peer *peer, uint64_t flag);
#endif /* _QUAGGA_BGP_VTY_H */ #endif /* _QUAGGA_BGP_VTY_H */

View File

@ -1178,6 +1178,8 @@ static void peer_free(struct peer *peer)
XFREE(MTYPE_PEER_CONF_IF, peer->conf_if); XFREE(MTYPE_PEER_CONF_IF, peer->conf_if);
XFREE(MTYPE_BGP_SOFT_VERSION, peer->soft_version);
/* Remove BFD configuration. */ /* Remove BFD configuration. */
if (peer->bfd_config) if (peer->bfd_config)
bgp_peer_remove_bfd_config(peer); bgp_peer_remove_bfd_config(peer);
@ -2631,6 +2633,7 @@ int peer_delete(struct peer *peer)
XFREE(MTYPE_BGP_PEER_HOST, peer->hostname); XFREE(MTYPE_BGP_PEER_HOST, peer->hostname);
XFREE(MTYPE_BGP_PEER_HOST, peer->domainname); XFREE(MTYPE_BGP_PEER_HOST, peer->domainname);
XFREE(MTYPE_BGP_SOFT_VERSION, peer->soft_version);
peer_unlock(peer); /* initial reference */ peer_unlock(peer); /* initial reference */
@ -2774,6 +2777,13 @@ static void peer_group2peer_config_copy(struct peer_group *group,
if (CHECK_FLAG(conf->flags, PEER_FLAG_CAPABILITY_ENHE)) if (CHECK_FLAG(conf->flags, PEER_FLAG_CAPABILITY_ENHE))
SET_FLAG(peer->flags, PEER_FLAG_CAPABILITY_ENHE); SET_FLAG(peer->flags, PEER_FLAG_CAPABILITY_ENHE);
/* capability software-version apply */
if (!CHECK_FLAG(peer->flags_override,
PEER_FLAG_CAPABILITY_SOFT_VERSION))
if (CHECK_FLAG(conf->flags, PEER_FLAG_CAPABILITY_SOFT_VERSION))
SET_FLAG(peer->flags,
PEER_FLAG_CAPABILITY_SOFT_VERSION);
/* password apply */ /* password apply */
if (!CHECK_FLAG(peer->flags_override, PEER_FLAG_PASSWORD)) if (!CHECK_FLAG(peer->flags_override, PEER_FLAG_PASSWORD))
PEER_STR_ATTR_INHERIT(peer, group, password, PEER_STR_ATTR_INHERIT(peer, group, password,
@ -4386,6 +4396,7 @@ static const struct peer_flag_action peer_flag_action_list[] = {
{PEER_FLAG_PORT, 0, peer_change_reset}, {PEER_FLAG_PORT, 0, peer_change_reset},
{PEER_FLAG_AIGP, 0, peer_change_none}, {PEER_FLAG_AIGP, 0, peer_change_none},
{PEER_FLAG_GRACEFUL_SHUTDOWN, 0, peer_change_none}, {PEER_FLAG_GRACEFUL_SHUTDOWN, 0, peer_change_none},
{PEER_FLAG_CAPABILITY_SOFT_VERSION, 0, peer_change_reset},
{0, 0, 0}}; {0, 0, 0}};
static const struct peer_flag_action peer_af_flag_action_list[] = { static const struct peer_flag_action peer_af_flag_action_list[] = {

View File

@ -1250,6 +1250,8 @@ struct peer {
#define PEER_CAP_GRACEFUL_RESTART_N_BIT_RCV (1U << 24) #define PEER_CAP_GRACEFUL_RESTART_N_BIT_RCV (1U << 24)
#define PEER_CAP_ROLE_ADV (1U << 25) /* role advertised */ #define PEER_CAP_ROLE_ADV (1U << 25) /* role advertised */
#define PEER_CAP_ROLE_RCV (1U << 26) /* role received */ #define PEER_CAP_ROLE_RCV (1U << 26) /* role received */
#define PEER_CAP_SOFT_VERSION_ADV (1U << 27)
#define PEER_CAP_SOFT_VERSION_RCV (1U << 28)
/* Capability flags (reset in bgp_stop) */ /* Capability flags (reset in bgp_stop) */
uint32_t af_cap[AFI_MAX][SAFI_MAX]; uint32_t af_cap[AFI_MAX][SAFI_MAX];
@ -1376,6 +1378,7 @@ struct peer {
#define PEER_FLAG_PORT (1ULL << 33) #define PEER_FLAG_PORT (1ULL << 33)
#define PEER_FLAG_AIGP (1ULL << 34) #define PEER_FLAG_AIGP (1ULL << 34)
#define PEER_FLAG_GRACEFUL_SHUTDOWN (1ULL << 35) #define PEER_FLAG_GRACEFUL_SHUTDOWN (1ULL << 35)
#define PEER_FLAG_CAPABILITY_SOFT_VERSION (1ULL << 36)
/* /*
*GR-Disabled mode means unset PEER_FLAG_GRACEFUL_RESTART *GR-Disabled mode means unset PEER_FLAG_GRACEFUL_RESTART
@ -1771,6 +1774,10 @@ struct peer {
/* Path attributes treat-as-withdraw */ /* Path attributes treat-as-withdraw */
bool withdraw_attrs[BGP_ATTR_MAX]; bool withdraw_attrs[BGP_ATTR_MAX];
/* BGP Software Version Capability */
#define BGP_MAX_SOFT_VERSION 64
char *soft_version;
QOBJ_FIELDS; QOBJ_FIELDS;
}; };
DECLARE_QOBJ_TYPE(peer); DECLARE_QOBJ_TYPE(peer);

View File

@ -2020,10 +2020,17 @@ Capability Negotiation
.. clicmd:: neighbor PEER override-capability .. clicmd:: neighbor PEER override-capability
Override the result of Capability Negotiation with local configuration. Override the result of Capability Negotiation with local configuration.
Ignore remote peer's capability value. Ignore remote peer's capability value.
.. clicmd:: neighbor PEER capability software-version
Send the software version in the BGP OPEN message to the neighbor. This is
very useful in environments with a large amount of peers with different
versions of FRR or any other vendor.
Disabled by default.
.. _bgp-as-path-access-lists: .. _bgp-as-path-access-lists:
AS Path Access Lists AS Path Access Lists

View File

@ -127,6 +127,11 @@ bool cmd_allow_reserved_ranges_get(void)
return host.allow_reserved_ranges; return host.allow_reserved_ranges;
} }
const char *cmd_software_version_get(void)
{
return FRR_FULL_NAME "/" FRR_VERSION;
}
static int root_on_exit(struct vty *vty); static int root_on_exit(struct vty *vty);
/* Standard command node structures. */ /* Standard command node structures. */

View File

@ -606,6 +606,7 @@ extern const char *cmd_domainname_get(void);
extern const char *cmd_system_get(void); extern const char *cmd_system_get(void);
extern const char *cmd_release_get(void); extern const char *cmd_release_get(void);
extern const char *cmd_version_get(void); extern const char *cmd_version_get(void);
extern const char *cmd_software_version_get(void);
extern bool cmd_allow_reserved_ranges_get(void); extern bool cmd_allow_reserved_ranges_get(void);
/* NOT safe for general use; call this only if DEV_BUILD! */ /* NOT safe for general use; call this only if DEV_BUILD! */

View File

@ -176,9 +176,9 @@ struct test_peer_attr {
enum test_peer_attr_type type; enum test_peer_attr_type type;
union { union {
uint32_t flag; uint64_t flag;
struct { struct {
uint32_t flag; uint64_t flag;
size_t direct; size_t direct;
} filter; } filter;
} u; } u;
@ -281,6 +281,18 @@ static struct test_peer_attr test_peer_attrs[] = {
.o.invert_peer = true, .o.invert_peer = true,
.o.use_iface_peer = true, .o.use_iface_peer = true,
}, },
{
.cmd = "capability software-version",
.u.flag = PEER_FLAG_CAPABILITY_SOFT_VERSION,
.type = PEER_AT_GLOBAL_FLAG,
},
{
.cmd = "capability software-version",
.u.flag = PEER_FLAG_CAPABILITY_SOFT_VERSION,
.type = PEER_AT_GLOBAL_FLAG,
.o.invert_peer = true,
.o.use_iface_peer = true,
},
{ {
.cmd = "description", .cmd = "description",
.peer_cmd = "description FRR Peer", .peer_cmd = "description FRR Peer",

View File

@ -0,0 +1,8 @@
!
router bgp 65001
no bgp ebgp-requires-policy
neighbor 192.168.1.2 remote-as external
neighbor 192.168.1.2 timers 1 3
neighbor 192.168.1.2 timers connect 1
neighbor 192.168.1.2 capability software-version
!

View File

@ -0,0 +1,4 @@
!
int r1-eth0
ip address 192.168.1.1/24
!

View File

@ -0,0 +1,7 @@
router bgp 65002
no bgp ebgp-requires-policy
neighbor 192.168.1.1 remote-as external
neighbor 192.168.1.1 timers 1 3
neighbor 192.168.1.1 timers connect 1
neighbor 192.168.1.1 capability software-version
!

View File

@ -0,0 +1,4 @@
!
int r2-eth0
ip address 192.168.1.2/24
!

View File

@ -0,0 +1,111 @@
#!/usr/bin/env python
# Copyright (c) 2022 by
# Donatas Abraitis <donatas@opensourcerouting.org>
#
# 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 Software Version capability works if forced with a knob.
Reference: https://datatracker.ietf.org/doc/html/draft-abraitis-bgp-version-capability
"""
import os
import re
import sys
import json
import pytest
import functools
pytestmark = pytest.mark.bgpd
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
pytestmark = [pytest.mark.bgpd]
def setup_module(mod):
topodef = {"s1": ("r1", "r2")}
tgen = Topogen(topodef, mod.__name__)
tgen.start_topology()
router_list = tgen.routers()
for i, (rname, router) in enumerate(router_list.items(), 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_software_version():
tgen = get_topogen()
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
r1 = tgen.gears["r1"]
def _bgp_converge():
output = json.loads(r1.vtysh_cmd("show bgp summary json"))
expected = {"ipv4Unicast": {"peers": {"192.168.1.2": {"state": "Established"}}}}
return topotest.json_cmp(output, expected)
test_func = functools.partial(
_bgp_converge,
)
_, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
assert result is None, "Can't converge"
def _bgp_check_software_version():
output = json.loads(r1.vtysh_cmd("show bgp neighbor 192.168.1.2 json"))
try:
versions = output["192.168.1.2"]["neighborCapabilities"]["softwareVersion"]
adv = versions["advertisedSoftwareVersion"]
rcv = versions["receivedSoftwareVersion"]
if not adv and not rcv:
return False
pattern = "^FRRouting/\\d.+"
if re.search(pattern, adv) and re.search(pattern, rcv):
return True
except:
return False
return False
assert _bgp_check_software_version(), "Neighbor's software version is n/a"
if __name__ == "__main__":
args = ["-s"] + sys.argv[1:]
sys.exit(pytest.main(args))