From 234f6fd4f4804bb17bd8cbb1dd91994a914f38d2 Mon Sep 17 00:00:00 2001 From: Donatas Abraitis Date: Sat, 25 Feb 2023 07:00:23 +0000 Subject: [PATCH] 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 --- bgpd/bgp_fsm.c | 9 ++ bgpd/bgp_memory.c | 2 + bgpd/bgp_memory.h | 2 + bgpd/bgp_open.c | 86 ++++++++++++++ bgpd/bgp_open.h | 2 + bgpd/bgp_packet.c | 7 ++ bgpd/bgp_vty.c | 83 ++++++++++++- bgpd/bgp_vty.h | 1 + bgpd/bgpd.c | 11 ++ bgpd/bgpd.h | 7 ++ doc/user/bgp.rst | 9 +- lib/command.c | 5 + lib/command.h | 1 + tests/bgpd/test_peer_attr.c | 12 ++ .../bgp_software_version/__init__.py | 0 .../bgp_software_version/r1/bgpd.conf | 8 ++ .../bgp_software_version/r1/zebra.conf | 4 + .../bgp_software_version/r2/bgpd.conf | 7 ++ .../bgp_software_version/r2/zebra.conf | 4 + .../test_bgp_software_version.py | 111 ++++++++++++++++++ 20 files changed, 367 insertions(+), 4 deletions(-) create mode 100644 tests/topotests/bgp_software_version/__init__.py create mode 100644 tests/topotests/bgp_software_version/r1/bgpd.conf create mode 100644 tests/topotests/bgp_software_version/r1/zebra.conf create mode 100644 tests/topotests/bgp_software_version/r2/bgpd.conf create mode 100644 tests/topotests/bgp_software_version/r2/zebra.conf create mode 100644 tests/topotests/bgp_software_version/test_bgp_software_version.py 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 $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 0452500abf..cdedd0c906 100644 --- a/tests/bgpd/test_peer_attr.c +++ b/tests/bgpd/test_peer_attr.c @@ -281,6 +281,18 @@ static struct test_peer_attr test_peer_attrs[] = { .o.invert_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", .peer_cmd = "description FRR Peer", 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 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 +# +# 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)) -- 2.39.5