diff options
Diffstat (limited to 'bgpd/bgp_packet.c')
-rw-r--r-- | bgpd/bgp_packet.c | 307 |
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); |