summaryrefslogtreecommitdiffstats
path: root/bgpd/bgp_packet.c
diff options
context:
space:
mode:
Diffstat (limited to 'bgpd/bgp_packet.c')
-rw-r--r--bgpd/bgp_packet.c307
1 files changed, 233 insertions, 74 deletions
diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c
index 1f808ee..010a31a 100644
--- a/bgpd/bgp_packet.c
+++ b/bgpd/bgp_packet.c
@@ -148,7 +148,8 @@ static void bgp_packet_add(struct peer_connection *connection,
EC_BGP_SENDQ_STUCK_PROPER,
"%pBP has not made any SendQ progress for 2 holdtimes (%jds), terminating session",
peer, sendholdtime);
- BGP_EVENT_ADD(connection, TCP_fatal_error);
+ bgp_stop_with_notify(connection,
+ BGP_NOTIFY_SEND_HOLD_ERR, 0);
} else if (delta > (intmax_t)holdtime &&
monotime(NULL) - peer->last_sendq_warn > 5) {
flog_warn(
@@ -559,40 +560,37 @@ void bgp_generate_updgrp_packets(struct event *thread)
}
}
- if (CHECK_FLAG(peer->cap,
- PEER_CAP_RESTART_RCV)) {
- if (!(PAF_SUBGRP(paf))->t_coalesce
- && peer->afc_nego[afi][safi]
- && peer->synctime
- && !CHECK_FLAG(
- peer->af_sflags[afi][safi],
- PEER_STATUS_EOR_SEND)) {
- /* If EOR is disabled,
- * the message is not sent
- */
- if (BGP_SEND_EOR(peer->bgp, afi,
- safi)) {
- SET_FLAG(
- peer->af_sflags
- [afi]
- [safi],
- PEER_STATUS_EOR_SEND);
-
- /* Update EOR
- * send time
- */
- peer->eor_stime[afi]
- [safi] =
- monotime(NULL);
-
- BGP_UPDATE_EOR_PKT(
- peer, afi, safi,
- s);
- bgp_process_pending_refresh(
- peer, afi,
- safi);
- }
- }
+ /* rfc4724 says:
+ * Although the End-of-RIB marker is
+ * specified for the purpose of BGP
+ * graceful restart, it is noted that
+ * the generation of such a marker upon
+ * completion of the initial update would
+ * be useful for routing convergence in
+ * general, and thus the practice is
+ * recommended.
+ */
+ if (!(PAF_SUBGRP(paf))->t_coalesce &&
+ peer->afc_nego[afi][safi] &&
+ peer->synctime &&
+ !CHECK_FLAG(peer->af_sflags[afi][safi],
+ PEER_STATUS_EOR_SEND)) {
+ /* If EOR is disabled, the message is
+ * not sent.
+ */
+ if (!BGP_SEND_EOR(peer->bgp, afi, safi))
+ continue;
+
+ SET_FLAG(peer->af_sflags[afi][safi],
+ PEER_STATUS_EOR_SEND);
+
+ /* Update EOR send time */
+ peer->eor_stime[afi][safi] =
+ monotime(NULL);
+
+ BGP_UPDATE_EOR_PKT(peer, afi, safi, s);
+ bgp_process_pending_refresh(peer, afi,
+ safi);
}
continue;
}
@@ -1224,7 +1222,7 @@ void bgp_capability_send(struct peer *peer, afi_t afi, safi_t safi,
if (!peer_established(peer->connection))
return;
- if (!CHECK_FLAG(peer->cap, PEER_CAP_DYNAMIC_RCV) &&
+ if (!CHECK_FLAG(peer->cap, PEER_CAP_DYNAMIC_RCV) ||
!CHECK_FLAG(peer->cap, PEER_CAP_DYNAMIC_ADV))
return;
@@ -1459,6 +1457,49 @@ void bgp_capability_send(struct peer *peer, afi_t afi, safi_t safi,
COND_FLAG(peer->cap, PEER_CAP_ADDPATH_ADV,
action == CAPABILITY_ACTION_SET);
break;
+ case CAPABILITY_CODE_PATHS_LIMIT:
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (!peer->afc[afi][safi])
+ continue;
+
+ addpath_afi_safi_count++;
+ }
+
+ stream_putc(s, action);
+ stream_putc(s, CAPABILITY_CODE_PATHS_LIMIT);
+ stream_putc(s, CAPABILITY_CODE_PATHS_LIMIT_LEN *
+ addpath_afi_safi_count);
+
+ 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_putw(s,
+ peer->addpath_paths_limit[afi][safi].send);
+
+ SET_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_PATHS_LIMIT_AF_ADV);
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%pBP sending CAPABILITY has %s %s for afi/safi: %s/%s, limit: %u",
+ peer,
+ action == CAPABILITY_ACTION_SET
+ ? "Advertising"
+ : "Removing",
+ capability, iana_afi2str(pkt_afi),
+ iana_safi2str(pkt_safi),
+ peer->addpath_paths_limit[afi][safi]
+ .send);
+ }
+
+ COND_FLAG(peer->cap, PEER_CAP_PATHS_LIMIT_ADV,
+ action == CAPABILITY_ACTION_SET);
+ break;
case CAPABILITY_CODE_ORF:
/* Convert AFI, SAFI to values for packet. */
bgp_map_afi_safi_int2iana(afi, safi, &pkt_afi, &pkt_safi);
@@ -3165,6 +3206,86 @@ ignore:
}
}
+static void bgp_dynamic_capability_paths_limit(uint8_t *pnt, int action,
+ struct capability_header *hdr,
+ struct peer *peer)
+{
+ uint8_t *data = pnt + 3;
+ uint8_t *end = data + hdr->length;
+ size_t len = end - data;
+ afi_t afi;
+ safi_t safi;
+
+ if (action == CAPABILITY_ACTION_SET) {
+ if (len % CAPABILITY_CODE_PATHS_LIMIT_LEN) {
+ flog_warn(EC_BGP_CAPABILITY_INVALID_LENGTH,
+ "Paths-Limit: Received invalid length %zu, non-multiple of %d",
+ len, CAPABILITY_CODE_PATHS_LIMIT_LEN);
+ return;
+ }
+
+ if (!CHECK_FLAG(peer->cap, PEER_CAP_ADDPATH_RCV)) {
+ flog_warn(EC_BGP_CAPABILITY_INVALID_DATA,
+ "Paths-Limit: Received Paths-Limit capability without Add-Path capability");
+ goto ignore;
+ }
+
+ SET_FLAG(peer->cap, PEER_CAP_PATHS_LIMIT_RCV);
+
+ while (data + CAPABILITY_CODE_PATHS_LIMIT_LEN <= end) {
+ afi_t afi;
+ safi_t safi;
+ iana_afi_t pkt_afi;
+ iana_safi_t pkt_safi;
+ uint16_t paths_limit = 0;
+ struct bgp_paths_limit_capability bpl = {};
+
+ memcpy(&bpl, data, sizeof(bpl));
+ pkt_afi = ntohs(bpl.afi);
+ pkt_safi = safi_int2iana(bpl.safi);
+ paths_limit = ntohs(bpl.paths_limit);
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s OPEN has %s capability for afi/safi: %s/%s limit: %u",
+ peer->host,
+ lookup_msg(capcode_str, hdr->code,
+ NULL),
+ iana_afi2str(pkt_afi),
+ iana_safi2str(pkt_safi), paths_limit);
+
+ 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 Paths-Limit capability for this AFI/SAFI",
+ peer->host,
+ iana_afi2str(pkt_afi),
+ iana_safi2str(pkt_safi));
+ goto ignore;
+ } 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 Paths-Limit capability for this AFI/SAFI",
+ peer->host,
+ iana_afi2str(pkt_afi),
+ iana_safi2str(pkt_safi));
+ goto ignore;
+ }
+
+ SET_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_PATHS_LIMIT_AF_RCV);
+ peer->addpath_paths_limit[afi][safi].receive =
+ paths_limit;
+ignore:
+ data += CAPABILITY_CODE_PATHS_LIMIT_LEN;
+ }
+ } else {
+ FOREACH_AFI_SAFI (afi, safi)
+ UNSET_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_PATHS_LIMIT_AF_RCV);
+
+ UNSET_FLAG(peer->cap, PEER_CAP_PATHS_LIMIT_RCV);
+ }
+}
+
static void bgp_dynamic_capability_orf(uint8_t *pnt, int action,
struct capability_header *hdr,
struct peer *peer)
@@ -3282,6 +3403,22 @@ static void bgp_dynamic_capability_orf(uint8_t *pnt, int action,
}
}
+static void bgp_dynamic_capability_role(uint8_t *pnt, int action,
+ struct peer *peer)
+{
+ uint8_t role;
+
+ if (action == CAPABILITY_ACTION_SET) {
+ SET_FLAG(peer->cap, PEER_CAP_ROLE_RCV);
+ memcpy(&role, pnt + 3, sizeof(role));
+
+ peer->remote_role = role;
+ } else {
+ UNSET_FLAG(peer->cap, PEER_CAP_ROLE_RCV);
+ peer->remote_role = ROLE_UNDEFINED;
+ }
+}
+
static void bgp_dynamic_capability_fqdn(uint8_t *pnt, int action,
struct capability_header *hdr,
struct peer *peer)
@@ -3293,14 +3430,14 @@ static void bgp_dynamic_capability_fqdn(uint8_t *pnt, int action,
if (action == CAPABILITY_ACTION_SET) {
/* hostname */
- if (data + 1 > end) {
+ if (data + 1 >= end) {
zlog_err("%pBP: Received invalid FQDN capability (host name length)",
peer);
return;
}
len = *data;
- if (data + len > end) {
+ if (data + len + 1 > end) {
zlog_err("%pBP: Received invalid FQDN capability length (host name) %d",
peer, hdr->length);
return;
@@ -3323,7 +3460,7 @@ static void bgp_dynamic_capability_fqdn(uint8_t *pnt, int action,
peer->hostname = XSTRDUP(MTYPE_BGP_PEER_HOST, str);
}
- if (data + 1 > end) {
+ if (data + 1 >= end) {
zlog_err("%pBP: Received invalid FQDN capability (domain name length)",
peer);
return;
@@ -3331,7 +3468,7 @@ static void bgp_dynamic_capability_fqdn(uint8_t *pnt, int action,
/* domainname */
len = *data;
- if (data + len > end) {
+ if (data + len + 1 > end) {
zlog_err("%pBP: Received invalid FQDN capability length (domain name) %d",
peer, len);
return;
@@ -3557,7 +3694,7 @@ static void bgp_dynamic_capability_software_version(uint8_t *pnt, int action,
char soft_version[BGP_MAX_SOFT_VERSION + 1] = {};
if (action == CAPABILITY_ACTION_SET) {
- if (data + len > end) {
+ if (data + len + 1 > end) {
zlog_err("%pBP: Received invalid Software Version capability length %d",
peer, len);
return;
@@ -3610,7 +3747,10 @@ static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt,
zlog_err("%pBP: Capability length error", peer);
bgp_notify_send(peer->connection, BGP_NOTIFY_CEASE,
BGP_NOTIFY_SUBCODE_UNSPECIFIC);
- pnt += length;
+ /*
+ * If we did not return then
+ * pnt += length;
+ */
return BGP_Stop;
}
action = *pnt;
@@ -3635,7 +3775,10 @@ static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt,
zlog_err("%pBP: Capability length error", peer);
bgp_notify_send(peer->connection, BGP_NOTIFY_CEASE,
BGP_NOTIFY_SUBCODE_UNSPECIFIC);
- pnt += length;
+ /*
+ * If we did not return then
+ * pnt += length;
+ */
return BGP_Stop;
}
@@ -3645,6 +3788,46 @@ static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt,
capability = lookup_msg(capcode_str, hdr->code, "Unknown");
+ /* Length sanity check, type-specific, for known capabilities */
+ switch (hdr->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:
+ case CAPABILITY_CODE_PATHS_LIMIT:
+ if (hdr->length < cap_minsizes[hdr->code]) {
+ zlog_info("%pBP: %s Capability length error: got %u, expected at least %u",
+ peer, capability, hdr->length,
+ (unsigned int)cap_minsizes[hdr->code]);
+ bgp_notify_send(peer->connection,
+ BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_MALFORMED_ATTR);
+ goto done;
+ }
+ if (hdr->length &&
+ hdr->length % cap_modsizes[hdr->code] != 0) {
+ zlog_info("%pBP %s Capability length error: got %u, expected a multiple of %u",
+ peer, capability, hdr->length,
+ (unsigned int)cap_modsizes[hdr->code]);
+ bgp_notify_send(peer->connection,
+ BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_MALFORMED_ATTR);
+ goto done;
+ }
+ break;
+ default:
+ break;
+ }
+
switch (hdr->code) {
case CAPABILITY_CODE_SOFT_VERSION:
bgp_dynamic_capability_software_version(pnt, action,
@@ -3702,15 +3885,6 @@ static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt,
}
break;
case CAPABILITY_CODE_RESTART:
- if ((hdr->length - 2) % 4) {
- zlog_err("%pBP: Received invalid Graceful-Restart capability length %d",
- peer, hdr->length);
- bgp_notify_send(peer->connection,
- BGP_NOTIFY_CEASE,
- BGP_NOTIFY_SUBCODE_UNSPECIFIC);
- goto done;
- }
-
bgp_dynamic_capability_graceful_restart(pnt, action,
hdr, peer);
break;
@@ -3720,6 +3894,10 @@ static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt,
case CAPABILITY_CODE_ADDPATH:
bgp_dynamic_capability_addpath(pnt, action, hdr, peer);
break;
+ case CAPABILITY_CODE_PATHS_LIMIT:
+ bgp_dynamic_capability_paths_limit(pnt, action, hdr,
+ peer);
+ break;
case CAPABILITY_CODE_ORF:
bgp_dynamic_capability_orf(pnt, action, hdr, peer);
break;
@@ -3734,26 +3912,7 @@ static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt,
case CAPABILITY_CODE_EXT_MESSAGE:
break;
case CAPABILITY_CODE_ROLE:
- if (hdr->length != CAPABILITY_CODE_ROLE_LEN) {
- zlog_err("%pBP: Capability (%s) length error",
- peer, capability);
- bgp_notify_send(peer->connection,
- BGP_NOTIFY_CEASE,
- BGP_NOTIFY_SUBCODE_UNSPECIFIC);
- goto done;
- }
-
- uint8_t role;
-
- if (action == CAPABILITY_ACTION_SET) {
- SET_FLAG(peer->cap, PEER_CAP_ROLE_RCV);
- memcpy(&role, pnt + 3, sizeof(role));
-
- peer->remote_role = role;
- } else {
- UNSET_FLAG(peer->cap, PEER_CAP_ROLE_RCV);
- peer->remote_role = ROLE_UNDEFINED;
- }
+ bgp_dynamic_capability_role(pnt, action, peer);
break;
default:
flog_warn(EC_BGP_UNRECOGNIZED_CAPABILITY,
@@ -3790,8 +3949,8 @@ int bgp_capability_receive(struct peer_connection *connection,
if (bgp_debug_neighbor_events(peer))
zlog_debug("%s rcv CAPABILITY", peer->host);
- /* If peer does not have the capability, send notification. */
- if (!CHECK_FLAG(peer->cap, PEER_CAP_DYNAMIC_ADV)) {
+ if (!CHECK_FLAG(peer->cap, PEER_CAP_DYNAMIC_ADV) ||
+ !CHECK_FLAG(peer->cap, PEER_CAP_DYNAMIC_RCV)) {
flog_err(EC_BGP_NO_CAP,
"%s [Error] BGP dynamic capability is not enabled",
peer->host);