summaryrefslogtreecommitdiffstats
path: root/bgpd/bgp_open.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-09 13:16:35 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-09 13:16:35 +0000
commite2bbf175a2184bd76f6c54ccf8456babeb1a46fc (patch)
treef0b76550d6e6f500ada964a3a4ee933a45e5a6f1 /bgpd/bgp_open.c
parentInitial commit. (diff)
downloadfrr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.tar.xz
frr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.zip
Adding upstream version 9.1.upstream/9.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'bgpd/bgp_open.c')
-rw-r--r--bgpd/bgp_open.c1979
1 files changed, 1979 insertions, 0 deletions
diff --git a/bgpd/bgp_open.c b/bgpd/bgp_open.c
new file mode 100644
index 0000000..b030c45
--- /dev/null
+++ b/bgpd/bgp_open.c
@@ -0,0 +1,1979 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* BGP open message handling
+ * Copyright (C) 1998, 1999 Kunihiro Ishiguro
+ */
+
+#include <zebra.h>
+
+#include "linklist.h"
+#include "prefix.h"
+#include "stream.h"
+#include "frrevent.h"
+#include "log.h"
+#include "command.h"
+#include "memory.h"
+#include "queue.h"
+#include "filter.h"
+
+#include "lib/json.h"
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_errors.h"
+#include "bgpd/bgp_fsm.h"
+#include "bgpd/bgp_packet.h"
+#include "bgpd/bgp_open.h"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_vty.h"
+#include "bgpd/bgp_memory.h"
+
+const struct message capcode_str[] = {
+ { CAPABILITY_CODE_MP, "MultiProtocol Extensions" },
+ { CAPABILITY_CODE_REFRESH, "Route Refresh" },
+ { CAPABILITY_CODE_ORF, "Cooperative Route Filtering" },
+ { CAPABILITY_CODE_RESTART, "Graceful Restart" },
+ { CAPABILITY_CODE_AS4, "4-octet AS number" },
+ { CAPABILITY_CODE_ADDPATH, "AddPath" },
+ { CAPABILITY_CODE_DYNAMIC, "Dynamic" },
+ { CAPABILITY_CODE_ENHE, "Extended Next Hop Encoding" },
+ { CAPABILITY_CODE_FQDN, "FQDN" },
+ { CAPABILITY_CODE_ENHANCED_RR, "Enhanced Route Refresh" },
+ { 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) */
+static const size_t cap_minsizes[] = {
+ [CAPABILITY_CODE_MP] = CAPABILITY_CODE_MP_LEN,
+ [CAPABILITY_CODE_REFRESH] = CAPABILITY_CODE_REFRESH_LEN,
+ [CAPABILITY_CODE_ORF] = CAPABILITY_CODE_ORF_LEN,
+ [CAPABILITY_CODE_RESTART] = CAPABILITY_CODE_RESTART_LEN,
+ [CAPABILITY_CODE_AS4] = CAPABILITY_CODE_AS4_LEN,
+ [CAPABILITY_CODE_ADDPATH] = CAPABILITY_CODE_ADDPATH_LEN,
+ [CAPABILITY_CODE_DYNAMIC] = CAPABILITY_CODE_DYNAMIC_LEN,
+ [CAPABILITY_CODE_ENHE] = CAPABILITY_CODE_ENHE_LEN,
+ [CAPABILITY_CODE_FQDN] = CAPABILITY_CODE_MIN_FQDN_LEN,
+ [CAPABILITY_CODE_ENHANCED_RR] = CAPABILITY_CODE_ENHANCED_LEN,
+ [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.
+ * 0-data capabilities won't be checked against this.
+ * Other capabilities whose data doesn't fall on convenient boundaries for this
+ * table should be set to 1.
+ */
+static const size_t cap_modsizes[] = {
+ [CAPABILITY_CODE_MP] = 4,
+ [CAPABILITY_CODE_REFRESH] = 1,
+ [CAPABILITY_CODE_ORF] = 1,
+ [CAPABILITY_CODE_RESTART] = 1,
+ [CAPABILITY_CODE_AS4] = 4,
+ [CAPABILITY_CODE_ADDPATH] = 4,
+ [CAPABILITY_CODE_DYNAMIC] = 1,
+ [CAPABILITY_CODE_ENHE] = 6,
+ [CAPABILITY_CODE_FQDN] = 1,
+ [CAPABILITY_CODE_ENHANCED_RR] = 1,
+ [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
+ negotiate remote peer supports extentions or not. But if
+ remote-peer doesn't supports negotiation process itself. We would
+ like to do manual configuration.
+
+ So there is many configurable point. First of all we want set each
+ peer whether we send capability negotiation to the peer or not.
+ Next, if we send capability to the peer we want to set my capability
+ inforation at each peer. */
+
+void bgp_capability_vty_out(struct vty *vty, struct peer *peer, bool use_json,
+ json_object *json_neigh)
+{
+ char *pnt;
+ char *end;
+ struct capability_mp_data mpc;
+ struct capability_header *hdr;
+ json_object *json_cap = NULL;
+
+ if (use_json)
+ json_cap = json_object_new_object();
+
+ pnt = peer->notify.data;
+ end = pnt + peer->notify.length;
+
+ while (pnt < end) {
+ if (pnt + sizeof(struct capability_mp_data) + 2 > end)
+ return;
+
+ hdr = (struct capability_header *)pnt;
+ if (pnt + hdr->length + 2 > end)
+ return;
+
+ memcpy(&mpc, pnt + 2, sizeof(struct capability_mp_data));
+
+ if (hdr->code == CAPABILITY_CODE_MP) {
+ afi_t afi;
+ safi_t safi;
+
+ (void)bgp_map_afi_safi_iana2int(ntohs(mpc.afi),
+ mpc.safi, &afi, &safi);
+
+ if (use_json) {
+ switch (afi) {
+ case AFI_IP:
+ json_object_string_add(
+ json_cap,
+ "capabilityErrorMultiProtocolAfi",
+ "IPv4");
+ break;
+ case AFI_IP6:
+ json_object_string_add(
+ json_cap,
+ "capabilityErrorMultiProtocolAfi",
+ "IPv6");
+ break;
+ case AFI_L2VPN:
+ json_object_string_add(
+ json_cap,
+ "capabilityErrorMultiProtocolAfi",
+ "L2VPN");
+ break;
+ case AFI_UNSPEC:
+ case AFI_MAX:
+ json_object_int_add(
+ json_cap,
+ "capabilityErrorMultiProtocolAfiUnknown",
+ ntohs(mpc.afi));
+ break;
+ }
+ switch (safi) {
+ case SAFI_UNICAST:
+ json_object_string_add(
+ json_cap,
+ "capabilityErrorMultiProtocolSafi",
+ "unicast");
+ break;
+ case SAFI_MULTICAST:
+ json_object_string_add(
+ json_cap,
+ "capabilityErrorMultiProtocolSafi",
+ "multicast");
+ break;
+ case SAFI_LABELED_UNICAST:
+ json_object_string_add(
+ json_cap,
+ "capabilityErrorMultiProtocolSafi",
+ "labeled-unicast");
+ break;
+ case SAFI_MPLS_VPN:
+ json_object_string_add(
+ json_cap,
+ "capabilityErrorMultiProtocolSafi",
+ "MPLS-labeled VPN");
+ break;
+ case SAFI_ENCAP:
+ json_object_string_add(
+ json_cap,
+ "capabilityErrorMultiProtocolSafi",
+ "encap");
+ break;
+ case SAFI_EVPN:
+ json_object_string_add(
+ json_cap,
+ "capabilityErrorMultiProtocolSafi",
+ "EVPN");
+ break;
+ case SAFI_FLOWSPEC:
+ json_object_string_add(
+ json_cap,
+ "capabilityErrorMultiProtocolSafi",
+ "flowspec");
+ break;
+ case SAFI_UNSPEC:
+ case SAFI_MAX:
+ json_object_int_add(
+ json_cap,
+ "capabilityErrorMultiProtocolSafiUnknown",
+ mpc.safi);
+ break;
+ }
+ } else {
+ vty_out(vty,
+ " Capability error for: Multi protocol ");
+ switch (afi) {
+ case AFI_IP:
+ vty_out(vty, "AFI IPv4, ");
+ break;
+ case AFI_IP6:
+ vty_out(vty, "AFI IPv6, ");
+ break;
+ case AFI_L2VPN:
+ vty_out(vty, "AFI L2VPN, ");
+ break;
+ case AFI_UNSPEC:
+ case AFI_MAX:
+ vty_out(vty, "AFI Unknown %d, ",
+ ntohs(mpc.afi));
+ break;
+ }
+ switch (safi) {
+ case SAFI_UNICAST:
+ vty_out(vty, "SAFI Unicast");
+ break;
+ case SAFI_MULTICAST:
+ vty_out(vty, "SAFI Multicast");
+ break;
+ case SAFI_LABELED_UNICAST:
+ vty_out(vty, "SAFI Labeled-unicast");
+ break;
+ case SAFI_MPLS_VPN:
+ vty_out(vty, "SAFI MPLS-labeled VPN");
+ break;
+ case SAFI_ENCAP:
+ vty_out(vty, "SAFI ENCAP");
+ break;
+ case SAFI_FLOWSPEC:
+ vty_out(vty, "SAFI FLOWSPEC");
+ break;
+ case SAFI_EVPN:
+ vty_out(vty, "SAFI EVPN");
+ break;
+ case SAFI_UNSPEC:
+ case SAFI_MAX:
+ vty_out(vty, "SAFI Unknown %d ",
+ mpc.safi);
+ break;
+ }
+ vty_out(vty, "\n");
+ }
+ } else if (hdr->code >= 128) {
+ if (use_json)
+ json_object_int_add(
+ json_cap,
+ "capabilityErrorVendorSpecificCapabilityCode",
+ hdr->code);
+ else
+ vty_out(vty,
+ " Capability error: vendor specific capability code %d",
+ hdr->code);
+ } else {
+ if (use_json)
+ json_object_int_add(
+ json_cap,
+ "capabilityErrorUnknownCapabilityCode",
+ hdr->code);
+ else
+ vty_out(vty,
+ " Capability error: unknown capability code %d",
+ hdr->code);
+ }
+ pnt += hdr->length + 2;
+ }
+ if (use_json)
+ json_object_object_add(json_neigh, "capabilityErrors",
+ json_cap);
+}
+
+static void bgp_capability_mp_data(struct stream *s,
+ struct capability_mp_data *mpc)
+{
+ mpc->afi = stream_getw(s);
+ mpc->reserved = stream_getc(s);
+ mpc->safi = stream_getc(s);
+}
+
+/* Set negotiated capability value. */
+static int bgp_capability_mp(struct peer *peer, struct capability_header *hdr)
+{
+ struct capability_mp_data mpc;
+ struct stream *s = BGP_INPUT(peer);
+ afi_t afi;
+ safi_t safi;
+
+ /* Verify length is 4 */
+ if (hdr->length != 4) {
+ flog_warn(
+ EC_BGP_CAPABILITY_INVALID_LENGTH,
+ "MP Cap: Received invalid length %d, non-multiple of 4",
+ hdr->length);
+ return -1;
+ }
+
+ bgp_capability_mp_data(s, &mpc);
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s OPEN has %s capability for afi/safi: %s/%s",
+ peer->host, lookup_msg(capcode_str, hdr->code, NULL),
+ iana_afi2str(mpc.afi), iana_safi2str(mpc.safi));
+
+ /* Convert AFI, SAFI to internal values, check. */
+ if (bgp_map_afi_safi_iana2int(mpc.afi, mpc.safi, &afi, &safi))
+ return -1;
+
+ /* Now safi remapped, and afi/safi are valid array indices */
+ peer->afc_recv[afi][safi] = 1;
+
+ if (peer->afc[afi][safi])
+ peer->afc_nego[afi][safi] = 1;
+ else
+ return -1;
+
+ return 0;
+}
+
+static void bgp_capability_orf_not_support(struct peer *peer, iana_afi_t afi,
+ iana_safi_t safi, uint8_t type,
+ uint8_t mode)
+{
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s Addr-family %d/%d has ORF type/mode %d/%d not supported",
+ peer->host, afi, safi, type, mode);
+}
+
+static const struct message orf_type_str[] = {
+ {ORF_TYPE_RESERVED, "Reserved"},
+ {ORF_TYPE_PREFIX, "Prefixlist"},
+ {0}};
+
+static const struct message orf_mode_str[] = {{ORF_MODE_RECEIVE, "Receive"},
+ {ORF_MODE_SEND, "Send"},
+ {ORF_MODE_BOTH, "Both"},
+ {0}};
+
+static int bgp_capability_orf_entry(struct peer *peer,
+ struct capability_header *hdr)
+{
+ struct stream *s = BGP_INPUT(peer);
+ struct capability_mp_data mpc;
+ uint8_t num;
+ iana_afi_t pkt_afi;
+ afi_t afi;
+ iana_safi_t pkt_safi;
+ safi_t safi;
+ uint8_t type;
+ uint8_t mode;
+ uint16_t sm_cap = 0; /* capability send-mode receive */
+ uint16_t rm_cap = 0; /* capability receive-mode receive */
+ int i;
+
+ /* ORF Entry header */
+ bgp_capability_mp_data(s, &mpc);
+ num = stream_getc(s);
+ pkt_afi = mpc.afi;
+ pkt_safi = mpc.safi;
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s ORF Cap entry for afi/safi: %s/%s", peer->host,
+ iana_afi2str(mpc.afi), iana_safi2str(mpc.safi));
+
+ /* Convert AFI, SAFI to internal values, check. */
+ if (bgp_map_afi_safi_iana2int(pkt_afi, pkt_safi, &afi, &safi)) {
+ zlog_info(
+ "%s Addr-family %d/%d not supported. Ignoring the ORF capability",
+ peer->host, pkt_afi, pkt_safi);
+ return 0;
+ }
+
+ mpc.afi = pkt_afi;
+ mpc.safi = safi;
+
+ /* validate number field */
+ if (CAPABILITY_CODE_ORF_LEN + (num * 2) > hdr->length) {
+ zlog_info(
+ "%s ORF Capability entry length error, Cap length %u, num %u",
+ peer->host, hdr->length, num);
+ bgp_notify_send(peer->connection, BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_MALFORMED_ATTR);
+ return -1;
+ }
+
+ for (i = 0; i < num; i++) {
+ type = stream_getc(s);
+ mode = stream_getc(s);
+
+ /* ORF Mode error check */
+ switch (mode) {
+ case ORF_MODE_BOTH:
+ case ORF_MODE_SEND:
+ case ORF_MODE_RECEIVE:
+ break;
+ default:
+ bgp_capability_orf_not_support(peer, pkt_afi, pkt_safi,
+ type, mode);
+ continue;
+ }
+ /* ORF Type and afi/safi error checks */
+ /* capcode versus type */
+ switch (hdr->code) {
+ case CAPABILITY_CODE_ORF:
+ switch (type) {
+ case ORF_TYPE_RESERVED:
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s Addr-family %d/%d has reserved ORF type, ignoring",
+ peer->host, afi, safi);
+ break;
+ case ORF_TYPE_PREFIX:
+ break;
+ default:
+ bgp_capability_orf_not_support(
+ peer, pkt_afi, pkt_safi, type, mode);
+ continue;
+ }
+ break;
+ default:
+ bgp_capability_orf_not_support(peer, pkt_afi, pkt_safi,
+ type, mode);
+ continue;
+ }
+
+ /* AFI vs SAFI */
+ if (!((afi == AFI_IP && safi == SAFI_UNICAST)
+ || (afi == AFI_IP && safi == SAFI_MULTICAST)
+ || (afi == AFI_IP6 && safi == SAFI_UNICAST))) {
+ bgp_capability_orf_not_support(peer, pkt_afi, pkt_safi,
+ type, mode);
+ continue;
+ }
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s OPEN has %s ORF capability as %s for afi/safi: %s/%s",
+ peer->host,
+ lookup_msg(orf_type_str, type, NULL),
+ lookup_msg(orf_mode_str, mode, NULL),
+ iana_afi2str(pkt_afi), iana_safi2str(pkt_safi));
+
+ if (hdr->code == CAPABILITY_CODE_ORF) {
+ sm_cap = PEER_CAP_ORF_PREFIX_SM_RCV;
+ rm_cap = PEER_CAP_ORF_PREFIX_RM_RCV;
+ } else {
+ bgp_capability_orf_not_support(peer, pkt_afi, pkt_safi,
+ type, mode);
+ continue;
+ }
+
+ switch (mode) {
+ case ORF_MODE_BOTH:
+ SET_FLAG(peer->af_cap[afi][safi], sm_cap);
+ SET_FLAG(peer->af_cap[afi][safi], rm_cap);
+ break;
+ case ORF_MODE_SEND:
+ SET_FLAG(peer->af_cap[afi][safi], sm_cap);
+ break;
+ case ORF_MODE_RECEIVE:
+ SET_FLAG(peer->af_cap[afi][safi], rm_cap);
+ break;
+ }
+ }
+ return 0;
+}
+
+static int bgp_capability_restart(struct peer *peer,
+ struct capability_header *caphdr)
+{
+ struct stream *s = BGP_INPUT(peer);
+ uint16_t restart_flag_time;
+ size_t end = stream_get_getp(s) + caphdr->length;
+
+ /* Verify length is a multiple of 4 */
+ if ((caphdr->length - 2) % 4) {
+ flog_warn(
+ EC_BGP_CAPABILITY_INVALID_LENGTH,
+ "Restart Cap: Received invalid length %d, non-multiple of 4",
+ caphdr->length);
+ return -1;
+ }
+
+ SET_FLAG(peer->cap, PEER_CAP_RESTART_RCV);
+ restart_flag_time = stream_getw(s);
+
+ /* The most significant bit is defined in [RFC4724] as
+ * the Restart State ("R") bit.
+ */
+ if (CHECK_FLAG(restart_flag_time, GRACEFUL_RESTART_R_BIT))
+ SET_FLAG(peer->cap, PEER_CAP_GRACEFUL_RESTART_R_BIT_RCV);
+ else
+ UNSET_FLAG(peer->cap, PEER_CAP_GRACEFUL_RESTART_R_BIT_RCV);
+
+ /* The second most significant bit is defined in this
+ * document as the Graceful Notification ("N") bit.
+ */
+ if (CHECK_FLAG(restart_flag_time, GRACEFUL_RESTART_N_BIT))
+ SET_FLAG(peer->cap, PEER_CAP_GRACEFUL_RESTART_N_BIT_RCV);
+ else
+ UNSET_FLAG(peer->cap, PEER_CAP_GRACEFUL_RESTART_N_BIT_RCV);
+
+ UNSET_FLAG(restart_flag_time, 0xF000);
+ peer->v_gr_restart = restart_flag_time;
+
+ if (bgp_debug_neighbor_events(peer)) {
+ zlog_debug(
+ "%s Peer has%srestarted. Restart Time: %d, N-bit set: %s",
+ peer->host,
+ CHECK_FLAG(peer->cap,
+ PEER_CAP_GRACEFUL_RESTART_R_BIT_RCV)
+ ? " "
+ : " not ",
+ peer->v_gr_restart,
+ CHECK_FLAG(peer->cap,
+ PEER_CAP_GRACEFUL_RESTART_N_BIT_RCV)
+ ? "yes"
+ : "no");
+ }
+
+ while (stream_get_getp(s) + 4 <= end) {
+ afi_t afi;
+ safi_t safi;
+ iana_afi_t pkt_afi = stream_getw(s);
+ iana_safi_t pkt_safi = stream_getc(s);
+ uint8_t flag = stream_getc(s);
+
+ /* Convert AFI, SAFI to internal values, check. */
+ if (bgp_map_afi_safi_iana2int(pkt_afi, pkt_safi, &afi, &safi)) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s Addr-family %s/%s(afi/safi) not supported. Ignore the Graceful Restart capability for this AFI/SAFI",
+ peer->host, iana_afi2str(pkt_afi),
+ iana_safi2str(pkt_safi));
+ } else if (!peer->afc[afi][safi]) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s Addr-family %s/%s(afi/safi) not enabled. Ignore the Graceful Restart capability",
+ peer->host, iana_afi2str(pkt_afi),
+ iana_safi2str(pkt_safi));
+ } else {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s Address family %s is%spreserved",
+ peer->host, get_afi_safi_str(afi, safi, false),
+ CHECK_FLAG(
+ peer->af_cap[afi][safi],
+ PEER_CAP_RESTART_AF_PRESERVE_RCV)
+ ? " "
+ : " not ");
+
+ SET_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_RESTART_AF_RCV);
+ if (CHECK_FLAG(flag, GRACEFUL_RESTART_F_BIT))
+ SET_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_RESTART_AF_PRESERVE_RCV);
+ }
+ }
+ return 0;
+}
+
+static int bgp_capability_llgr(struct peer *peer,
+ struct capability_header *caphdr)
+{
+ struct stream *s = BGP_INPUT(peer);
+ size_t end = stream_get_getp(s) + caphdr->length;
+
+ SET_FLAG(peer->cap, PEER_CAP_LLGR_RCV);
+
+ while (stream_get_getp(s) + BGP_CAP_LLGR_MIN_PACKET_LEN <= end) {
+ afi_t afi;
+ safi_t safi;
+ iana_afi_t pkt_afi = stream_getw(s);
+ iana_safi_t pkt_safi = stream_getc(s);
+ uint8_t flags = stream_getc(s);
+ uint32_t stale_time = stream_get3(s);
+
+ if (bgp_map_afi_safi_iana2int(pkt_afi, pkt_safi, &afi, &safi)) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s Addr-family %s/%s(afi/safi) not supported. Ignore the Long-lived Graceful Restart capability for this AFI/SAFI",
+ peer->host, iana_afi2str(pkt_afi),
+ iana_safi2str(pkt_safi));
+ } else if (!peer->afc[afi][safi]
+ || !CHECK_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_RESTART_AF_RCV)) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s Addr-family %s/%s(afi/safi) not enabled. Ignore the Long-lived Graceful Restart capability",
+ peer->host, iana_afi2str(pkt_afi),
+ iana_safi2str(pkt_safi));
+ } else {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s Addr-family %s/%s(afi/safi) Long-lived Graceful Restart capability stale time %u sec",
+ peer->host, iana_afi2str(pkt_afi),
+ iana_safi2str(pkt_safi), stale_time);
+
+ peer->llgr[afi][safi].flags = flags;
+ peer->llgr[afi][safi].stale_time =
+ MIN(stale_time, peer->bgp->llgr_stale_time);
+ SET_FLAG(peer->af_cap[afi][safi], PEER_CAP_LLGR_AF_RCV);
+ }
+ }
+
+ return 0;
+}
+
+/* Unlike other capability parsing routines, this one returns 0 on error */
+static as_t bgp_capability_as4(struct peer *peer, struct capability_header *hdr)
+{
+ SET_FLAG(peer->cap, PEER_CAP_AS4_RCV);
+
+ if (hdr->length != CAPABILITY_CODE_AS4_LEN) {
+ flog_err(EC_BGP_PKT_OPEN,
+ "%s AS4 capability has incorrect data length %d",
+ peer->host, hdr->length);
+ return 0;
+ }
+
+ as_t as4 = stream_getl(BGP_INPUT(peer));
+
+ if (BGP_DEBUG(as4, AS4))
+ zlog_debug(
+ "%s [AS4] about to set cap PEER_CAP_AS4_RCV, got as4 %u",
+ peer->host, as4);
+ return as4;
+}
+
+static int bgp_capability_ext_message(struct peer *peer,
+ struct capability_header *hdr)
+{
+ if (hdr->length != CAPABILITY_CODE_EXT_MESSAGE_LEN) {
+ flog_err(
+ EC_BGP_PKT_OPEN,
+ "%s: BGP Extended Message capability has incorrect data length %d",
+ peer->host, hdr->length);
+ return -1;
+ }
+
+ SET_FLAG(peer->cap, PEER_CAP_EXTENDED_MESSAGE_RCV);
+
+ return 0;
+}
+
+static int bgp_capability_addpath(struct peer *peer,
+ struct capability_header *hdr)
+{
+ struct stream *s = BGP_INPUT(peer);
+ size_t end = stream_get_getp(s) + hdr->length;
+
+ SET_FLAG(peer->cap, PEER_CAP_ADDPATH_RCV);
+
+ /* Verify length is a multiple of 4 */
+ if (hdr->length % 4) {
+ flog_warn(
+ EC_BGP_CAPABILITY_INVALID_LENGTH,
+ "Add Path: Received invalid length %d, non-multiple of 4",
+ hdr->length);
+ return -1;
+ }
+
+ while (stream_get_getp(s) + 4 <= end) {
+ afi_t afi;
+ safi_t safi;
+ iana_afi_t pkt_afi = stream_getw(s);
+ iana_safi_t pkt_safi = stream_getc(s);
+ uint8_t send_receive = stream_getc(s);
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s OPEN has %s capability for afi/safi: %s/%s%s%s",
+ peer->host,
+ lookup_msg(capcode_str, hdr->code, NULL),
+ iana_afi2str(pkt_afi), iana_safi2str(pkt_safi),
+ (send_receive & BGP_ADDPATH_RX) ? ", receive"
+ : "",
+ (send_receive & BGP_ADDPATH_TX) ? ", transmit"
+ : "");
+
+ /* Convert AFI, SAFI to internal values, check. */
+ if (bgp_map_afi_safi_iana2int(pkt_afi, pkt_safi, &afi, &safi)) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s Addr-family %s/%s(afi/safi) not supported. Ignore the Addpath Attribute for this AFI/SAFI",
+ peer->host, iana_afi2str(pkt_afi),
+ iana_safi2str(pkt_safi));
+ continue;
+ } else if (!peer->afc[afi][safi]) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s Addr-family %s/%s(afi/safi) not enabled. Ignore the AddPath capability for this AFI/SAFI",
+ peer->host, iana_afi2str(pkt_afi),
+ iana_safi2str(pkt_safi));
+ continue;
+ }
+
+ if (send_receive & BGP_ADDPATH_RX)
+ SET_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ADDPATH_AF_RX_RCV);
+
+ if (send_receive & BGP_ADDPATH_TX)
+ SET_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ADDPATH_AF_TX_RCV);
+ }
+
+ return 0;
+}
+
+static int bgp_capability_enhe(struct peer *peer, struct capability_header *hdr)
+{
+ struct stream *s = BGP_INPUT(peer);
+ size_t end = stream_get_getp(s) + hdr->length;
+
+ /* Verify length is a multiple of 4 */
+ if (hdr->length % 6) {
+ flog_warn(
+ EC_BGP_CAPABILITY_INVALID_LENGTH,
+ "Extended NH: Received invalid length %d, non-multiple of 6",
+ hdr->length);
+ return -1;
+ }
+
+ while (stream_get_getp(s) + 6 <= end) {
+ iana_afi_t pkt_afi = stream_getw(s);
+ afi_t afi;
+ iana_safi_t pkt_safi = stream_getw(s);
+ safi_t safi;
+ iana_afi_t pkt_nh_afi = stream_getw(s);
+ afi_t nh_afi;
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s Received with afi/safi/next-hop afi: %s/%s/%u",
+ peer->host, iana_afi2str(pkt_afi),
+ iana_safi2str(pkt_safi), pkt_nh_afi);
+
+ /* Convert AFI, SAFI to internal values, check. */
+ if (bgp_map_afi_safi_iana2int(pkt_afi, pkt_safi, &afi, &safi)) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s Addr-family %s/%s(afi/safi) not supported. Ignore the ENHE Attribute for this AFI/SAFI",
+ peer->host, iana_afi2str(pkt_afi),
+ iana_safi2str(pkt_safi));
+ continue;
+ }
+
+ /* RFC 5549 specifies use of this capability only for IPv4 AFI,
+ * with
+ * the Nexthop AFI being IPv6. A future spec may introduce other
+ * possibilities, so we ignore other values with a log. Also,
+ * only
+ * SAFI_UNICAST and SAFI_LABELED_UNICAST are currently supported
+ * (and expected).
+ */
+ nh_afi = afi_iana2int(pkt_nh_afi);
+
+ if (afi != AFI_IP || nh_afi != AFI_IP6
+ || !(safi == SAFI_UNICAST || safi == SAFI_MPLS_VPN
+ || safi == SAFI_LABELED_UNICAST)) {
+ flog_warn(
+ EC_BGP_CAPABILITY_INVALID_DATA,
+ "%s Unexpected afi/safi/next-hop afi: %s/%s/%u in Extended Next-hop capability, ignoring",
+ peer->host, iana_afi2str(pkt_afi),
+ iana_safi2str(pkt_safi), pkt_nh_afi);
+ continue;
+ }
+
+ SET_FLAG(peer->af_cap[afi][safi], PEER_CAP_ENHE_AF_RCV);
+
+ if (CHECK_FLAG(peer->af_cap[afi][safi], PEER_CAP_ENHE_AF_ADV))
+ SET_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ENHE_AF_NEGO);
+ }
+
+ SET_FLAG(peer->cap, PEER_CAP_ENHE_RCV);
+
+ return 0;
+}
+
+static int bgp_capability_hostname(struct peer *peer,
+ struct capability_header *hdr)
+{
+ struct stream *s = BGP_INPUT(peer);
+ char str[BGP_MAX_HOSTNAME + 1];
+ size_t end = stream_get_getp(s) + hdr->length;
+ uint8_t len;
+
+ SET_FLAG(peer->cap, PEER_CAP_HOSTNAME_RCV);
+
+ len = stream_getc(s);
+ if (stream_get_getp(s) + len > end) {
+ flog_warn(
+ EC_BGP_CAPABILITY_INVALID_DATA,
+ "%s: Received malformed hostname capability from peer %s",
+ __func__, peer->host);
+ return -1;
+ }
+
+ if (len > BGP_MAX_HOSTNAME) {
+ stream_get(str, s, BGP_MAX_HOSTNAME);
+ stream_forward_getp(s, len - BGP_MAX_HOSTNAME);
+ len = BGP_MAX_HOSTNAME; /* to set the '\0' below */
+ } else if (len)
+ stream_get(str, s, len);
+
+ if (len) {
+ str[len] = '\0';
+
+ XFREE(MTYPE_BGP_PEER_HOST, peer->hostname);
+ XFREE(MTYPE_BGP_PEER_HOST, peer->domainname);
+
+ peer->hostname = XSTRDUP(MTYPE_BGP_PEER_HOST, str);
+ }
+
+ if (stream_get_getp(s) + 1 > end) {
+ flog_warn(
+ EC_BGP_CAPABILITY_INVALID_DATA,
+ "%s: Received invalid domain name len (hostname capability) from peer %s",
+ __func__, peer->host);
+ return -1;
+ }
+
+ len = stream_getc(s);
+ if (stream_get_getp(s) + len > end) {
+ flog_warn(
+ EC_BGP_CAPABILITY_INVALID_DATA,
+ "%s: Received runt domain name (hostname capability) from peer %s",
+ __func__, peer->host);
+ return -1;
+ }
+
+ if (len > BGP_MAX_HOSTNAME) {
+ stream_get(str, s, BGP_MAX_HOSTNAME);
+ stream_forward_getp(s, len - BGP_MAX_HOSTNAME);
+ len = BGP_MAX_HOSTNAME; /* to set the '\0' below */
+ } else if (len)
+ stream_get(str, s, len);
+
+ if (len) {
+ str[len] = '\0';
+
+ XFREE(MTYPE_BGP_PEER_HOST, peer->domainname);
+
+ peer->domainname = XSTRDUP(MTYPE_BGP_PEER_HOST, str);
+ }
+
+ if (bgp_debug_neighbor_events(peer)) {
+ zlog_debug("%s received hostname %s, domainname %s", peer->host,
+ peer->hostname, peer->domainname);
+ }
+
+ return 0;
+}
+
+static int bgp_capability_role(struct peer *peer, struct capability_header *hdr)
+{
+ SET_FLAG(peer->cap, PEER_CAP_ROLE_RCV);
+ if (hdr->length != CAPABILITY_CODE_ROLE_LEN) {
+ flog_warn(EC_BGP_CAPABILITY_INVALID_LENGTH,
+ "Role: Received invalid length %d", hdr->length);
+ return -1;
+ }
+ uint8_t role = stream_getc(BGP_INPUT(peer));
+
+ peer->remote_role = role;
+ 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;
+
+ 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;
+ }
+
+ SET_FLAG(peer->cap, PEER_CAP_SOFT_VERSION_RCV);
+
+ if (len > BGP_MAX_SOFT_VERSION) {
+ flog_warn(EC_BGP_CAPABILITY_INVALID_LENGTH,
+ "%s: Received Software Version, but the length is too big, truncating, from peer %s",
+ __func__, peer->host);
+ stream_get(str, s, BGP_MAX_SOFT_VERSION);
+ stream_forward_getp(s, len - BGP_MAX_SOFT_VERSION);
+ len = BGP_MAX_SOFT_VERSION;
+ } else if (len) {
+ stream_get(str, s, len);
+ }
+
+ if (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
+ *
+ * @param[out] mp_capability Set to 1 on return iff one or more Multiprotocol
+ * capabilities were encountered.
+ */
+static int bgp_capability_parse(struct peer *peer, size_t length,
+ int *mp_capability, uint8_t **error)
+{
+ int ret;
+ struct stream *s = BGP_INPUT(peer);
+ size_t end = stream_get_getp(s) + length;
+ uint16_t restart_flag_time = 0;
+
+ assert(STREAM_READABLE(s) >= length);
+
+ while (stream_get_getp(s) < end) {
+ size_t start;
+ uint8_t *sp = stream_pnt(s);
+ struct capability_header caphdr;
+
+ ret = 0;
+ /* We need at least capability code and capability length. */
+ if (stream_get_getp(s) + 2 > end) {
+ zlog_info("%s Capability length error (< header)",
+ peer->host);
+ bgp_notify_send(peer->connection, BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_MALFORMED_ATTR);
+ return -1;
+ }
+
+ caphdr.code = stream_getc(s);
+ caphdr.length = stream_getc(s);
+ start = stream_get_getp(s);
+
+ /* Capability length check sanity check. */
+ if (start + caphdr.length > end) {
+ zlog_info("%s Capability length error (< length)",
+ peer->host);
+ bgp_notify_send(peer->connection, BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_MALFORMED_ATTR);
+ return -1;
+ }
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s OPEN has %s capability (%u), length %u",
+ peer->host,
+ lookup_msg(capcode_str, caphdr.code, NULL),
+ caphdr.code, caphdr.length);
+
+ /* Length sanity check, type-specific, for known capabilities */
+ switch (caphdr.code) {
+ case CAPABILITY_CODE_MP:
+ case CAPABILITY_CODE_REFRESH:
+ case CAPABILITY_CODE_ORF:
+ case CAPABILITY_CODE_RESTART:
+ case CAPABILITY_CODE_AS4:
+ case CAPABILITY_CODE_ADDPATH:
+ case CAPABILITY_CODE_DYNAMIC:
+ case CAPABILITY_CODE_ENHE:
+ case CAPABILITY_CODE_FQDN:
+ 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(
+ "%s %s Capability length error: got %u, expected at least %u",
+ peer->host,
+ lookup_msg(capcode_str, caphdr.code,
+ NULL),
+ caphdr.length,
+ (unsigned)cap_minsizes[caphdr.code]);
+ bgp_notify_send(peer->connection,
+ BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_MALFORMED_ATTR);
+ return -1;
+ }
+ if (caphdr.length
+ && caphdr.length % cap_modsizes[caphdr.code] != 0) {
+ zlog_info(
+ "%s %s Capability length error: got %u, expected a multiple of %u",
+ peer->host,
+ lookup_msg(capcode_str, caphdr.code,
+ NULL),
+ caphdr.length,
+ (unsigned)cap_modsizes[caphdr.code]);
+ bgp_notify_send(peer->connection,
+ BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_MALFORMED_ATTR);
+ return -1;
+ }
+ /* we deliberately ignore unknown codes, see below */
+ default:
+ break;
+ }
+
+ switch (caphdr.code) {
+ case CAPABILITY_CODE_MP: {
+ *mp_capability = 1;
+
+ /* Ignore capability when override-capability is set. */
+ if (!CHECK_FLAG(peer->flags,
+ PEER_FLAG_OVERRIDE_CAPABILITY)) {
+ /* Set negotiated value. */
+ ret = bgp_capability_mp(peer, &caphdr);
+
+ /* Unsupported Capability. */
+ if (ret < 0) {
+ /* Store return data. */
+ memcpy(*error, sp, caphdr.length + 2);
+ *error += caphdr.length + 2;
+ }
+ ret = 0; /* Don't return error for this */
+ }
+ } break;
+ case CAPABILITY_CODE_ENHANCED_RR:
+ case CAPABILITY_CODE_REFRESH: {
+ /* BGP refresh capability */
+ if (caphdr.code == CAPABILITY_CODE_ENHANCED_RR)
+ SET_FLAG(peer->cap, PEER_CAP_ENHANCED_RR_RCV);
+ else
+ SET_FLAG(peer->cap, PEER_CAP_REFRESH_RCV);
+ } break;
+ case CAPABILITY_CODE_ORF:
+ ret = bgp_capability_orf_entry(peer, &caphdr);
+ break;
+ case CAPABILITY_CODE_RESTART:
+ ret = bgp_capability_restart(peer, &caphdr);
+ break;
+ case CAPABILITY_CODE_LLGR:
+ ret = bgp_capability_llgr(peer, &caphdr);
+ break;
+ case CAPABILITY_CODE_DYNAMIC:
+ SET_FLAG(peer->cap, PEER_CAP_DYNAMIC_RCV);
+ break;
+ case CAPABILITY_CODE_AS4:
+ /* Already handled as a special-case parsing of the
+ * capabilities
+ * at the beginning of OPEN processing. So we care not a
+ * jot
+ * for the value really, only error case.
+ */
+ if (!bgp_capability_as4(peer, &caphdr))
+ ret = -1;
+ break;
+ case CAPABILITY_CODE_ADDPATH:
+ ret = bgp_capability_addpath(peer, &caphdr);
+ break;
+ case CAPABILITY_CODE_ENHE:
+ ret = bgp_capability_enhe(peer, &caphdr);
+ break;
+ case CAPABILITY_CODE_EXT_MESSAGE:
+ ret = bgp_capability_ext_message(peer, &caphdr);
+ break;
+ case CAPABILITY_CODE_FQDN:
+ ret = bgp_capability_hostname(peer, &caphdr);
+ break;
+ 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
+ specific
+ capabilities. It seems reasonable for now...
+ */
+ flog_warn(EC_BGP_CAPABILITY_VENDOR,
+ "%s Vendor specific capability %d",
+ peer->host, caphdr.code);
+ } else {
+ flog_warn(
+ EC_BGP_CAPABILITY_UNKNOWN,
+ "%s unrecognized capability code: %d - ignored",
+ peer->host, caphdr.code);
+ memcpy(*error, sp, caphdr.length + 2);
+ *error += caphdr.length + 2;
+ }
+ }
+
+ if (ret < 0) {
+ bgp_notify_send(peer->connection, BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_MALFORMED_ATTR);
+ return -1;
+ }
+ if (stream_get_getp(s) != (start + caphdr.length)) {
+ if (stream_get_getp(s) > (start + caphdr.length))
+ flog_warn(
+ EC_BGP_CAPABILITY_INVALID_LENGTH,
+ "%s Cap-parser for %s read past cap-length, %u!",
+ peer->host,
+ lookup_msg(capcode_str, caphdr.code,
+ NULL),
+ caphdr.length);
+ stream_set_getp(s, start + caphdr.length);
+ }
+
+ if (!CHECK_FLAG(peer->cap, PEER_CAP_RESTART_RCV)) {
+ UNSET_FLAG(restart_flag_time, 0xF000);
+ peer->v_gr_restart = restart_flag_time;
+ }
+ }
+ return 0;
+}
+
+static bool strict_capability_same(struct peer *peer)
+{
+ int i, j;
+
+ for (i = AFI_IP; i < AFI_MAX; i++)
+ for (j = SAFI_UNICAST; j < SAFI_MAX; j++)
+ if (peer->afc[i][j] != peer->afc_nego[i][j])
+ return false;
+ return true;
+}
+
+
+static bool bgp_role_violation(struct peer *peer)
+{
+ uint8_t local_role = peer->local_role;
+ uint8_t remote_role = peer->remote_role;
+
+ if (local_role != ROLE_UNDEFINED && remote_role != ROLE_UNDEFINED &&
+ !((local_role == ROLE_PEER && remote_role == ROLE_PEER) ||
+ (local_role == ROLE_PROVIDER && remote_role == ROLE_CUSTOMER) ||
+ (local_role == ROLE_CUSTOMER && remote_role == ROLE_PROVIDER) ||
+ (local_role == ROLE_RS_SERVER && remote_role == ROLE_RS_CLIENT) ||
+ (local_role == ROLE_RS_CLIENT &&
+ remote_role == ROLE_RS_SERVER))) {
+ bgp_notify_send(peer->connection, BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_ROLE_MISMATCH);
+ return true;
+ }
+ if (remote_role == ROLE_UNDEFINED &&
+ CHECK_FLAG(peer->flags, PEER_FLAG_ROLE_STRICT_MODE)) {
+ const char *err_msg =
+ "Strict mode. Please set the role on your side.";
+ bgp_notify_send_with_data(peer->connection, BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_ROLE_MISMATCH,
+ (uint8_t *)err_msg, strlen(err_msg));
+ return true;
+ }
+ return false;
+}
+
+
+/* peek into option, stores ASN to *as4 if the AS4 capability was found.
+ * Returns 0 if no as4 found, as4cap value otherwise.
+ */
+as_t peek_for_as4_capability(struct peer *peer, uint16_t length)
+{
+ struct stream *s = BGP_INPUT(peer);
+ size_t orig_getp = stream_get_getp(s);
+ size_t end = orig_getp + length;
+ as_t as4 = 0;
+
+ if (BGP_DEBUG(as4, AS4))
+ zlog_debug(
+ "%s [AS4] rcv OPEN w/ OPTION parameter len: %u, peeking for as4",
+ peer->host, length);
+ /* the error cases we DONT handle, we ONLY try to read as4 out of
+ * correctly formatted options.
+ */
+ while (stream_get_getp(s) < end) {
+ uint8_t opt_type;
+ uint16_t opt_length;
+
+ /* Ensure we can read the option type */
+ if (stream_get_getp(s) + 1 > end)
+ goto end;
+
+ /* Fetch the option type */
+ opt_type = stream_getc(s);
+
+ /*
+ * Check the length and fetch the opt_length
+ * If the peer is BGP_OPEN_EXT_OPT_PARAMS_CAPABLE(peer)
+ * then we do a getw which is 2 bytes. So we need to
+ * ensure that we can read that as well
+ */
+ if (BGP_OPEN_EXT_OPT_PARAMS_CAPABLE(peer)) {
+ if (stream_get_getp(s) + 2 > end)
+ goto end;
+
+ opt_length = stream_getw(s);
+ } else {
+ if (stream_get_getp(s) + 1 > end)
+ goto end;
+
+ opt_length = stream_getc(s);
+ }
+
+ /* Option length check. */
+ if (stream_get_getp(s) + opt_length > end)
+ goto end;
+
+ if (opt_type == BGP_OPEN_OPT_CAP) {
+ unsigned long capd_start = stream_get_getp(s);
+ unsigned long capd_end = capd_start + opt_length;
+
+ assert(capd_end <= end);
+
+ while (stream_get_getp(s) < capd_end) {
+ struct capability_header hdr;
+
+ if (stream_get_getp(s) + 2 > capd_end)
+ goto end;
+
+ hdr.code = stream_getc(s);
+ hdr.length = stream_getc(s);
+
+ if ((stream_get_getp(s) + hdr.length)
+ > capd_end)
+ goto end;
+
+ if (hdr.code == CAPABILITY_CODE_AS4) {
+ if (BGP_DEBUG(as4, AS4))
+ zlog_debug(
+ "[AS4] found AS4 capability, about to parse");
+ as4 = bgp_capability_as4(peer, &hdr);
+
+ goto end;
+ }
+ stream_forward_getp(s, hdr.length);
+ }
+ }
+ }
+
+end:
+ stream_set_getp(s, orig_getp);
+ return as4;
+}
+
+/**
+ * Parse open option.
+ *
+ * @param[out] mp_capability @see bgp_capability_parse() for semantics.
+ */
+int bgp_open_option_parse(struct peer *peer, uint16_t length,
+ int *mp_capability)
+{
+ int ret = 0;
+ uint8_t *error;
+ uint8_t error_data[BGP_STANDARD_MESSAGE_MAX_PACKET_SIZE];
+ struct stream *s = BGP_INPUT(peer);
+ size_t end = stream_get_getp(s) + length;
+
+ error = error_data;
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s rcv OPEN w/ OPTION parameter len: %u",
+ peer->host, length);
+
+ /* Unset any previously received GR capability. */
+ UNSET_FLAG(peer->cap, PEER_CAP_RESTART_RCV);
+
+ while (stream_get_getp(s) < end) {
+ uint8_t opt_type;
+ uint16_t opt_length;
+
+ /*
+ * Check that we can read the opt_type and fetch it
+ */
+ if (STREAM_READABLE(s) < 1) {
+ zlog_info("%s Option length error", peer->host);
+ bgp_notify_send(peer->connection, BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_MALFORMED_ATTR);
+ return -1;
+ }
+ opt_type = stream_getc(s);
+
+ /*
+ * Check the length of the stream to ensure that
+ * FRR can properly read the opt_length. Then read it
+ */
+ if (BGP_OPEN_EXT_OPT_PARAMS_CAPABLE(peer)) {
+ if (STREAM_READABLE(s) < 2) {
+ zlog_info("%s Option length error", peer->host);
+ bgp_notify_send(peer->connection,
+ BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_MALFORMED_ATTR);
+ return -1;
+ }
+
+ opt_length = stream_getw(s);
+ } else {
+ if (STREAM_READABLE(s) < 1) {
+ zlog_info("%s Option length error", peer->host);
+ bgp_notify_send(peer->connection,
+ BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_MALFORMED_ATTR);
+ return -1;
+ }
+
+ opt_length = stream_getc(s);
+ }
+
+ /* Option length check. */
+ if (STREAM_READABLE(s) < opt_length) {
+ zlog_info("%s Option length error (%d)", peer->host,
+ opt_length);
+ bgp_notify_send(peer->connection, BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_MALFORMED_ATTR);
+ return -1;
+ }
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s rcvd OPEN w/ optional parameter type %u (%s) len %u",
+ peer->host, opt_type,
+ opt_type == BGP_OPEN_OPT_CAP ? "Capability"
+ : "Unknown",
+ opt_length);
+
+ switch (opt_type) {
+ case BGP_OPEN_OPT_CAP:
+ ret = bgp_capability_parse(peer, opt_length,
+ mp_capability, &error);
+ break;
+ default:
+ bgp_notify_send(peer->connection, BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_UNSUP_PARAM);
+ ret = -1;
+ break;
+ }
+
+ /* Parse error. To accumulate all unsupported capability codes,
+ bgp_capability_parse does not return -1 when encounter
+ unsupported capability code. To detect that, please check
+ error and erro_data pointer, like below. */
+ if (ret < 0)
+ return -1;
+ }
+
+ /* All OPEN option is parsed. Check capability when strict compare
+ flag is enabled.*/
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_STRICT_CAP_MATCH)) {
+ /* If Unsupported Capability exists. */
+ if (error != error_data) {
+ bgp_notify_send_with_data(peer->connection,
+ BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_UNSUP_CAPBL,
+ error_data,
+ error - error_data);
+ return -1;
+ }
+
+ /* Check local capability does not negotiated with remote
+ peer. */
+ if (!strict_capability_same(peer)) {
+ bgp_notify_send(peer->connection, BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_UNSUP_CAPBL);
+ return -1;
+ }
+ }
+
+ /* Extended Message Support */
+ peer->max_packet_size =
+ (CHECK_FLAG(peer->cap, PEER_CAP_EXTENDED_MESSAGE_RCV)
+ && CHECK_FLAG(peer->cap, PEER_CAP_EXTENDED_MESSAGE_ADV))
+ ? BGP_EXTENDED_MESSAGE_MAX_PACKET_SIZE
+ : BGP_STANDARD_MESSAGE_MAX_PACKET_SIZE;
+
+ /* Check that roles are corresponding to each other */
+ if (bgp_role_violation(peer))
+ return -1;
+
+ /* Check there are no common AFI/SAFIs and send Unsupported Capability
+ error. */
+ if (*mp_capability
+ && !CHECK_FLAG(peer->flags, PEER_FLAG_OVERRIDE_CAPABILITY)) {
+ if (!peer->afc_nego[AFI_IP][SAFI_UNICAST]
+ && !peer->afc_nego[AFI_IP][SAFI_MULTICAST]
+ && !peer->afc_nego[AFI_IP][SAFI_LABELED_UNICAST]
+ && !peer->afc_nego[AFI_IP][SAFI_MPLS_VPN]
+ && !peer->afc_nego[AFI_IP][SAFI_ENCAP]
+ && !peer->afc_nego[AFI_IP][SAFI_FLOWSPEC]
+ && !peer->afc_nego[AFI_IP6][SAFI_UNICAST]
+ && !peer->afc_nego[AFI_IP6][SAFI_MULTICAST]
+ && !peer->afc_nego[AFI_IP6][SAFI_LABELED_UNICAST]
+ && !peer->afc_nego[AFI_IP6][SAFI_MPLS_VPN]
+ && !peer->afc_nego[AFI_IP6][SAFI_ENCAP]
+ && !peer->afc_nego[AFI_IP6][SAFI_FLOWSPEC]
+ && !peer->afc_nego[AFI_L2VPN][SAFI_EVPN]) {
+ flog_err(EC_BGP_PKT_OPEN,
+ "%s [Error] Configured AFI/SAFIs do not overlap with received MP capabilities",
+ peer->host);
+
+ if (error != error_data)
+ bgp_notify_send_with_data(peer->connection,
+ BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_UNSUP_CAPBL,
+ error_data,
+ error - error_data);
+ else
+ bgp_notify_send(peer->connection,
+ BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_UNSUP_CAPBL);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static void bgp_open_capability_orf(struct stream *s, struct peer *peer,
+ afi_t afi, safi_t safi, uint8_t code,
+ bool ext_opt_params)
+{
+ uint16_t cap_len;
+ uint8_t orf_len;
+ unsigned long capp;
+ unsigned long orfp;
+ unsigned long numberp;
+ int number_of_orfs = 0;
+ iana_afi_t pkt_afi = IANA_AFI_IPV4;
+ iana_safi_t pkt_safi = IANA_SAFI_UNICAST;
+
+ /* Convert AFI, SAFI to values for packet. */
+ bgp_map_afi_safi_int2iana(afi, safi, &pkt_afi, &pkt_safi);
+
+ stream_putc(s, BGP_OPEN_OPT_CAP);
+ capp = stream_get_endp(s); /* Set Capability Len Pointer */
+ ext_opt_params ? stream_putw(s, 0)
+ : stream_putc(s, 0); /* Capability Length */
+ stream_putc(s, code); /* Capability Code */
+ orfp = stream_get_endp(s); /* Set ORF Len Pointer */
+ stream_putc(s, 0); /* ORF Length */
+ stream_putw(s, pkt_afi);
+ stream_putc(s, 0);
+ stream_putc(s, pkt_safi);
+ numberp = stream_get_endp(s); /* Set Number Pointer */
+ stream_putc(s, 0); /* Number of ORFs */
+
+ /* Address Prefix ORF */
+ if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_ORF_PREFIX_SM)
+ || CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_ORF_PREFIX_RM)) {
+ stream_putc(s, ORF_TYPE_PREFIX);
+
+ if (CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_ORF_PREFIX_SM)
+ && CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_ORF_PREFIX_RM)) {
+ SET_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_SM_ADV);
+ SET_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_RM_ADV);
+ stream_putc(s, ORF_MODE_BOTH);
+ } else if (CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_ORF_PREFIX_SM)) {
+ SET_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_SM_ADV);
+ stream_putc(s, ORF_MODE_SEND);
+ } else {
+ SET_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_RM_ADV);
+ stream_putc(s, ORF_MODE_RECEIVE);
+ }
+ number_of_orfs++;
+ }
+
+ /* Total Number of ORFs. */
+ stream_putc_at(s, numberp, number_of_orfs);
+
+ /* Total ORF Len. */
+ orf_len = stream_get_endp(s) - orfp - 1;
+ stream_putc_at(s, orfp, orf_len);
+
+ /* Total Capability Len. */
+ cap_len = stream_get_endp(s) - capp - 1;
+ ext_opt_params ? stream_putw_at(s, capp, cap_len)
+ : stream_putc_at(s, capp, cap_len);
+}
+
+static void bgp_peer_send_gr_capability(struct stream *s, struct peer *peer,
+ bool ext_opt_params)
+{
+ int len;
+ iana_afi_t pkt_afi = IANA_AFI_IPV4;
+ afi_t afi;
+ safi_t safi;
+ iana_safi_t pkt_safi = IANA_SAFI_UNICAST;
+ uint32_t restart_time;
+ unsigned long capp = 0;
+ unsigned long rcapp = 0;
+
+ if (!CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART)
+ && !CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART_HELPER))
+ return;
+
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug("[BGP_GR] Sending helper Capability for Peer :%s :",
+ peer->host);
+
+ SET_FLAG(peer->cap, PEER_CAP_RESTART_ADV);
+ stream_putc(s, BGP_OPEN_OPT_CAP);
+ capp = stream_get_endp(s); /* Set Capability Len Pointer */
+ ext_opt_params ? stream_putw(s, 0)
+ : stream_putc(s, 0); /* Capability Length */
+ stream_putc(s, CAPABILITY_CODE_RESTART);
+ /* Set Restart Capability Len Pointer */
+ rcapp = stream_get_endp(s);
+ stream_putc(s, 0);
+ restart_time = peer->bgp->restart_time;
+ if (peer->bgp->t_startup) {
+ SET_FLAG(restart_time, GRACEFUL_RESTART_R_BIT);
+ SET_FLAG(peer->cap, PEER_CAP_GRACEFUL_RESTART_R_BIT_ADV);
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug("[BGP_GR] Sending R-Bit for peer: %s",
+ peer->host);
+ }
+
+ if (CHECK_FLAG(peer->bgp->flags, BGP_FLAG_GRACEFUL_NOTIFICATION)) {
+ SET_FLAG(restart_time, GRACEFUL_RESTART_N_BIT);
+ SET_FLAG(peer->cap, PEER_CAP_GRACEFUL_RESTART_N_BIT_ADV);
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug("[BGP_GR] Sending N-Bit for peer: %s",
+ peer->host);
+ }
+
+ stream_putw(s, restart_time);
+
+ /* Send address-family specific graceful-restart capability
+ * only when GR config is present
+ */
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART)) {
+ if (CHECK_FLAG(peer->bgp->flags, BGP_FLAG_GR_PRESERVE_FWD)
+ && BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug("[BGP_GR] F bit Set");
+
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (!peer->afc[afi][safi])
+ continue;
+
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug(
+ "[BGP_GR] Sending GR Capability for AFI :%d :, SAFI :%d:",
+ afi, safi);
+
+ /* Convert AFI, SAFI to values for
+ * packet.
+ */
+ bgp_map_afi_safi_int2iana(afi, safi, &pkt_afi,
+ &pkt_safi);
+ stream_putw(s, pkt_afi);
+ stream_putc(s, pkt_safi);
+ if (CHECK_FLAG(peer->bgp->flags,
+ BGP_FLAG_GR_PRESERVE_FWD))
+ stream_putc(s, GRACEFUL_RESTART_F_BIT);
+ else
+ stream_putc(s, 0);
+ }
+ }
+
+ /* Total Graceful restart capability Len. */
+ len = stream_get_endp(s) - rcapp - 1;
+ stream_putc_at(s, rcapp, len);
+
+ /* Total Capability Len. */
+ len = stream_get_endp(s) - capp - 1;
+ ext_opt_params ? stream_putw_at(s, capp, len - 1)
+ : stream_putc_at(s, capp, len);
+}
+
+static void bgp_peer_send_llgr_capability(struct stream *s, struct peer *peer,
+ bool ext_opt_params)
+{
+ int len;
+ iana_afi_t pkt_afi = IANA_AFI_IPV4;
+ afi_t afi;
+ safi_t safi;
+ iana_safi_t pkt_safi = IANA_SAFI_UNICAST;
+ unsigned long capp = 0;
+ unsigned long rcapp = 0;
+
+ if (!CHECK_FLAG(peer->cap, PEER_CAP_RESTART_ADV))
+ return;
+
+ SET_FLAG(peer->cap, PEER_CAP_LLGR_ADV);
+
+ stream_putc(s, BGP_OPEN_OPT_CAP);
+ capp = stream_get_endp(s); /* Set Capability Len Pointer */
+ ext_opt_params ? stream_putw(s, 0)
+ : stream_putc(s, 0); /* Capability Length */
+ stream_putc(s, CAPABILITY_CODE_LLGR);
+
+ rcapp = stream_get_endp(s);
+ stream_putc(s, 0);
+
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (!peer->afc[afi][safi])
+ continue;
+
+ bgp_map_afi_safi_int2iana(afi, safi, &pkt_afi, &pkt_safi);
+
+ stream_putw(s, pkt_afi);
+ stream_putc(s, pkt_safi);
+ stream_putc(s, LLGR_F_BIT);
+ stream_put3(s, peer->bgp->llgr_stale_time);
+
+ SET_FLAG(peer->af_cap[afi][safi], PEER_CAP_LLGR_AF_ADV);
+ }
+
+ /* Total Long-lived Graceful Restart capability Len. */
+ len = stream_get_endp(s) - rcapp - 1;
+ stream_putc_at(s, rcapp, len);
+
+ /* Total Capability Len. */
+ len = stream_get_endp(s) - capp - 1;
+ ext_opt_params ? stream_putw_at(s, capp, len - 1)
+ : stream_putc_at(s, capp, len);
+}
+
+/* Fill in capability open option to the packet. */
+uint16_t bgp_open_capability(struct stream *s, struct peer *peer,
+ bool ext_opt_params)
+{
+ uint16_t len;
+ unsigned long cp, capp, rcapp, eopl = 0;
+ iana_afi_t pkt_afi = IANA_AFI_IPV4;
+ afi_t afi;
+ safi_t safi;
+ iana_safi_t pkt_safi = IANA_SAFI_UNICAST;
+ as_t local_as;
+ uint8_t afi_safi_count = 0;
+ bool adv_addpath_tx = false;
+
+ /* Non-Ext OP Len. */
+ cp = stream_get_endp(s);
+ stream_putc(s, 0);
+
+ if (ext_opt_params) {
+ /* Non-Ext OP Len. */
+ stream_putc_at(s, cp, BGP_OPEN_NON_EXT_OPT_LEN);
+
+ /* Non-Ext OP Type */
+ stream_putc(s, BGP_OPEN_NON_EXT_OPT_TYPE_EXTENDED_LENGTH);
+
+ /* Extended Opt. Parm. Length */
+ eopl = stream_get_endp(s);
+ stream_putw(s, 0);
+ }
+
+ /* Do not send capability. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_CAPABILITY_OPEN)
+ || CHECK_FLAG(peer->flags, PEER_FLAG_DONT_CAPABILITY))
+ return 0;
+
+ /* MP capability for configured AFI, SAFI */
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (peer->afc[afi][safi]) {
+ /* Convert AFI, SAFI to values for packet. */
+ bgp_map_afi_safi_int2iana(afi, safi, &pkt_afi,
+ &pkt_safi);
+
+ peer->afc_adv[afi][safi] = 1;
+ stream_putc(s, BGP_OPEN_OPT_CAP);
+ ext_opt_params
+ ? stream_putw(s, CAPABILITY_CODE_MP_LEN + 2)
+ : stream_putc(s, CAPABILITY_CODE_MP_LEN + 2);
+ stream_putc(s, CAPABILITY_CODE_MP);
+ stream_putc(s, CAPABILITY_CODE_MP_LEN);
+ stream_putw(s, pkt_afi);
+ stream_putc(s, 0);
+ stream_putc(s, pkt_safi);
+
+ /* Extended nexthop capability - currently
+ * supporting RFC-5549 for
+ * Link-Local peering only
+ */
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_CAPABILITY_ENHE) &&
+ peer->connection->su.sa.sa_family == AF_INET6 &&
+ afi == AFI_IP &&
+ (safi == SAFI_UNICAST || safi == SAFI_MPLS_VPN ||
+ safi == SAFI_LABELED_UNICAST)) {
+ /* RFC 5549 Extended Next Hop Encoding
+ */
+ SET_FLAG(peer->cap, PEER_CAP_ENHE_ADV);
+ stream_putc(s, BGP_OPEN_OPT_CAP);
+ ext_opt_params
+ ? stream_putw(s,
+ CAPABILITY_CODE_ENHE_LEN
+ + 2)
+ : stream_putc(s,
+ CAPABILITY_CODE_ENHE_LEN
+ + 2);
+ stream_putc(s, CAPABILITY_CODE_ENHE);
+ stream_putc(s, CAPABILITY_CODE_ENHE_LEN);
+
+ SET_FLAG(peer->af_cap[AFI_IP][safi],
+ PEER_CAP_ENHE_AF_ADV);
+ stream_putw(s, pkt_afi);
+ stream_putw(s, pkt_safi);
+ stream_putw(s, afi_int2iana(AFI_IP6));
+
+ if (CHECK_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ENHE_AF_RCV))
+ SET_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ENHE_AF_NEGO);
+ }
+ }
+ }
+
+ /* Route refresh. */
+ SET_FLAG(peer->cap, PEER_CAP_REFRESH_ADV);
+ stream_putc(s, BGP_OPEN_OPT_CAP);
+ ext_opt_params ? stream_putw(s, CAPABILITY_CODE_REFRESH_LEN + 2)
+ : stream_putc(s, CAPABILITY_CODE_REFRESH_LEN + 2);
+ stream_putc(s, CAPABILITY_CODE_REFRESH);
+ stream_putc(s, CAPABILITY_CODE_REFRESH_LEN);
+
+ /* Enhanced Route Refresh. */
+ SET_FLAG(peer->cap, PEER_CAP_ENHANCED_RR_ADV);
+ stream_putc(s, BGP_OPEN_OPT_CAP);
+ ext_opt_params ? stream_putw(s, CAPABILITY_CODE_ENHANCED_LEN + 2)
+ : stream_putc(s, CAPABILITY_CODE_ENHANCED_LEN + 2);
+ stream_putc(s, CAPABILITY_CODE_ENHANCED_RR);
+ stream_putc(s, CAPABILITY_CODE_ENHANCED_LEN);
+
+ /* AS4 */
+ SET_FLAG(peer->cap, PEER_CAP_AS4_ADV);
+ stream_putc(s, BGP_OPEN_OPT_CAP);
+ ext_opt_params ? stream_putw(s, CAPABILITY_CODE_AS4_LEN + 2)
+ : stream_putc(s, CAPABILITY_CODE_AS4_LEN + 2);
+ stream_putc(s, CAPABILITY_CODE_AS4);
+ stream_putc(s, CAPABILITY_CODE_AS4_LEN);
+ if (peer->change_local_as)
+ local_as = peer->change_local_as;
+ else
+ local_as = peer->local_as;
+ stream_putl(s, local_as);
+
+ /* Extended Message Support */
+ SET_FLAG(peer->cap, PEER_CAP_EXTENDED_MESSAGE_ADV);
+ stream_putc(s, BGP_OPEN_OPT_CAP);
+ ext_opt_params ? stream_putw(s, CAPABILITY_CODE_EXT_MESSAGE_LEN + 2)
+ : stream_putc(s, CAPABILITY_CODE_EXT_MESSAGE_LEN + 2);
+ stream_putc(s, CAPABILITY_CODE_EXT_MESSAGE);
+ stream_putc(s, CAPABILITY_CODE_EXT_MESSAGE_LEN);
+
+ /* Role*/
+ if (peer->local_role != ROLE_UNDEFINED) {
+ SET_FLAG(peer->cap, PEER_CAP_ROLE_ADV);
+ stream_putc(s, BGP_OPEN_OPT_CAP);
+ stream_putc(s, CAPABILITY_CODE_ROLE_LEN + 2);
+ stream_putc(s, CAPABILITY_CODE_ROLE);
+ stream_putc(s, CAPABILITY_CODE_ROLE_LEN);
+ stream_putc(s, peer->local_role);
+ }
+
+ /* AddPath */
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (peer->afc[afi][safi]) {
+ afi_safi_count++;
+
+ /* Only advertise addpath TX if a feature that
+ * will use it is
+ * configured */
+ if (peer->addpath_type[afi][safi] != BGP_ADDPATH_NONE)
+ adv_addpath_tx = true;
+
+ /* If we have enabled labeled unicast, we MUST check
+ * against unicast SAFI because addpath IDs are
+ * allocated under unicast SAFI, the same as the RIB
+ * is managed in unicast SAFI.
+ */
+ if (safi == SAFI_LABELED_UNICAST)
+ if (peer->addpath_type[afi][SAFI_UNICAST] !=
+ BGP_ADDPATH_NONE)
+ adv_addpath_tx = true;
+ }
+ }
+
+ SET_FLAG(peer->cap, PEER_CAP_ADDPATH_ADV);
+ stream_putc(s, BGP_OPEN_OPT_CAP);
+ ext_opt_params
+ ? stream_putw(s, (CAPABILITY_CODE_ADDPATH_LEN * afi_safi_count)
+ + 2)
+ : stream_putc(s, (CAPABILITY_CODE_ADDPATH_LEN * afi_safi_count)
+ + 2);
+ stream_putc(s, CAPABILITY_CODE_ADDPATH);
+ stream_putc(s, CAPABILITY_CODE_ADDPATH_LEN * afi_safi_count);
+
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (peer->afc[afi][safi]) {
+ bool adv_addpath_rx =
+ !CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_DISABLE_ADDPATH_RX);
+ uint8_t flags = 0;
+
+ /* Convert AFI, SAFI to values for packet. */
+ bgp_map_afi_safi_int2iana(afi, safi, &pkt_afi,
+ &pkt_safi);
+
+ stream_putw(s, pkt_afi);
+ stream_putc(s, pkt_safi);
+
+ if (adv_addpath_rx) {
+ SET_FLAG(flags, BGP_ADDPATH_RX);
+ SET_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ADDPATH_AF_RX_ADV);
+ } else {
+ UNSET_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ADDPATH_AF_RX_ADV);
+ }
+
+ if (adv_addpath_tx) {
+ SET_FLAG(flags, BGP_ADDPATH_TX);
+ SET_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ADDPATH_AF_TX_ADV);
+ if (safi == SAFI_LABELED_UNICAST)
+ SET_FLAG(
+ peer->af_cap[afi][SAFI_UNICAST],
+ PEER_CAP_ADDPATH_AF_TX_ADV);
+ } else {
+ UNSET_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ADDPATH_AF_TX_ADV);
+ }
+
+ stream_putc(s, flags);
+ }
+ }
+
+ /* ORF capability. */
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_ORF_PREFIX_SM)
+ || CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_ORF_PREFIX_RM)) {
+ bgp_open_capability_orf(s, peer, afi, safi,
+ CAPABILITY_CODE_ORF,
+ ext_opt_params);
+ }
+ }
+
+ /* Dynamic capability. */
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_DYNAMIC_CAPABILITY)) {
+ SET_FLAG(peer->cap, PEER_CAP_DYNAMIC_ADV);
+ stream_putc(s, BGP_OPEN_OPT_CAP);
+ ext_opt_params
+ ? stream_putw(s, CAPABILITY_CODE_DYNAMIC_LEN + 2)
+ : stream_putc(s, CAPABILITY_CODE_DYNAMIC_LEN + 2);
+ stream_putc(s, CAPABILITY_CODE_DYNAMIC);
+ stream_putc(s, CAPABILITY_CODE_DYNAMIC_LEN);
+ }
+
+ /* Hostname capability */
+ if (cmd_hostname_get()) {
+ SET_FLAG(peer->cap, PEER_CAP_HOSTNAME_ADV);
+ stream_putc(s, BGP_OPEN_OPT_CAP);
+ rcapp = stream_get_endp(s); /* Ptr to length placeholder */
+ ext_opt_params ? stream_putw(s, 0)
+ : stream_putc(s, 0); /* Capability Length */
+ stream_putc(s, CAPABILITY_CODE_FQDN);
+ capp = stream_get_endp(s);
+ stream_putc(s, 0); /* dummy len for now */
+ len = strlen(cmd_hostname_get());
+ if (len > BGP_MAX_HOSTNAME)
+ len = BGP_MAX_HOSTNAME;
+
+ stream_putc(s, len);
+ stream_put(s, cmd_hostname_get(), len);
+ if (cmd_domainname_get()) {
+ len = strlen(cmd_domainname_get());
+ if (len > BGP_MAX_HOSTNAME)
+ len = BGP_MAX_HOSTNAME;
+
+ stream_putc(s, len);
+ stream_put(s, cmd_domainname_get(), len);
+ } else
+ stream_putc(s, 0); /* 0 length */
+
+ /* Set the lengths straight */
+ len = stream_get_endp(s) - rcapp - 1;
+ ext_opt_params ? stream_putw_at(s, rcapp, len - 1)
+ : stream_putc_at(s, rcapp, len);
+
+ len = stream_get_endp(s) - capp - 1;
+ stream_putc_at(s, capp, len);
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s Sending hostname cap with hn = %s, dn = %s",
+ peer->host, cmd_hostname_get(),
+ cmd_domainname_get());
+ }
+
+ 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) ||
+ CHECK_FLAG(peer->bgp->flags, BGP_FLAG_SOFT_VERSION_CAPABILITY) ||
+ 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;
+
+ if (ext_opt_params) {
+ len = stream_get_endp(s) - eopl - 2;
+ stream_putw_at(s, eopl, len);
+ } else {
+ stream_putc_at(s, cp, len);
+ }
+
+ return len;
+}