diff options
author | Donatas Abraitis <donatas@opensourcerouting.org> | 2023-02-25 08:00:23 +0100 |
---|---|---|
committer | Donatas Abraitis <donatas@opensourcerouting.org> | 2023-02-15 22:14:48 +0100 |
commit | 234f6fd4f4804bb17bd8cbb1dd91994a914f38d2 (patch) | |
tree | 5b8381007aa613d875c454144c83c855ccf4e947 /bgpd | |
parent | tests: Increase flags from uint32_t to uint64_t (diff) | |
download | frr-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.c | 9 | ||||
-rw-r--r-- | bgpd/bgp_memory.c | 2 | ||||
-rw-r--r-- | bgpd/bgp_memory.h | 2 | ||||
-rw-r--r-- | bgpd/bgp_open.c | 86 | ||||
-rw-r--r-- | bgpd/bgp_open.h | 2 | ||||
-rw-r--r-- | bgpd/bgp_packet.c | 7 | ||||
-rw-r--r-- | bgpd/bgp_vty.c | 83 | ||||
-rw-r--r-- | bgpd/bgp_vty.h | 1 | ||||
-rw-r--r-- | bgpd/bgpd.c | 11 | ||||
-rw-r--r-- | bgpd/bgpd.h | 7 |
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); |