mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-05-30 03:08:59 +00:00
Merge pull request #12727 from opensourcerouting/feature/bgp_software_version_capability
bgpd: Software Version Capability
This commit is contained in:
commit
a15b0b1024
@ -297,6 +297,15 @@ static struct peer *peer_xfer_conn(struct peer *from_peer)
|
|||||||
from_peer->domainname = NULL;
|
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) {
|
FOREACH_AFI_SAFI (afi, safi) {
|
||||||
peer->af_sflags[afi][safi] = from_peer->af_sflags[afi][safi];
|
peer->af_sflags[afi][safi] = from_peer->af_sflags[afi][safi];
|
||||||
peer->af_cap[afi][safi] = from_peer->af_cap[afi][safi];
|
peer->af_cap[afi][safi] = from_peer->af_cap[afi][safi];
|
||||||
|
@ -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, EVPN_REMOTE_IP, "BGP EVPN Remote IP hash entry");
|
||||||
|
|
||||||
DEFINE_MTYPE(BGPD, BGP_NOTIFICATION, "BGP Notification Message");
|
DEFINE_MTYPE(BGPD, BGP_NOTIFICATION, "BGP Notification Message");
|
||||||
|
|
||||||
|
DEFINE_MTYPE(BGPD, BGP_SOFT_VERSION, "Software Version");
|
||||||
|
@ -138,4 +138,6 @@ DECLARE_MTYPE(EVPN_REMOTE_IP);
|
|||||||
|
|
||||||
DECLARE_MTYPE(BGP_NOTIFICATION);
|
DECLARE_MTYPE(BGP_NOTIFICATION);
|
||||||
|
|
||||||
|
DECLARE_MTYPE(BGP_SOFT_VERSION);
|
||||||
|
|
||||||
#endif /* _QUAGGA_BGP_MEMORY_H */
|
#endif /* _QUAGGA_BGP_MEMORY_H */
|
||||||
|
@ -59,6 +59,7 @@ static const struct message capcode_str[] = {
|
|||||||
{CAPABILITY_CODE_EXT_MESSAGE, "BGP Extended Message"},
|
{CAPABILITY_CODE_EXT_MESSAGE, "BGP Extended Message"},
|
||||||
{CAPABILITY_CODE_LLGR, "Long-lived BGP Graceful Restart"},
|
{CAPABILITY_CODE_LLGR, "Long-lived BGP Graceful Restart"},
|
||||||
{CAPABILITY_CODE_ROLE, "Role"},
|
{CAPABILITY_CODE_ROLE, "Role"},
|
||||||
|
{CAPABILITY_CODE_SOFT_VERSION, "Software Version"},
|
||||||
{0}};
|
{0}};
|
||||||
|
|
||||||
/* Minimum sizes for length field of each cap (so not inc. the header) */
|
/* 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_EXT_MESSAGE] = CAPABILITY_CODE_EXT_MESSAGE_LEN,
|
||||||
[CAPABILITY_CODE_LLGR] = CAPABILITY_CODE_LLGR_LEN,
|
[CAPABILITY_CODE_LLGR] = CAPABILITY_CODE_LLGR_LEN,
|
||||||
[CAPABILITY_CODE_ROLE] = CAPABILITY_CODE_ROLE_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.
|
/* 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_EXT_MESSAGE] = 1,
|
||||||
[CAPABILITY_CODE_LLGR] = 1,
|
[CAPABILITY_CODE_LLGR] = 1,
|
||||||
[CAPABILITY_CODE_ROLE] = 1,
|
[CAPABILITY_CODE_ROLE] = 1,
|
||||||
|
[CAPABILITY_CODE_SOFT_VERSION] = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* BGP-4 Multiprotocol Extentions lead us to the complex world. We can
|
/* 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;
|
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.
|
* Parse given capability.
|
||||||
* XXX: This is reading into a stream, but not using stream API
|
* 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_ENHANCED_RR:
|
||||||
case CAPABILITY_CODE_EXT_MESSAGE:
|
case CAPABILITY_CODE_EXT_MESSAGE:
|
||||||
case CAPABILITY_CODE_ROLE:
|
case CAPABILITY_CODE_ROLE:
|
||||||
|
case CAPABILITY_CODE_SOFT_VERSION:
|
||||||
/* Check length. */
|
/* Check length. */
|
||||||
if (caphdr.length < cap_minsizes[caphdr.code]) {
|
if (caphdr.length < cap_minsizes[caphdr.code]) {
|
||||||
zlog_info(
|
zlog_info(
|
||||||
@ -1089,6 +1128,9 @@ static int bgp_capability_parse(struct peer *peer, size_t length,
|
|||||||
case CAPABILITY_CODE_ROLE:
|
case CAPABILITY_CODE_ROLE:
|
||||||
ret = bgp_capability_role(peer, &caphdr);
|
ret = bgp_capability_role(peer, &caphdr);
|
||||||
break;
|
break;
|
||||||
|
case CAPABILITY_CODE_SOFT_VERSION:
|
||||||
|
ret = bgp_capability_software_version(peer, &caphdr);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
if (caphdr.code > 128) {
|
if (caphdr.code > 128) {
|
||||||
/* We don't send Notification for unknown vendor
|
/* 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_gr_capability(s, peer, ext_opt_params);
|
||||||
bgp_peer_send_llgr_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. */
|
/* Total Opt Parm Len. */
|
||||||
len = stream_get_endp(s) - cp - 1;
|
len = stream_get_endp(s) - cp - 1;
|
||||||
|
|
||||||
|
@ -52,6 +52,7 @@ struct graceful_restart_af {
|
|||||||
#define CAPABILITY_CODE_ENHANCED_RR 70 /* Enhanced Route Refresh capability */
|
#define CAPABILITY_CODE_ENHANCED_RR 70 /* Enhanced Route Refresh capability */
|
||||||
#define CAPABILITY_CODE_LLGR 71 /* Long-lived Graceful Restart */
|
#define CAPABILITY_CODE_LLGR 71 /* Long-lived Graceful Restart */
|
||||||
#define CAPABILITY_CODE_FQDN 73 /* Advertise hostname capability */
|
#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_ENHE 5 /* Extended Next Hop Encoding */
|
||||||
#define CAPABILITY_CODE_REFRESH_OLD 128 /* Route Refresh Capability(cisco) */
|
#define CAPABILITY_CODE_REFRESH_OLD 128 /* Route Refresh Capability(cisco) */
|
||||||
#define CAPABILITY_CODE_ORF_OLD 130 /* Cooperative Route Filtering 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_ORF_LEN 5
|
||||||
#define CAPABILITY_CODE_EXT_MESSAGE_LEN 0 /* Extended Message Support */
|
#define CAPABILITY_CODE_EXT_MESSAGE_LEN 0 /* Extended Message Support */
|
||||||
#define CAPABILITY_CODE_ROLE_LEN 1
|
#define CAPABILITY_CODE_ROLE_LEN 1
|
||||||
|
#define CAPABILITY_CODE_SOFT_VERSION_LEN 1
|
||||||
|
|
||||||
/* Cooperative Route Filtering Capability. */
|
/* Cooperative Route Filtering Capability. */
|
||||||
|
|
||||||
|
@ -1685,6 +1685,13 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size)
|
|||||||
peer->v_keepalive = peer->bgp->default_keepalive;
|
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. */
|
/* Open option part parse. */
|
||||||
if (optlen != 0) {
|
if (optlen != 0) {
|
||||||
if (bgp_open_option_parse(peer, optlen, &mp_capability) < 0)
|
if (bgp_open_option_parse(peer, optlen, &mp_capability) < 0)
|
||||||
|
@ -5575,6 +5575,30 @@ DEFUN (no_neighbor_capability_enhe,
|
|||||||
PEER_FLAG_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,
|
static int peer_af_flag_modify_vty(struct vty *vty, const char *peer_str,
|
||||||
afi_t afi, safi_t safi, uint32_t flag,
|
afi_t afi, safi_t safi, uint32_t flag,
|
||||||
int set)
|
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]);
|
peer_down_str[(int)peer->last_reset]);
|
||||||
json_object_int_add(json_peer, "lastResetCode",
|
json_object_int_add(json_peer, "lastResetCode",
|
||||||
peer->last_reset);
|
peer->last_reset);
|
||||||
|
json_object_string_add(json_peer, "softwareVersion",
|
||||||
|
peer->soft_version ? peer->soft_version
|
||||||
|
: "n/a");
|
||||||
} else {
|
} else {
|
||||||
if (peer->last_reset == PEER_DOWN_NOTIFY_SEND
|
if (peer->last_reset == PEER_DOWN_NOTIFY_SEND
|
||||||
|| peer->last_reset == PEER_DOWN_NOTIFY_RECEIVED) {
|
|| 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)
|
BGP_NOTIFY_CEASE_HARD_RESET)
|
||||||
: "");
|
: "");
|
||||||
} else {
|
} else {
|
||||||
vty_out(vty, " %s\n",
|
vty_out(vty, " %s (%s)\n",
|
||||||
peer_down_str[(int)peer->last_reset]);
|
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_object_object_add(json_cap, "hostName",
|
||||||
json_hname);
|
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 */
|
/* Graceful Restart */
|
||||||
if (CHECK_FLAG(p->cap, PEER_CAP_RESTART_RCV) ||
|
if (CHECK_FLAG(p->cap, PEER_CAP_RESTART_RCV) ||
|
||||||
CHECK_FLAG(p->cap, PEER_CAP_RESTART_ADV)) {
|
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");
|
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 */
|
/* Graceful Restart */
|
||||||
if (CHECK_FLAG(p->cap, PEER_CAP_RESTART_RCV) ||
|
if (CHECK_FLAG(p->cap, PEER_CAP_RESTART_RCV) ||
|
||||||
CHECK_FLAG(p->cap, PEER_CAP_RESTART_ADV)) {
|
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 */
|
/* 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 (!peer_group_active(peer)) {
|
||||||
if (CHECK_FLAG(peer->flags_invert, flag))
|
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);
|
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 */
|
/* dont-capability-negotiation */
|
||||||
if (peergroup_flag_check(peer, PEER_FLAG_DONT_CAPABILITY))
|
if (peergroup_flag_check(peer, PEER_FLAG_DONT_CAPABILITY))
|
||||||
vty_out(vty, " neighbor %s dont-capability-negotiate\n", addr);
|
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, &neighbor_capability_enhe_cmd);
|
||||||
install_element(BGP_NODE, &no_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.*/
|
/* "neighbor capability orf prefix-list" commands.*/
|
||||||
install_element(BGP_NODE, &neighbor_capability_orf_prefix_hidden_cmd);
|
install_element(BGP_NODE, &neighbor_capability_orf_prefix_hidden_cmd);
|
||||||
install_element(BGP_NODE,
|
install_element(BGP_NODE,
|
||||||
|
@ -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,
|
extern int bgp_show_summary_vty(struct vty *vty, const char *name, afi_t afi,
|
||||||
safi_t safi, const char *neighbor, int as_type,
|
safi_t safi, const char *neighbor, int as_type,
|
||||||
as_t as, uint16_t show_flags);
|
as_t as, uint16_t show_flags);
|
||||||
|
extern bool peergroup_flag_check(struct peer *peer, uint64_t flag);
|
||||||
|
|
||||||
#endif /* _QUAGGA_BGP_VTY_H */
|
#endif /* _QUAGGA_BGP_VTY_H */
|
||||||
|
11
bgpd/bgpd.c
11
bgpd/bgpd.c
@ -1178,6 +1178,8 @@ static void peer_free(struct peer *peer)
|
|||||||
|
|
||||||
XFREE(MTYPE_PEER_CONF_IF, peer->conf_if);
|
XFREE(MTYPE_PEER_CONF_IF, peer->conf_if);
|
||||||
|
|
||||||
|
XFREE(MTYPE_BGP_SOFT_VERSION, peer->soft_version);
|
||||||
|
|
||||||
/* Remove BFD configuration. */
|
/* Remove BFD configuration. */
|
||||||
if (peer->bfd_config)
|
if (peer->bfd_config)
|
||||||
bgp_peer_remove_bfd_config(peer);
|
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->hostname);
|
||||||
XFREE(MTYPE_BGP_PEER_HOST, peer->domainname);
|
XFREE(MTYPE_BGP_PEER_HOST, peer->domainname);
|
||||||
|
XFREE(MTYPE_BGP_SOFT_VERSION, peer->soft_version);
|
||||||
|
|
||||||
peer_unlock(peer); /* initial reference */
|
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))
|
if (CHECK_FLAG(conf->flags, PEER_FLAG_CAPABILITY_ENHE))
|
||||||
SET_FLAG(peer->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 */
|
/* password apply */
|
||||||
if (!CHECK_FLAG(peer->flags_override, PEER_FLAG_PASSWORD))
|
if (!CHECK_FLAG(peer->flags_override, PEER_FLAG_PASSWORD))
|
||||||
PEER_STR_ATTR_INHERIT(peer, group, 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_PORT, 0, peer_change_reset},
|
||||||
{PEER_FLAG_AIGP, 0, peer_change_none},
|
{PEER_FLAG_AIGP, 0, peer_change_none},
|
||||||
{PEER_FLAG_GRACEFUL_SHUTDOWN, 0, peer_change_none},
|
{PEER_FLAG_GRACEFUL_SHUTDOWN, 0, peer_change_none},
|
||||||
|
{PEER_FLAG_CAPABILITY_SOFT_VERSION, 0, peer_change_reset},
|
||||||
{0, 0, 0}};
|
{0, 0, 0}};
|
||||||
|
|
||||||
static const struct peer_flag_action peer_af_flag_action_list[] = {
|
static const struct peer_flag_action peer_af_flag_action_list[] = {
|
||||||
|
@ -1250,6 +1250,8 @@ struct peer {
|
|||||||
#define PEER_CAP_GRACEFUL_RESTART_N_BIT_RCV (1U << 24)
|
#define PEER_CAP_GRACEFUL_RESTART_N_BIT_RCV (1U << 24)
|
||||||
#define PEER_CAP_ROLE_ADV (1U << 25) /* role advertised */
|
#define PEER_CAP_ROLE_ADV (1U << 25) /* role advertised */
|
||||||
#define PEER_CAP_ROLE_RCV (1U << 26) /* role received */
|
#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) */
|
/* Capability flags (reset in bgp_stop) */
|
||||||
uint32_t af_cap[AFI_MAX][SAFI_MAX];
|
uint32_t af_cap[AFI_MAX][SAFI_MAX];
|
||||||
@ -1376,6 +1378,7 @@ struct peer {
|
|||||||
#define PEER_FLAG_PORT (1ULL << 33)
|
#define PEER_FLAG_PORT (1ULL << 33)
|
||||||
#define PEER_FLAG_AIGP (1ULL << 34)
|
#define PEER_FLAG_AIGP (1ULL << 34)
|
||||||
#define PEER_FLAG_GRACEFUL_SHUTDOWN (1ULL << 35)
|
#define PEER_FLAG_GRACEFUL_SHUTDOWN (1ULL << 35)
|
||||||
|
#define PEER_FLAG_CAPABILITY_SOFT_VERSION (1ULL << 36)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*GR-Disabled mode means unset PEER_FLAG_GRACEFUL_RESTART
|
*GR-Disabled mode means unset PEER_FLAG_GRACEFUL_RESTART
|
||||||
@ -1771,6 +1774,10 @@ struct peer {
|
|||||||
/* Path attributes treat-as-withdraw */
|
/* Path attributes treat-as-withdraw */
|
||||||
bool withdraw_attrs[BGP_ATTR_MAX];
|
bool withdraw_attrs[BGP_ATTR_MAX];
|
||||||
|
|
||||||
|
/* BGP Software Version Capability */
|
||||||
|
#define BGP_MAX_SOFT_VERSION 64
|
||||||
|
char *soft_version;
|
||||||
|
|
||||||
QOBJ_FIELDS;
|
QOBJ_FIELDS;
|
||||||
};
|
};
|
||||||
DECLARE_QOBJ_TYPE(peer);
|
DECLARE_QOBJ_TYPE(peer);
|
||||||
|
@ -2020,10 +2020,17 @@ Capability Negotiation
|
|||||||
|
|
||||||
.. clicmd:: neighbor PEER override-capability
|
.. clicmd:: neighbor PEER override-capability
|
||||||
|
|
||||||
|
|
||||||
Override the result of Capability Negotiation with local configuration.
|
Override the result of Capability Negotiation with local configuration.
|
||||||
Ignore remote peer's capability value.
|
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:
|
.. _bgp-as-path-access-lists:
|
||||||
|
|
||||||
AS Path Access Lists
|
AS Path Access Lists
|
||||||
|
@ -127,6 +127,11 @@ bool cmd_allow_reserved_ranges_get(void)
|
|||||||
return host.allow_reserved_ranges;
|
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);
|
static int root_on_exit(struct vty *vty);
|
||||||
|
|
||||||
/* Standard command node structures. */
|
/* Standard command node structures. */
|
||||||
|
@ -606,6 +606,7 @@ extern const char *cmd_domainname_get(void);
|
|||||||
extern const char *cmd_system_get(void);
|
extern const char *cmd_system_get(void);
|
||||||
extern const char *cmd_release_get(void);
|
extern const char *cmd_release_get(void);
|
||||||
extern const char *cmd_version_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);
|
extern bool cmd_allow_reserved_ranges_get(void);
|
||||||
|
|
||||||
/* NOT safe for general use; call this only if DEV_BUILD! */
|
/* NOT safe for general use; call this only if DEV_BUILD! */
|
||||||
|
@ -176,9 +176,9 @@ struct test_peer_attr {
|
|||||||
|
|
||||||
enum test_peer_attr_type type;
|
enum test_peer_attr_type type;
|
||||||
union {
|
union {
|
||||||
uint32_t flag;
|
uint64_t flag;
|
||||||
struct {
|
struct {
|
||||||
uint32_t flag;
|
uint64_t flag;
|
||||||
size_t direct;
|
size_t direct;
|
||||||
} filter;
|
} filter;
|
||||||
} u;
|
} u;
|
||||||
@ -281,6 +281,18 @@ static struct test_peer_attr test_peer_attrs[] = {
|
|||||||
.o.invert_peer = true,
|
.o.invert_peer = true,
|
||||||
.o.use_iface_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",
|
.cmd = "description",
|
||||||
.peer_cmd = "description FRR Peer",
|
.peer_cmd = "description FRR Peer",
|
||||||
|
0
tests/topotests/bgp_software_version/__init__.py
Normal file
0
tests/topotests/bgp_software_version/__init__.py
Normal file
8
tests/topotests/bgp_software_version/r1/bgpd.conf
Normal file
8
tests/topotests/bgp_software_version/r1/bgpd.conf
Normal file
@ -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
|
||||||
|
!
|
4
tests/topotests/bgp_software_version/r1/zebra.conf
Normal file
4
tests/topotests/bgp_software_version/r1/zebra.conf
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
!
|
||||||
|
int r1-eth0
|
||||||
|
ip address 192.168.1.1/24
|
||||||
|
!
|
7
tests/topotests/bgp_software_version/r2/bgpd.conf
Normal file
7
tests/topotests/bgp_software_version/r2/bgpd.conf
Normal file
@ -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
|
||||||
|
!
|
4
tests/topotests/bgp_software_version/r2/zebra.conf
Normal file
4
tests/topotests/bgp_software_version/r2/zebra.conf
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
!
|
||||||
|
int r2-eth0
|
||||||
|
ip address 192.168.1.2/24
|
||||||
|
!
|
@ -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))
|
Loading…
Reference in New Issue
Block a user