summaryrefslogtreecommitdiffstats
path: root/bgpd
diff options
context:
space:
mode:
authorDonatas Abraitis <donatas@opensourcerouting.org>2023-02-25 08:00:23 +0100
committerDonatas Abraitis <donatas@opensourcerouting.org>2023-02-15 22:14:48 +0100
commit234f6fd4f4804bb17bd8cbb1dd91994a914f38d2 (patch)
tree5b8381007aa613d875c454144c83c855ccf4e947 /bgpd
parenttests: Increase flags from uint32_t to uint64_t (diff)
downloadfrr-234f6fd4f4804bb17bd8cbb1dd91994a914f38d2.tar.xz
frr-234f6fd4f4804bb17bd8cbb1dd91994a914f38d2.zip
bgpd: Add BGP Software Version Capability
Implement: https://datatracker.ietf.org/doc/html/draft-abraitis-bgp-version-capability Tested with GoBGP: ``` % ./gobgp neighbor 192.168.10.124 BGP neighbor is 192.168.10.124, remote AS 65001 BGP version 4, remote router ID 200.200.200.202 BGP state = ESTABLISHED, up for 00:01:49 BGP OutQ = 0, Flops = 0 Hold time is 3, keepalive interval is 1 seconds Configured hold time is 90, keepalive interval is 30 seconds Neighbor capabilities: multiprotocol: ipv4-unicast: advertised and received ipv6-unicast: advertised route-refresh: advertised and received extended-nexthop: advertised Local: nlri: ipv4-unicast, nexthop: ipv6 UnknownCapability(6): received UnknownCapability(9): received graceful-restart: advertised and received Local: restart time 10 sec ipv6-unicast ipv4-unicast Remote: restart time 120 sec, notification flag set ipv4-unicast, forward flag set 4-octet-as: advertised and received add-path: received Remote: ipv4-unicast: receive enhanced-route-refresh: received long-lived-graceful-restart: advertised and received Local: ipv6-unicast, restart time 10 sec ipv4-unicast, restart time 20 sec Remote: ipv4-unicast, restart time 0 sec, forward flag set fqdn: advertised and received Local: name: donatas-pc, domain: Remote: name: spine1-debian-11, domain: software-version: advertised and received Local: GoBGP/3.10.0 Remote: FRRouting/8.5-dev-MyOwnFRRVersion-gdc92f44a45-dirt cisco-route-refresh: received Message statistics: ``` FRR side: ``` root@spine1-debian-11:~# vtysh -c 'show bgp neighbor 192.168.10.17 json' | \ > jq '."192.168.10.17".neighborCapabilities.softwareVersion.receivedSoftwareVersion' "GoBGP/3.10.0" root@spine1-debian-11:~# ``` Signed-off-by: Donatas Abraitis <donatas@opensourcerouting.org>
Diffstat (limited to 'bgpd')
-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
10 files changed, 207 insertions, 3 deletions
diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c
index faf3a4994..540d65311 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 850657d35..efa26e292 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 510cfa21c..c8542c5b8 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 3e361fccf..daae53525 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 6be94443c..d1dc1c8a9 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 080cf0ae4..511bd00e7 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 27f0ea9b7..810938e12 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 019789dff..820fc4429 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 f4e823e21..a09d25e8a 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 64f016dfc..a8bb7a511 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);