]> git.proxmox.com Git - mirror_frr.git/commitdiff
bgpd: Add BGP Software Version Capability
authorDonatas Abraitis <donatas@opensourcerouting.org>
Sat, 25 Feb 2023 07:00:23 +0000 (07:00 +0000)
committerDonatas Abraitis <donatas@opensourcerouting.org>
Wed, 15 Feb 2023 21:14:48 +0000 (23:14 +0200)
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>
20 files changed:
bgpd/bgp_fsm.c
bgpd/bgp_memory.c
bgpd/bgp_memory.h
bgpd/bgp_open.c
bgpd/bgp_open.h
bgpd/bgp_packet.c
bgpd/bgp_vty.c
bgpd/bgp_vty.h
bgpd/bgpd.c
bgpd/bgpd.h
doc/user/bgp.rst
lib/command.c
lib/command.h
tests/bgpd/test_peer_attr.c
tests/topotests/bgp_software_version/__init__.py [new file with mode: 0644]
tests/topotests/bgp_software_version/r1/bgpd.conf [new file with mode: 0644]
tests/topotests/bgp_software_version/r1/zebra.conf [new file with mode: 0644]
tests/topotests/bgp_software_version/r2/bgpd.conf [new file with mode: 0644]
tests/topotests/bgp_software_version/r2/zebra.conf [new file with mode: 0644]
tests/topotests/bgp_software_version/test_bgp_software_version.py [new file with mode: 0644]

index faf3a4994dd914814aace12e87465c874d89784b..540d653116de6f9e736fe3f5018124126840e70c 100644 (file)
@@ -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];
index 850657d35ed0c730d57298c45ab712baf17ef711..efa26e292a3f2f8bab0c7ab10459c1e57cb01ff3 100644 (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, BGP_NOTIFICATION, "BGP Notification Message");
+
+DEFINE_MTYPE(BGPD, BGP_SOFT_VERSION, "Software Version");
index 510cfa21c9217913f0459c09d61c43cd80be8279..c8542c5b87cb68613c1962a53583a09f9a017160 100644 (file)
@@ -138,4 +138,6 @@ DECLARE_MTYPE(EVPN_REMOTE_IP);
 
 DECLARE_MTYPE(BGP_NOTIFICATION);
 
+DECLARE_MTYPE(BGP_SOFT_VERSION);
+
 #endif /* _QUAGGA_BGP_MEMORY_H */
index 3e361fccfd1c36e1f2be219b0e1e2fc71778506f..daae53525074d31eca593d1202ad19c5599215d5 100644 (file)
@@ -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;
 
index 6be94443c80f818ea0a1b0d102d2497469700f85..d1dc1c8a901fc3869bd9166bca42a3a93049f2cb 100644 (file)
@@ -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.  */
 
index 080cf0ae40405ac1c05a6b4ef24d134f23d32ad7..511bd00e7e1805a6d4e9c12f80834936721d4a8f 100644 (file)
@@ -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)
index 27f0ea9b7191a9ad8e312912c30050368eb8a3f9..810938e12c0cee27a506df024474745c3aed3134 100644 (file)
@@ -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,
index 019789dff84a8e86a54efae7c3f487d6c99dece6..820fc4429ddbea34957ef5cc2d8500f4ff7f70f6 100644 (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,
                                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 */
index f4e823e21286da553286eb205c5c5a6f82a16172..a09d25e8ac08ac28b1826ec69f1d4d00d7c00b4f 100644 (file)
@@ -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[] = {
index 64f016dfc5b7ce787749106ecd74775928a6dbfd..a8bb7a51179422879cd7dbc0621c31bc325cbd99 100644 (file)
@@ -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);
index 6979c77d736d782e17586ca9a507bdbeaa1182e6..c5319a8e8830c368eddc0f75ef0778ef22906044 100644 (file)
@@ -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
index 4e8194bbc6e854bc30efd3ab60dcd8fca9333125..c8f150edf651842661c5b31296c400868f07fac8 100644 (file)
@@ -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. */
index 5aaa6d6cd8b5ece1aae1f27089b5c53e81dc4c47..28bb96e0ec38da9652cf52f7e397d59d0744564c 100644 (file)
@@ -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! */
index 0452500abfb4dd10274484971c57ea3130f69d0e..cdedd0c906a52aa073d50b38b8db5cf1bd84f90a 100644 (file)
@@ -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 (file)
index 0000000..e69de29
diff --git a/tests/topotests/bgp_software_version/r1/bgpd.conf b/tests/topotests/bgp_software_version/r1/bgpd.conf
new file mode 100644 (file)
index 0000000..80a05b3
--- /dev/null
@@ -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 (file)
index 0000000..b29940f
--- /dev/null
@@ -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 (file)
index 0000000..c8ab017
--- /dev/null
@@ -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 (file)
index 0000000..cffe827
--- /dev/null
@@ -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 (file)
index 0000000..55327b7
--- /dev/null
@@ -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))