summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bgpd/bgp_fsm.c9
-rw-r--r--bgpd/bgp_memory.c2
-rw-r--r--bgpd/bgp_memory.h2
-rw-r--r--bgpd/bgp_open.c86
-rw-r--r--bgpd/bgp_open.h2
-rw-r--r--bgpd/bgp_packet.c7
-rw-r--r--bgpd/bgp_vty.c83
-rw-r--r--bgpd/bgp_vty.h1
-rw-r--r--bgpd/bgpd.c11
-rw-r--r--bgpd/bgpd.h7
-rw-r--r--doc/user/bgp.rst9
-rw-r--r--lib/command.c5
-rw-r--r--lib/command.h1
-rw-r--r--tests/bgpd/test_peer_attr.c16
-rw-r--r--tests/topotests/bgp_software_version/__init__.py0
-rw-r--r--tests/topotests/bgp_software_version/r1/bgpd.conf8
-rw-r--r--tests/topotests/bgp_software_version/r1/zebra.conf4
-rw-r--r--tests/topotests/bgp_software_version/r2/bgpd.conf7
-rw-r--r--tests/topotests/bgp_software_version/r2/zebra.conf4
-rw-r--r--tests/topotests/bgp_software_version/test_bgp_software_version.py111
20 files changed, 369 insertions, 6 deletions
diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c
index faf3a4994d..540d653116 100644
--- a/bgpd/bgp_fsm.c
+++ b/bgpd/bgp_fsm.c
@@ -297,6 +297,15 @@ static struct peer *peer_xfer_conn(struct peer *from_peer)
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) {
peer->af_sflags[afi][safi] = from_peer->af_sflags[afi][safi];
peer->af_cap[afi][safi] = from_peer->af_cap[afi][safi];
diff --git a/bgpd/bgp_memory.c b/bgpd/bgp_memory.c
index 850657d35e..efa26e292a 100644
--- a/bgpd/bgp_memory.c
+++ b/bgpd/bgp_memory.c
@@ -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, BGP_NOTIFICATION, "BGP Notification Message");
+
+DEFINE_MTYPE(BGPD, BGP_SOFT_VERSION, "Software Version");
diff --git a/bgpd/bgp_memory.h b/bgpd/bgp_memory.h
index 510cfa21c9..c8542c5b87 100644
--- a/bgpd/bgp_memory.h
+++ b/bgpd/bgp_memory.h
@@ -138,4 +138,6 @@ DECLARE_MTYPE(EVPN_REMOTE_IP);
DECLARE_MTYPE(BGP_NOTIFICATION);
+DECLARE_MTYPE(BGP_SOFT_VERSION);
+
#endif /* _QUAGGA_BGP_MEMORY_H */
diff --git a/bgpd/bgp_open.c b/bgpd/bgp_open.c
index 3e361fccfd..daae535250 100644
--- a/bgpd/bgp_open.c
+++ b/bgpd/bgp_open.c
@@ -59,6 +59,7 @@ static const struct message capcode_str[] = {
{CAPABILITY_CODE_EXT_MESSAGE, "BGP Extended Message"},
{CAPABILITY_CODE_LLGR, "Long-lived BGP Graceful Restart"},
{CAPABILITY_CODE_ROLE, "Role"},
+ {CAPABILITY_CODE_SOFT_VERSION, "Software Version"},
{0}};
/* 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_LLGR] = CAPABILITY_CODE_LLGR_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.
@@ -103,6 +105,7 @@ static const size_t cap_modsizes[] = {
[CAPABILITY_CODE_EXT_MESSAGE] = 1,
[CAPABILITY_CODE_LLGR] = 1,
[CAPABILITY_CODE_ROLE] = 1,
+ [CAPABILITY_CODE_SOFT_VERSION] = 1,
};
/* 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;
}
+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.
* 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_EXT_MESSAGE:
case CAPABILITY_CODE_ROLE:
+ case CAPABILITY_CODE_SOFT_VERSION:
/* Check length. */
if (caphdr.length < cap_minsizes[caphdr.code]) {
zlog_info(
@@ -1089,6 +1128,9 @@ static int bgp_capability_parse(struct peer *peer, size_t length,
case CAPABILITY_CODE_ROLE:
ret = bgp_capability_role(peer, &caphdr);
break;
+ case CAPABILITY_CODE_SOFT_VERSION:
+ ret = bgp_capability_software_version(peer, &caphdr);
+ break;
default:
if (caphdr.code > 128) {
/* 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_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. */
len = stream_get_endp(s) - cp - 1;
diff --git a/bgpd/bgp_open.h b/bgpd/bgp_open.h
index 6be94443c8..d1dc1c8a90 100644
--- a/bgpd/bgp_open.h
+++ b/bgpd/bgp_open.h
@@ -52,6 +52,7 @@ struct graceful_restart_af {
#define CAPABILITY_CODE_ENHANCED_RR 70 /* Enhanced Route Refresh capability */
#define CAPABILITY_CODE_LLGR 71 /* Long-lived Graceful Restart */
#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_REFRESH_OLD 128 /* Route Refresh 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_EXT_MESSAGE_LEN 0 /* Extended Message Support */
#define CAPABILITY_CODE_ROLE_LEN 1
+#define CAPABILITY_CODE_SOFT_VERSION_LEN 1
/* Cooperative Route Filtering Capability. */
diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c
index 080cf0ae40..511bd00e7e 100644
--- a/bgpd/bgp_packet.c
+++ b/bgpd/bgp_packet.c
@@ -1685,6 +1685,13 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size)
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. */
if (optlen != 0) {
if (bgp_open_option_parse(peer, optlen, &mp_capability) < 0)
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
index 27f0ea9b71..810938e12c 100644
--- a/bgpd/bgp_vty.c
+++ b/bgpd/bgp_vty.c
@@ -5575,6 +5575,30 @@ DEFUN (no_neighbor_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,
afi_t afi, safi_t safi, uint32_t flag,
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]);
json_object_int_add(json_peer, "lastResetCode",
peer->last_reset);
+ json_object_string_add(json_peer, "softwareVersion",
+ peer->soft_version ? peer->soft_version
+ : "n/a");
} else {
if (peer->last_reset == PEER_DOWN_NOTIFY_SEND
|| 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)
: "");
} else {
- vty_out(vty, " %s\n",
- peer_down_str[(int)peer->last_reset]);
+ vty_out(vty, " %s (%s)\n",
+ 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_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 */
if (CHECK_FLAG(p->cap, PEER_CAP_RESTART_RCV) ||
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");
+ /* 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 */
if (CHECK_FLAG(p->cap, PEER_CAP_RESTART_RCV) ||
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 */
-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 (CHECK_FLAG(peer->flags_invert, flag))
@@ -17507,6 +17576,11 @@ static void bgp_config_write_peer_global(struct vty *vty, struct bgp *bgp,
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 */
if (peergroup_flag_check(peer, PEER_FLAG_DONT_CAPABILITY))
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, &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.*/
install_element(BGP_NODE, &neighbor_capability_orf_prefix_hidden_cmd);
install_element(BGP_NODE,
diff --git a/bgpd/bgp_vty.h b/bgpd/bgp_vty.h
index 019789dff8..820fc4429d 100644
--- a/bgpd/bgp_vty.h
+++ b/bgpd/bgp_vty.h
@@ -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,
safi_t safi, const char *neighbor, int as_type,
as_t as, uint16_t show_flags);
+extern bool peergroup_flag_check(struct peer *peer, uint64_t flag);
#endif /* _QUAGGA_BGP_VTY_H */
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index f4e823e212..a09d25e8ac 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -1178,6 +1178,8 @@ static void peer_free(struct peer *peer)
XFREE(MTYPE_PEER_CONF_IF, peer->conf_if);
+ XFREE(MTYPE_BGP_SOFT_VERSION, peer->soft_version);
+
/* Remove BFD configuration. */
if (peer->bfd_config)
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->domainname);
+ XFREE(MTYPE_BGP_SOFT_VERSION, peer->soft_version);
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))
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 */
if (!CHECK_FLAG(peer->flags_override, PEER_FLAG_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_AIGP, 0, peer_change_none},
{PEER_FLAG_GRACEFUL_SHUTDOWN, 0, peer_change_none},
+ {PEER_FLAG_CAPABILITY_SOFT_VERSION, 0, peer_change_reset},
{0, 0, 0}};
static const struct peer_flag_action peer_af_flag_action_list[] = {
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index 64f016dfc5..a8bb7a5117 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -1250,6 +1250,8 @@ struct peer {
#define PEER_CAP_GRACEFUL_RESTART_N_BIT_RCV (1U << 24)
#define PEER_CAP_ROLE_ADV (1U << 25) /* role advertised */
#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) */
uint32_t af_cap[AFI_MAX][SAFI_MAX];
@@ -1376,6 +1378,7 @@ struct peer {
#define PEER_FLAG_PORT (1ULL << 33)
#define PEER_FLAG_AIGP (1ULL << 34)
#define PEER_FLAG_GRACEFUL_SHUTDOWN (1ULL << 35)
+#define PEER_FLAG_CAPABILITY_SOFT_VERSION (1ULL << 36)
/*
*GR-Disabled mode means unset PEER_FLAG_GRACEFUL_RESTART
@@ -1771,6 +1774,10 @@ struct peer {
/* Path attributes treat-as-withdraw */
bool withdraw_attrs[BGP_ATTR_MAX];
+ /* BGP Software Version Capability */
+#define BGP_MAX_SOFT_VERSION 64
+ char *soft_version;
+
QOBJ_FIELDS;
};
DECLARE_QOBJ_TYPE(peer);
diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst
index 6979c77d73..c5319a8e88 100644
--- a/doc/user/bgp.rst
+++ b/doc/user/bgp.rst
@@ -2020,10 +2020,17 @@ Capability Negotiation
.. clicmd:: neighbor PEER override-capability
-
Override the result of Capability Negotiation with local configuration.
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:
AS Path Access Lists
diff --git a/lib/command.c b/lib/command.c
index 4e8194bbc6..c8f150edf6 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -127,6 +127,11 @@ bool cmd_allow_reserved_ranges_get(void)
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);
/* Standard command node structures. */
diff --git a/lib/command.h b/lib/command.h
index 5aaa6d6cd8..28bb96e0ec 100644
--- a/lib/command.h
+++ b/lib/command.h
@@ -606,6 +606,7 @@ extern const char *cmd_domainname_get(void);
extern const char *cmd_system_get(void);
extern const char *cmd_release_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);
/* NOT safe for general use; call this only if DEV_BUILD! */
diff --git a/tests/bgpd/test_peer_attr.c b/tests/bgpd/test_peer_attr.c
index cc4f71e688..cdedd0c906 100644
--- a/tests/bgpd/test_peer_attr.c
+++ b/tests/bgpd/test_peer_attr.c
@@ -176,9 +176,9 @@ struct test_peer_attr {
enum test_peer_attr_type type;
union {
- uint32_t flag;
+ uint64_t flag;
struct {
- uint32_t flag;
+ uint64_t flag;
size_t direct;
} filter;
} u;
@@ -282,6 +282,18 @@ static struct test_peer_attr test_peer_attrs[] = {
.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",
.peer_cmd = "description FRR Peer",
.group_cmd = "description FRR Group",
diff --git a/tests/topotests/bgp_software_version/__init__.py b/tests/topotests/bgp_software_version/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bgp_software_version/__init__.py
diff --git a/tests/topotests/bgp_software_version/r1/bgpd.conf b/tests/topotests/bgp_software_version/r1/bgpd.conf
new file mode 100644
index 0000000000..80a05b3b64
--- /dev/null
+++ b/tests/topotests/bgp_software_version/r1/bgpd.conf
@@ -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
+!
diff --git a/tests/topotests/bgp_software_version/r1/zebra.conf b/tests/topotests/bgp_software_version/r1/zebra.conf
new file mode 100644
index 0000000000..b29940f46a
--- /dev/null
+++ b/tests/topotests/bgp_software_version/r1/zebra.conf
@@ -0,0 +1,4 @@
+!
+int r1-eth0
+ ip address 192.168.1.1/24
+!
diff --git a/tests/topotests/bgp_software_version/r2/bgpd.conf b/tests/topotests/bgp_software_version/r2/bgpd.conf
new file mode 100644
index 0000000000..c8ab017208
--- /dev/null
+++ b/tests/topotests/bgp_software_version/r2/bgpd.conf
@@ -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
+!
diff --git a/tests/topotests/bgp_software_version/r2/zebra.conf b/tests/topotests/bgp_software_version/r2/zebra.conf
new file mode 100644
index 0000000000..cffe827363
--- /dev/null
+++ b/tests/topotests/bgp_software_version/r2/zebra.conf
@@ -0,0 +1,4 @@
+!
+int r2-eth0
+ ip address 192.168.1.2/24
+!
diff --git a/tests/topotests/bgp_software_version/test_bgp_software_version.py b/tests/topotests/bgp_software_version/test_bgp_software_version.py
new file mode 100644
index 0000000000..55327b77f8
--- /dev/null
+++ b/tests/topotests/bgp_software_version/test_bgp_software_version.py
@@ -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))