diff options
Diffstat (limited to 'bgpd/bgp_packet.c')
| -rw-r--r-- | bgpd/bgp_packet.c | 1524 |
1 files changed, 1309 insertions, 215 deletions
diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index 9469a0778f..78554893ff 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -24,6 +24,7 @@ #include "lib_errors.h" #include "bgpd/bgpd.h" +#include "bgpd/bgp_addpath.h" #include "bgpd/bgp_table.h" #include "bgpd/bgp_dump.h" #include "bgpd/bgp_bmp.h" @@ -105,21 +106,22 @@ void bgp_packet_set_size(struct stream *s) * Push a packet onto the beginning of the peer's output queue. * This function acquires the peer's write mutex before proceeding. */ -static void bgp_packet_add(struct peer *peer, struct stream *s) +static void bgp_packet_add(struct peer_connection *connection, + struct peer *peer, struct stream *s) { intmax_t delta; uint32_t holdtime; intmax_t sendholdtime; - frr_with_mutex (&peer->io_mtx) { + frr_with_mutex (&connection->io_mtx) { /* if the queue is empty, reset the "last OK" timestamp to * now, otherwise if we write another packet immediately * after it'll get confused */ - if (!stream_fifo_count_safe(peer->obuf)) + if (!stream_fifo_count_safe(connection->obuf)) peer->last_sendq_ok = monotime(NULL); - stream_fifo_push(peer->obuf, s); + stream_fifo_push(connection->obuf, s); delta = monotime(NULL) - peer->last_sendq_ok; @@ -146,7 +148,8 @@ static void bgp_packet_add(struct peer *peer, struct stream *s) EC_BGP_SENDQ_STUCK_PROPER, "%pBP has not made any SendQ progress for 2 holdtimes (%jds), terminating session", peer, sendholdtime); - BGP_EVENT_ADD(peer, 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( @@ -260,7 +263,7 @@ void bgp_update_restarted_peers(struct peer *peer) if (bgp_debug_neighbor_events(peer)) zlog_debug("Peer %s: Checking restarted", peer->host); - if (peer_established(peer)) { + if (peer_established(peer->connection)) { peer->update_delay_over = 1; peer->bgp->restarted_peers++; bgp_check_update_delay(peer->bgp); @@ -283,7 +286,7 @@ void bgp_update_implicit_eors(struct peer *peer) if (bgp_debug_neighbor_events(peer)) zlog_debug("Peer %s: Checking implicit EORs", peer->host); - if (peer_established(peer)) { + if (peer_established(peer->connection)) { peer->update_delay_over = 1; peer->bgp->implicit_eors++; bgp_check_update_delay(peer->bgp); @@ -391,6 +394,7 @@ static void bgp_write_proceed_actions(struct peer *peer) struct bpacket *next_pkt; struct update_subgroup *subgrp; enum bgp_af_index index; + struct peer_connection *connection = peer->connection; for (index = BGP_AF_START; index < BGP_AF_MAX; index++) { paf = peer->peer_af_array[index]; @@ -403,7 +407,7 @@ static void bgp_write_proceed_actions(struct peer *peer) next_pkt = paf->next_pkt_to_send; if (next_pkt && next_pkt->buffer) { - BGP_TIMER_ON(peer->t_generate_updgrp_packets, + BGP_TIMER_ON(connection->t_generate_updgrp_packets, bgp_generate_updgrp_packets, 0); return; } @@ -414,7 +418,7 @@ static void bgp_write_proceed_actions(struct peer *peer) if (bpacket_queue_is_full(SUBGRP_INST(subgrp), SUBGRP_PKTQ(subgrp)) || subgroup_packets_to_build(subgrp)) { - BGP_TIMER_ON(peer->t_generate_updgrp_packets, + BGP_TIMER_ON(connection->t_generate_updgrp_packets, bgp_generate_updgrp_packets, 0); return; } @@ -429,7 +433,7 @@ static void bgp_write_proceed_actions(struct peer *peer) && !CHECK_FLAG(peer->af_sflags[afi][safi], PEER_STATUS_EOR_SEND) && safi != SAFI_MPLS_VPN) { - BGP_TIMER_ON(peer->t_generate_updgrp_packets, + BGP_TIMER_ON(connection->t_generate_updgrp_packets, bgp_generate_updgrp_packets, 0); return; } @@ -444,8 +448,8 @@ static void bgp_write_proceed_actions(struct peer *peer) */ void bgp_generate_updgrp_packets(struct event *thread) { - struct peer *peer = EVENT_ARG(thread); - + struct peer_connection *connection = EVENT_ARG(thread); + struct peer *peer = connection->peer; struct stream *s; struct peer_af *paf; struct bpacket *next_pkt; @@ -462,14 +466,14 @@ void bgp_generate_updgrp_packets(struct event *thread) * if peer is Established and updates are not on hold (as part of * update-delay processing). */ - if (!peer_established(peer)) + if (!peer_established(peer->connection)) return; if ((peer->bgp->main_peers_update_hold) || bgp_update_delay_active(peer->bgp)) return; - if (peer->t_routeadv) + if (peer->connection->t_routeadv) return; /* @@ -477,7 +481,7 @@ void bgp_generate_updgrp_packets(struct event *thread) * let's stop adding to the outq if we are * already at the limit. */ - if (peer->obuf->count >= bm->outq_limit) { + if (connection->obuf->count >= bm->outq_limit) { bgp_write_proceed_actions(peer); return; } @@ -601,14 +605,14 @@ void bgp_generate_updgrp_packets(struct event *thread) * packet with appropriate attributes from peer * and advance peer */ s = bpacket_reformat_for_peer(next_pkt, paf); - bgp_packet_add(peer, s); + bgp_packet_add(connection, peer, s); bpacket_queue_advance_peer(paf); } } while (s && (++generated < wpq) && - (peer->obuf->count <= bm->outq_limit)); + (connection->obuf->count <= bm->outq_limit)); if (generated) - bgp_writes_on(peer); + bgp_writes_on(connection); bgp_write_proceed_actions(peer); } @@ -635,20 +639,21 @@ void bgp_keepalive_send(struct peer *peer) zlog_debug("%s sending KEEPALIVE", peer->host); /* Add packet to the peer. */ - bgp_packet_add(peer, s); + bgp_packet_add(peer->connection, peer, s); - bgp_writes_on(peer); + bgp_writes_on(peer->connection); } /* * Creates a BGP Open packet and appends it to the peer's output queue. * Sets capabilities as necessary. */ -void bgp_open_send(struct peer *peer) +void bgp_open_send(struct peer_connection *connection) { struct stream *s; uint16_t send_holdtime; as_t local_as; + struct peer *peer = connection->peer; if (CHECK_FLAG(peer->flags, PEER_FLAG_TIMER)) send_holdtime = peer->holdtime; @@ -704,31 +709,32 @@ void bgp_open_send(struct peer *peer) hook_call(bgp_packet_send, peer, BGP_MSG_OPEN, stream_get_endp(s), s); /* Add packet to the peer. */ - bgp_packet_add(peer, s); + bgp_packet_add(connection, peer, s); - bgp_writes_on(peer); + bgp_writes_on(connection); } /* * Writes NOTIFICATION message directly to a peer socket without waiting for * the I/O thread. * - * There must be exactly one stream on the peer->obuf FIFO, and the data within - * this stream must match the format of a BGP NOTIFICATION message. + * There must be exactly one stream on the peer->connection->obuf FIFO, and the + * data within this stream must match the format of a BGP NOTIFICATION message. * Transmission is best-effort. * - * @requires peer->io_mtx + * @requires peer->connection->io_mtx * @param peer * @return 0 */ -static void bgp_write_notify(struct peer *peer) +static void bgp_write_notify(struct peer_connection *connection, + struct peer *peer) { int ret, val; uint8_t type; struct stream *s; /* There should be at least one packet. */ - s = stream_fifo_pop(peer->obuf); + s = stream_fifo_pop(connection->obuf); if (!s) return; @@ -739,7 +745,7 @@ static void bgp_write_notify(struct peer *peer) * socket is in nonblocking mode, if we can't deliver the NOTIFY, well, * we only care about getting a clean shutdown at this point. */ - ret = write(peer->fd, STREAM_DATA(s), stream_get_endp(s)); + ret = write(connection->fd, STREAM_DATA(s), stream_get_endp(s)); /* * only connection reset/close gets counted as TCP_fatal_error, failure @@ -747,13 +753,13 @@ static void bgp_write_notify(struct peer *peer) */ if (ret <= 0) { stream_free(s); - BGP_EVENT_ADD(peer, TCP_fatal_error); + BGP_EVENT_ADD(connection, TCP_fatal_error); return; } /* Disable Nagle, make NOTIFY packet go out right away */ val = 1; - (void)setsockopt(peer->fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, + (void)setsockopt(connection->fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val)); /* Retrieve BGP packet type. */ @@ -776,7 +782,7 @@ static void bgp_write_notify(struct peer *peer) * Handle Graceful Restart case where the state changes to * Connect instead of Idle */ - BGP_EVENT_ADD(peer, BGP_Stop); + BGP_EVENT_ADD(connection, BGP_Stop); stream_free(s); } @@ -902,15 +908,17 @@ bool bgp_notify_received_hard_reset(struct peer *peer, uint8_t code, * @param data Data portion * @param datalen length of data portion */ -static void bgp_notify_send_internal(struct peer *peer, uint8_t code, - uint8_t sub_code, uint8_t *data, - size_t datalen, bool use_curr) +static void bgp_notify_send_internal(struct peer_connection *connection, + uint8_t code, uint8_t sub_code, + uint8_t *data, size_t datalen, + bool use_curr) { struct stream *s; + struct peer *peer = connection->peer; bool hard_reset = bgp_notify_send_hard_reset(peer, code, sub_code); /* Lock I/O mutex to prevent other threads from pushing packets */ - frr_mutex_lock_autounlock(&peer->io_mtx); + frr_mutex_lock_autounlock(&connection->io_mtx); /* ============================================== */ /* Allocate new stream. */ @@ -943,7 +951,7 @@ static void bgp_notify_send_internal(struct peer *peer, uint8_t code, bgp_packet_set_size(s); /* wipe output buffer */ - stream_fifo_clean(peer->obuf); + stream_fifo_clean(connection->obuf); /* * If possible, store last packet for debugging purposes. This check is @@ -956,8 +964,9 @@ static void bgp_notify_send_internal(struct peer *peer, uint8_t code, if (use_curr && peer->curr) { size_t packetsize = stream_get_endp(peer->curr); assert(packetsize <= peer->max_packet_size); - memcpy(peer->last_reset_cause, peer->curr->data, packetsize); - peer->last_reset_cause_size = packetsize; + if (peer->last_reset_cause) + stream_free(peer->last_reset_cause); + peer->last_reset_cause = stream_dup(peer->curr); } /* For debug */ @@ -1027,13 +1036,13 @@ static void bgp_notify_send_internal(struct peer *peer, uint8_t code, peer->last_reset = PEER_DOWN_NOTIFY_SEND; /* Add packet to peer's output queue */ - stream_fifo_push(peer->obuf, s); + stream_fifo_push(connection->obuf, s); bgp_peer_gr_flags_update(peer); BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(peer->bgp, peer->bgp->peer); - bgp_write_notify(peer); + bgp_write_notify(connection, peer); } /* @@ -1046,18 +1055,20 @@ static void bgp_notify_send_internal(struct peer *peer, uint8_t code, * @param code BGP error code * @param sub_code BGP error subcode */ -void bgp_notify_send(struct peer *peer, uint8_t code, uint8_t sub_code) +void bgp_notify_send(struct peer_connection *connection, uint8_t code, + uint8_t sub_code) { - bgp_notify_send_internal(peer, code, sub_code, NULL, 0, true); + bgp_notify_send_internal(connection, code, sub_code, NULL, 0, true); } /* * Enqueue notification; called from the main pthread, peer object access is ok. */ -void bgp_notify_send_with_data(struct peer *peer, uint8_t code, +void bgp_notify_send_with_data(struct peer_connection *connection, uint8_t code, uint8_t sub_code, uint8_t *data, size_t datalen) { - bgp_notify_send_internal(peer, code, sub_code, data, datalen, true); + bgp_notify_send_internal(connection, code, sub_code, data, datalen, + true); } /* @@ -1068,7 +1079,8 @@ void bgp_notify_io_invalid(struct peer *peer, uint8_t code, uint8_t sub_code, uint8_t *data, size_t datalen) { /* Avoid touching the peer object */ - bgp_notify_send_internal(peer, code, sub_code, data, datalen, false); + bgp_notify_send_internal(peer->connection, code, sub_code, data, + datalen, false); } /* @@ -1080,6 +1092,7 @@ void bgp_notify_io_invalid(struct peer *peer, uint8_t code, uint8_t sub_code, * @param orf_type Outbound Route Filtering type * @param when_to_refresh Whether to refresh immediately or defer * @param remove Whether to remove ORF for specified AFI/SAFI + * @param subtype BGP enhanced route refresh optional subtypes */ void bgp_route_refresh_send(struct peer *peer, afi_t afi, safi_t safi, uint8_t orf_type, uint8_t when_to_refresh, @@ -1102,7 +1115,7 @@ void bgp_route_refresh_send(struct peer *peer, afi_t afi, safi_t safi, s = stream_new(peer->max_packet_size); /* Make BGP update packet. */ - if (CHECK_FLAG(peer->cap, PEER_CAP_REFRESH_NEW_RCV)) + if (CHECK_FLAG(peer->cap, PEER_CAP_REFRESH_RCV)) bgp_packet_set_marker(s, BGP_MSG_ROUTE_REFRESH_NEW); else bgp_packet_set_marker(s, BGP_MSG_ROUTE_REFRESH_OLD); @@ -1115,7 +1128,7 @@ void bgp_route_refresh_send(struct peer *peer, afi_t afi, safi_t safi, stream_putc(s, 0); stream_putc(s, pkt_safi); - if (orf_type == ORF_TYPE_PREFIX || orf_type == ORF_TYPE_PREFIX_OLD) + if (orf_type == ORF_TYPE_PREFIX) if (remove || filter->plist[FILTER_IN].plist) { uint16_t orf_len; unsigned long orfp; @@ -1177,9 +1190,9 @@ void bgp_route_refresh_send(struct peer *peer, afi_t afi, safi_t safi, } /* Add packet to the peer. */ - bgp_packet_add(peer, s); + bgp_packet_add(peer->connection, peer, s); - bgp_writes_on(peer); + bgp_writes_on(peer->connection); } /* @@ -1197,6 +1210,24 @@ void bgp_capability_send(struct peer *peer, afi_t afi, safi_t safi, struct stream *s; iana_afi_t pkt_afi = IANA_AFI_IPV4; iana_safi_t pkt_safi = IANA_SAFI_UNICAST; + unsigned long cap_len; + uint16_t len; + uint32_t gr_restart_time; + uint8_t addpath_afi_safi_count = 0; + bool adv_addpath_tx = false; + unsigned long number_of_orfs_p; + uint8_t number_of_orfs = 0; + const char *capability = lookup_msg(capcode_str, capability_code, + "Unknown"); + const char *hostname = cmd_hostname_get(); + const char *domainname = cmd_domainname_get(); + + if (!peer_established(peer->connection)) + return; + + if (!CHECK_FLAG(peer->cap, PEER_CAP_DYNAMIC_RCV) || + !CHECK_FLAG(peer->cap, PEER_CAP_DYNAMIC_ADV)) + return; /* Convert AFI, SAFI to values for packet. */ bgp_map_afi_safi_int2iana(afi, safi, &pkt_afi, &pkt_safi); @@ -1207,7 +1238,43 @@ void bgp_capability_send(struct peer *peer, afi_t afi, safi_t safi, bgp_packet_set_marker(s, BGP_MSG_CAPABILITY); /* Encode MP_EXT capability. */ - if (capability_code == CAPABILITY_CODE_MP) { + switch (capability_code) { + case CAPABILITY_CODE_SOFT_VERSION: + stream_putc(s, action); + stream_putc(s, CAPABILITY_CODE_SOFT_VERSION); + cap_len = stream_get_endp(s); + stream_putc(s, 0); /* Capability Length */ + + /* The Capability Length SHOULD be no greater than 64. + * This is the limit to allow other capabilities as much + * space as they require. + */ + const char *soft_version = cmd_software_version_get(); + + len = strlen(soft_version); + if (len > BGP_MAX_SOFT_VERSION) + len = BGP_MAX_SOFT_VERSION; + + stream_putc(s, len); + stream_put(s, soft_version, len); + + /* Software Version capability Len. */ + len = stream_get_endp(s) - cap_len - 1; + stream_putc_at(s, cap_len, len); + + if (bgp_debug_neighbor_events(peer)) + zlog_debug("%pBP sending CAPABILITY has %s %s for afi/safi: %s/%s", + peer, + action == CAPABILITY_ACTION_SET + ? "Advertising" + : "Removing", + capability, iana_afi2str(pkt_afi), + iana_safi2str(pkt_safi)); + + COND_FLAG(peer->cap, PEER_CAP_SOFT_VERSION_ADV, + action == CAPABILITY_ACTION_SET); + break; + case CAPABILITY_CODE_MP: stream_putc(s, action); stream_putc(s, CAPABILITY_CODE_MP); stream_putc(s, CAPABILITY_CODE_MP_LEN); @@ -1216,27 +1283,369 @@ void bgp_capability_send(struct peer *peer, afi_t afi, safi_t safi, stream_putc(s, pkt_safi); if (bgp_debug_neighbor_events(peer)) - zlog_debug( - "%pBP sending CAPABILITY has %s MP_EXT CAP for afi/safi: %s/%s", - peer, - action == CAPABILITY_ACTION_SET ? "Advertising" - : "Removing", - iana_afi2str(pkt_afi), iana_safi2str(pkt_safi)); + zlog_debug("%pBP sending CAPABILITY has %s %s for afi/safi: %s/%s", + peer, + action == CAPABILITY_ACTION_SET + ? "Advertising" + : "Removing", + capability, iana_afi2str(pkt_afi), + iana_safi2str(pkt_safi)); + break; + case CAPABILITY_CODE_RESTART: + stream_putc(s, action); + stream_putc(s, CAPABILITY_CODE_RESTART); + cap_len = stream_get_endp(s); + stream_putc(s, 0); + gr_restart_time = peer->bgp->restart_time; + + if (peer->bgp->t_startup) { + SET_FLAG(gr_restart_time, GRACEFUL_RESTART_R_BIT); + SET_FLAG(peer->cap, PEER_CAP_GRACEFUL_RESTART_R_BIT_ADV); + } + + if (CHECK_FLAG(peer->bgp->flags, + BGP_FLAG_GRACEFUL_NOTIFICATION)) { + SET_FLAG(gr_restart_time, GRACEFUL_RESTART_N_BIT); + SET_FLAG(peer->cap, PEER_CAP_GRACEFUL_RESTART_N_BIT_ADV); + } + + stream_putw(s, gr_restart_time); + + if (CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART)) { + 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); + if (CHECK_FLAG(peer->bgp->flags, + BGP_FLAG_GR_PRESERVE_FWD)) + stream_putc(s, GRACEFUL_RESTART_F_BIT); + else + stream_putc(s, 0); + } + } + + len = stream_get_endp(s) - cap_len - 1; + stream_putc_at(s, cap_len, len); + + if (bgp_debug_neighbor_events(peer)) + zlog_debug("%pBP sending CAPABILITY has %s %s for afi/safi: %s/%s", + peer, + action == CAPABILITY_ACTION_SET + ? "Advertising" + : "Removing", + capability, iana_afi2str(pkt_afi), + iana_safi2str(pkt_safi)); + + COND_FLAG(peer->cap, PEER_CAP_RESTART_ADV, + action == CAPABILITY_ACTION_SET); + break; + case CAPABILITY_CODE_LLGR: + stream_putc(s, action); + stream_putc(s, CAPABILITY_CODE_LLGR); + cap_len = 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); + } + + len = stream_get_endp(s) - cap_len - 1; + stream_putc_at(s, cap_len, len); + + if (bgp_debug_neighbor_events(peer)) + zlog_debug("%pBP sending CAPABILITY has %s %s for afi/safi: %s/%s", + peer, + action == CAPABILITY_ACTION_SET + ? "Advertising" + : "Removing", + capability, iana_afi2str(pkt_afi), + iana_safi2str(pkt_safi)); + + COND_FLAG(peer->cap, PEER_CAP_LLGR_ADV, + action == CAPABILITY_ACTION_SET); + break; + case CAPABILITY_CODE_ADDPATH: + FOREACH_AFI_SAFI (afi, safi) { + if (peer->afc[afi][safi]) { + addpath_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; + } + } + + stream_putc(s, action); + stream_putc(s, CAPABILITY_CODE_ADDPATH); + stream_putc(s, CAPABILITY_CODE_ADDPATH_LEN * + addpath_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); + } + } + + if (bgp_debug_neighbor_events(peer)) + zlog_debug("%pBP sending CAPABILITY has %s %s for afi/safi: %s/%s", + peer, + action == CAPABILITY_ACTION_SET + ? "Advertising" + : "Removing", + capability, iana_afi2str(pkt_afi), + iana_safi2str(pkt_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); + + stream_putc(s, action); + stream_putc(s, CAPABILITY_CODE_ORF); + cap_len = stream_get_endp(s); + stream_putc(s, 0); + + stream_putw(s, pkt_afi); /* Address Family Identifier */ + stream_putc(s, 0); /* Reserved */ + stream_putc(s, + pkt_safi); /* Subsequent Address Family Identifier */ + + number_of_orfs_p = + stream_get_endp(s); /* Number of ORFs 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); + UNSET_FLAG(peer->af_cap[afi][safi], + PEER_CAP_ORF_PREFIX_RM_ADV); + stream_putc(s, ORF_MODE_SEND); + } else { + SET_FLAG(peer->af_cap[afi][safi], + PEER_CAP_ORF_PREFIX_RM_ADV); + UNSET_FLAG(peer->af_cap[afi][safi], + PEER_CAP_ORF_PREFIX_SM_ADV); + stream_putc(s, ORF_MODE_RECEIVE); + } + number_of_orfs++; + } else { + UNSET_FLAG(peer->af_cap[afi][safi], + PEER_CAP_ORF_PREFIX_SM_ADV); + UNSET_FLAG(peer->af_cap[afi][safi], + PEER_CAP_ORF_PREFIX_RM_ADV); + } + + /* Total Number of ORFs. */ + stream_putc_at(s, number_of_orfs_p, number_of_orfs); + + len = stream_get_endp(s) - cap_len - 1; + stream_putc_at(s, cap_len, len); + + if (bgp_debug_neighbor_events(peer)) + zlog_debug("%pBP sending CAPABILITY has %s %s for afi/safi: %s/%s", + peer, + action == CAPABILITY_ACTION_SET + ? "Advertising" + : "Removing", + capability, iana_afi2str(pkt_afi), + iana_safi2str(pkt_safi)); + break; + case CAPABILITY_CODE_FQDN: + stream_putc(s, action); + stream_putc(s, CAPABILITY_CODE_FQDN); + cap_len = stream_get_endp(s); + stream_putc(s, 0); /* Capability Length */ + + len = strlen(hostname); + if (len > BGP_MAX_HOSTNAME) + len = BGP_MAX_HOSTNAME; + + stream_putc(s, len); + stream_put(s, hostname, len); + + if (domainname) { + len = strlen(domainname); + if (len > BGP_MAX_HOSTNAME) + len = BGP_MAX_HOSTNAME; + + stream_putc(s, len); + stream_put(s, domainname, len); + } else + stream_putc(s, 0); + + len = stream_get_endp(s) - cap_len - 1; + stream_putc_at(s, cap_len, len); + + if (bgp_debug_neighbor_events(peer)) + zlog_debug("%pBP sending CAPABILITY has %s %s for afi/safi: %s/%s", + peer, + action == CAPABILITY_ACTION_SET + ? "Advertising" + : "Removing", + capability, iana_afi2str(pkt_afi), + iana_safi2str(pkt_safi)); + + COND_FLAG(peer->cap, PEER_CAP_HOSTNAME_ADV, + action == CAPABILITY_ACTION_SET); + break; + case CAPABILITY_CODE_REFRESH: + case CAPABILITY_CODE_AS4: + case CAPABILITY_CODE_DYNAMIC: + case CAPABILITY_CODE_ENHANCED_RR: + case CAPABILITY_CODE_ENHE: + case CAPABILITY_CODE_EXT_MESSAGE: + break; + case CAPABILITY_CODE_ROLE: + stream_putc(s, action); + stream_putc(s, CAPABILITY_CODE_ROLE); + stream_putc(s, CAPABILITY_CODE_ROLE_LEN); + stream_putc(s, peer->local_role); + COND_FLAG(peer->cap, PEER_CAP_ROLE_ADV, + action == CAPABILITY_ACTION_SET); + break; + default: + break; } /* Set packet size. */ bgp_packet_set_size(s); /* Add packet to the peer. */ - bgp_packet_add(peer, s); + bgp_packet_add(peer->connection, peer, s); - bgp_writes_on(peer); + bgp_writes_on(peer->connection); } /* RFC1771 6.8 Connection collision detection. */ -static int bgp_collision_detect(struct peer *new, struct in_addr remote_id) +static int bgp_collision_detect(struct peer_connection *connection, + struct peer *new, struct in_addr remote_id) { struct peer *peer; + struct peer_connection *other; /* * Upon receipt of an OPEN message, the local system must examine @@ -1252,18 +1661,22 @@ static int bgp_collision_detect(struct peer *new, struct in_addr remote_id) if (peer == NULL) return 0; + other = peer->connection; + /* * Do not accept the new connection in Established or Clearing * states. Note that a peer GR is handled by closing the existing * connection upon receipt of new one. */ - if (peer_established(peer) || peer->status == Clearing) { - bgp_notify_send(new, BGP_NOTIFY_CEASE, + if (peer_established(other) || + other->status == Clearing) { + bgp_notify_send(connection, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_COLLISION_RESOLUTION); return -1; } - if ((peer->status != OpenConfirm) && (peer->status != OpenSent)) + if ((other->status != OpenConfirm) && + (other->status != OpenSent)) return 0; /* @@ -1290,11 +1703,11 @@ static int bgp_collision_detect(struct peer *new, struct in_addr remote_id) * and accepts BGP connection initiated by * the remote system. */ - bgp_notify_send(peer, BGP_NOTIFY_CEASE, + bgp_notify_send(other, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_COLLISION_RESOLUTION); return 1; } else { - bgp_notify_send(new, BGP_NOTIFY_CEASE, + bgp_notify_send(connection, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_COLLISION_RESOLUTION); return -1; } @@ -1313,11 +1726,11 @@ static int bgp_collision_detect(struct peer *new, struct in_addr remote_id) * OpenConfirm state). */ if (CHECK_FLAG(peer->sflags, PEER_STATUS_ACCEPT_PEER)) { - bgp_notify_send(peer, BGP_NOTIFY_CEASE, + bgp_notify_send(other, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_COLLISION_RESOLUTION); return 1; } else { - bgp_notify_send(new, BGP_NOTIFY_CEASE, + bgp_notify_send(connection, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_COLLISION_RESOLUTION); return -1; } @@ -1344,7 +1757,7 @@ static int bgp_collision_detect(struct peer *new, struct in_addr remote_id) * Side effects * ------------ * - May send NOTIFY messages - * - May not modify peer->status + * - May not modify peer->connection->status * - May not call bgp_event_update() */ @@ -1360,7 +1773,8 @@ static int bgp_collision_detect(struct peer *new, struct in_addr remote_id) * @param size size of the packet * @return as in summary */ -static int bgp_open_receive(struct peer *peer, bgp_size_t size) +static int bgp_open_receive(struct peer_connection *connection, + struct peer *peer, bgp_size_t size) { int ret; uint8_t version; @@ -1399,7 +1813,7 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size) EC_BGP_PKT_OPEN, "%s: stream does not have enough bytes for extended optional parameters", peer->host); - bgp_notify_send(peer, BGP_NOTIFY_OPEN_ERR, + bgp_notify_send(connection, BGP_NOTIFY_OPEN_ERR, BGP_NOTIFY_OPEN_MALFORMED_ATTR); return BGP_Stop; } @@ -1411,7 +1825,7 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size) EC_BGP_PKT_OPEN, "%s: stream does not have enough bytes to read the extended optional parameters optlen", peer->host); - bgp_notify_send(peer, BGP_NOTIFY_OPEN_ERR, + bgp_notify_send(connection, BGP_NOTIFY_OPEN_ERR, BGP_NOTIFY_OPEN_MALFORMED_ATTR); return BGP_Stop; } @@ -1438,7 +1852,7 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size) flog_err(EC_BGP_PKT_OPEN, "%s: stream has not enough bytes (%u)", peer->host, optlen); - bgp_notify_send(peer, BGP_NOTIFY_OPEN_ERR, + bgp_notify_send(connection, BGP_NOTIFY_OPEN_ERR, BGP_NOTIFY_OPEN_MALFORMED_ATTR); return BGP_Stop; } @@ -1460,7 +1874,7 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size) flog_err(EC_BGP_PKT_OPEN, "%s bad OPEN, got AS4 capability, but AS4 set to 0", peer->host); - bgp_notify_send_with_data(peer, BGP_NOTIFY_OPEN_ERR, + bgp_notify_send_with_data(connection, BGP_NOTIFY_OPEN_ERR, BGP_NOTIFY_OPEN_BAD_PEER_AS, notify_data_remote_as4, 4); return BGP_Stop; @@ -1470,7 +1884,7 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size) if (remote_as == BGP_AS_ZERO) { flog_err(EC_BGP_PKT_OPEN, "%s bad OPEN, got AS set to 0", peer->host); - bgp_notify_send(peer, BGP_NOTIFY_OPEN_ERR, + bgp_notify_send(connection, BGP_NOTIFY_OPEN_ERR, BGP_NOTIFY_OPEN_BAD_PEER_AS); return BGP_Stop; } @@ -1485,7 +1899,8 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size) EC_BGP_PKT_OPEN, "%s [AS4] NEW speaker using AS_TRANS for AS4, not allowed", peer->host); - bgp_notify_send_with_data(peer, BGP_NOTIFY_OPEN_ERR, + bgp_notify_send_with_data(connection, + BGP_NOTIFY_OPEN_ERR, BGP_NOTIFY_OPEN_BAD_PEER_AS, notify_data_remote_as4, 4); return BGP_Stop; @@ -1513,7 +1928,8 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size) EC_BGP_PKT_OPEN, "%s bad OPEN, got AS4 capability, but remote_as %u mismatch with 16bit 'myasn' %u in open", peer->host, as4, remote_as); - bgp_notify_send_with_data(peer, BGP_NOTIFY_OPEN_ERR, + bgp_notify_send_with_data(connection, + BGP_NOTIFY_OPEN_ERR, BGP_NOTIFY_OPEN_BAD_PEER_AS, notify_data_remote_as4, 4); return BGP_Stop; @@ -1533,7 +1949,7 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size) if (bgp_debug_neighbor_events(peer)) zlog_debug("%s bad OPEN, wrong router identifier %pI4", peer->host, &remote_id); - bgp_notify_send_with_data(peer, BGP_NOTIFY_OPEN_ERR, + bgp_notify_send_with_data(connection, BGP_NOTIFY_OPEN_ERR, BGP_NOTIFY_OPEN_BAD_BGP_IDENT, notify_data_remote_id, 4); return BGP_Stop; @@ -1548,7 +1964,7 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size) "%s bad protocol version, remote requested %d, local request %d", peer->host, version, BGP_VERSION_4); /* Data must be in network byte order here */ - bgp_notify_send_with_data(peer, BGP_NOTIFY_OPEN_ERR, + bgp_notify_send_with_data(connection, BGP_NOTIFY_OPEN_ERR, BGP_NOTIFY_OPEN_UNSUP_VERSION, (uint8_t *)&maxver, 2); return BGP_Stop; @@ -1560,7 +1976,7 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size) zlog_debug( "%s bad OPEN, remote AS is unspecified currently", peer->host); - bgp_notify_send_with_data(peer, BGP_NOTIFY_OPEN_ERR, + bgp_notify_send_with_data(connection, BGP_NOTIFY_OPEN_ERR, BGP_NOTIFY_OPEN_BAD_PEER_AS, notify_data_remote_as, 2); return BGP_Stop; @@ -1570,7 +1986,8 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size) zlog_debug( "%s bad OPEN, remote AS is %u, internal specified", peer->host, remote_as); - bgp_notify_send_with_data(peer, BGP_NOTIFY_OPEN_ERR, + bgp_notify_send_with_data(connection, + BGP_NOTIFY_OPEN_ERR, BGP_NOTIFY_OPEN_BAD_PEER_AS, notify_data_remote_as, 2); return BGP_Stop; @@ -1582,7 +1999,8 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size) zlog_debug( "%s bad OPEN, remote AS is %u, external specified", peer->host, remote_as); - bgp_notify_send_with_data(peer, BGP_NOTIFY_OPEN_ERR, + bgp_notify_send_with_data(connection, + BGP_NOTIFY_OPEN_ERR, BGP_NOTIFY_OPEN_BAD_PEER_AS, notify_data_remote_as, 2); return BGP_Stop; @@ -1592,7 +2010,7 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size) if (bgp_debug_neighbor_events(peer)) zlog_debug("%s bad OPEN, remote AS is %u, expected %u", peer->host, remote_as, peer->as); - bgp_notify_send_with_data(peer, BGP_NOTIFY_OPEN_ERR, + bgp_notify_send_with_data(connection, BGP_NOTIFY_OPEN_ERR, BGP_NOTIFY_OPEN_BAD_PEER_AS, notify_data_remote_as, 2); return BGP_Stop; @@ -1602,7 +2020,7 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size) * When collision is detected and this peer is closed. * Return immediately. */ - ret = bgp_collision_detect(peer, remote_id); + ret = bgp_collision_detect(connection, peer, remote_id); if (ret < 0) return BGP_Stop; @@ -1625,7 +2043,7 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size) */ if (holdtime < 3 && holdtime != 0) { - bgp_notify_send_with_data(peer, BGP_NOTIFY_OPEN_ERR, + bgp_notify_send_with_data(connection, BGP_NOTIFY_OPEN_ERR, BGP_NOTIFY_OPEN_UNACEP_HOLDTIME, (uint8_t *)holdtime_ptr, 2); return BGP_Stop; @@ -1635,7 +2053,7 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size) * is smaller than configured minimum Hold Time. */ if (holdtime < peer->bgp->default_min_holdtime && peer->bgp->default_min_holdtime != 0) { - bgp_notify_send_with_data(peer, BGP_NOTIFY_OPEN_ERR, + bgp_notify_send_with_data(connection, BGP_NOTIFY_OPEN_ERR, BGP_NOTIFY_OPEN_UNACEP_HOLDTIME, (uint8_t *)holdtime_ptr, 2); return BGP_Stop; @@ -1733,14 +2151,17 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size) || peer->afc_nego[AFI_IP6][SAFI_MULTICAST] || peer->afc_nego[AFI_IP6][SAFI_MPLS_VPN] || peer->afc_nego[AFI_IP6][SAFI_ENCAP]) { - if (IN6_IS_ADDR_UNSPECIFIED(&peer->nexthop.v6_global)) { -#if defined(HAVE_CUMULUS) - zlog_warn("%s: No local IPv6 address, BGP routing may not work", - peer->host); -#endif + if (IN6_IS_ADDR_UNSPECIFIED(&peer->nexthop.v6_global) && + !bm->v6_with_v4_nexthops) { + flog_err(EC_BGP_SND_FAIL, +"%s: No local IPv6 address, and zebra does not support V6 routing with v4 nexthops, BGP routing for V6 will not work", + peer->host); + bgp_notify_send(connection, BGP_NOTIFY_CEASE, + BGP_NOTIFY_SUBCODE_UNSPECIFIC); + return BGP_Stop; } } - peer->rtt = sockopt_tcp_rtt(peer->fd); + peer->rtt = sockopt_tcp_rtt(connection->fd); return Receive_OPEN_message; } @@ -1752,14 +2173,15 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size) * @param size size of the packet * @return as in summary */ -static int bgp_keepalive_receive(struct peer *peer, bgp_size_t size) +static int bgp_keepalive_receive(struct peer_connection *connection, + struct peer *peer, bgp_size_t size) { if (bgp_debug_keepalive(peer)) zlog_debug("%s KEEPALIVE rcvd", peer->host); bgp_update_implicit_eors(peer); - peer->rtt = sockopt_tcp_rtt(peer->fd); + peer->rtt = sockopt_tcp_rtt(connection->fd); /* If the peer's RTT is higher than expected, shutdown * the peer automatically. @@ -1812,7 +2234,7 @@ static void bgp_refresh_stalepath_timer_expire(struct event *thread) "%pBP route-refresh (BoRR) timer expired for afi/safi: %d/%d", peer, afi, safi); - bgp_timer_set(peer); + bgp_timer_set(peer->connection); } /** @@ -1824,7 +2246,8 @@ static void bgp_refresh_stalepath_timer_expire(struct event *thread) * @param size size of the packet * @return as in summary */ -static int bgp_update_receive(struct peer *peer, bgp_size_t size) +static int bgp_update_receive(struct peer_connection *connection, + struct peer *peer, bgp_size_t size) { int ret, nlri_ret; uint8_t *end; @@ -1845,13 +2268,14 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size) struct bgp_nlri nlris[NLRI_TYPE_MAX]; /* Status must be Established. */ - if (!peer_established(peer)) { + if (!peer_established(connection)) { flog_err(EC_BGP_INVALID_STATUS, "%s [FSM] Update packet received under status %s", peer->host, - lookup_msg(bgp_status_msg, peer->status, NULL)); - bgp_notify_send(peer, BGP_NOTIFY_FSM_ERR, - bgp_fsm_error_subcode(peer->status)); + lookup_msg(bgp_status_msg, peer->connection->status, + NULL)); + bgp_notify_send(connection, BGP_NOTIFY_FSM_ERR, + bgp_fsm_error_subcode(peer->connection->status)); return BGP_Stop; } @@ -1874,7 +2298,7 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size) flog_err(EC_BGP_UPDATE_RCV, "%s [Error] Update packet error (packet length is short for unfeasible length)", peer->host); - bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR, + bgp_notify_send(connection, BGP_NOTIFY_UPDATE_ERR, BGP_NOTIFY_UPDATE_MAL_ATTR); return BGP_Stop; } @@ -1887,7 +2311,7 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size) flog_err(EC_BGP_UPDATE_RCV, "%s [Error] Update packet error (packet unfeasible length overflow %d)", peer->host, withdraw_len); - bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR, + bgp_notify_send(connection, BGP_NOTIFY_UPDATE_ERR, BGP_NOTIFY_UPDATE_MAL_ATTR); return BGP_Stop; } @@ -1907,7 +2331,7 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size) EC_BGP_UPDATE_PACKET_SHORT, "%s [Error] Packet Error (update packet is short for attribute length)", peer->host); - bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR, + bgp_notify_send(peer->connection, BGP_NOTIFY_UPDATE_ERR, BGP_NOTIFY_UPDATE_MAL_ATTR); return BGP_Stop; } @@ -1921,7 +2345,7 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size) EC_BGP_UPDATE_PACKET_LONG, "%s [Error] Packet Error (update packet attribute length overflow %d)", peer->host, attribute_len); - bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR, + bgp_notify_send(connection, BGP_NOTIFY_UPDATE_ERR, BGP_NOTIFY_UPDATE_MAL_ATTR); return BGP_Stop; } @@ -1965,7 +2389,8 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size) "%pBP rcvd UPDATE with errors in attr(s)!! Withdrawing route.", peer); - if (ret && bgp_debug_update(peer, NULL, NULL, 1)) { + if (ret && bgp_debug_update(peer, NULL, NULL, 1) && + BGP_DEBUG(update, UPDATE_DETAIL)) { zlog_debug("%pBP rcvd UPDATE w/ attr: %s", peer, peer->rcvd_attr_str); peer->rcvd_attr_printed = 1; @@ -1975,7 +2400,12 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size) /* Network Layer Reachability Information. */ update_len = end - stream_pnt(s); - if (update_len) { + /* If we received MP_UNREACH_NLRI attribute, but also NLRIs, then + * NLRIs should be handled as a new data. Though, if we received + * NLRIs without mandatory attributes, they should be ignored. + */ + if (update_len && attribute_len && + attr_parse_ret != BGP_ATTR_PARSE_MISSING_MANDATORY) { /* Set NLRI portion to structure. */ nlris[NLRI_UPDATE].afi = AFI_IP; nlris[NLRI_UPDATE].safi = SAFI_UNICAST; @@ -1995,7 +2425,7 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size) } } - if (BGP_DEBUG(update, UPDATE_IN)) + if (BGP_DEBUG(update, UPDATE_IN) && BGP_DEBUG(update, UPDATE_DETAIL)) zlog_debug("%pBP rcvd UPDATE wlen %d attrlen %d alen %d", peer, withdraw_len, attribute_len, update_len); @@ -2036,12 +2466,12 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size) && nlri_ret != BGP_NLRI_PARSE_ERROR_PREFIX_OVERFLOW) { flog_err(EC_BGP_UPDATE_RCV, "%s [Error] Error parsing NLRI", peer->host); - if (peer_established(peer)) - bgp_notify_send( - peer, BGP_NOTIFY_UPDATE_ERR, - i <= NLRI_WITHDRAW - ? BGP_NOTIFY_UPDATE_INVAL_NETWORK - : BGP_NOTIFY_UPDATE_OPT_ATTR_ERR); + if (peer_established(connection)) + bgp_notify_send(connection, + BGP_NOTIFY_UPDATE_ERR, + i <= NLRI_WITHDRAW + ? BGP_NOTIFY_UPDATE_INVAL_NETWORK + : BGP_NOTIFY_UPDATE_OPT_ATTR_ERR); bgp_attr_unintern_sub(&attr); return BGP_Stop; } @@ -2052,8 +2482,7 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size) * Non-MP IPv4/Unicast EoR is a completely empty UPDATE * and MP EoR should have only an empty MP_UNREACH */ - if ((!update_len && !withdraw_len && nlris[NLRI_MP_UPDATE].length == 0) - || (attr_parse_ret == BGP_ATTR_PARSE_EOR)) { + if (!update_len && !withdraw_len && nlris[NLRI_MP_UPDATE].length == 0) { afi_t afi = 0; safi_t safi; struct graceful_restart_info *gr_info; @@ -2074,9 +2503,6 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size) && nlris[NLRI_MP_WITHDRAW].length == 0) { afi = nlris[NLRI_MP_WITHDRAW].afi; safi = nlris[NLRI_MP_WITHDRAW].safi; - } else if (attr_parse_ret == BGP_ATTR_PARSE_EOR) { - afi = nlris[NLRI_MP_UPDATE].afi; - safi = nlris[NLRI_MP_UPDATE].safi; } if (afi && peer->afc[afi][safi]) { @@ -2149,7 +2575,8 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size) * @param size size of the packet * @return as in summary */ -static int bgp_notify_receive(struct peer *peer, bgp_size_t size) +static int bgp_notify_receive(struct peer_connection *connection, + struct peer *peer, bgp_size_t size) { struct bgp_notify outer = {}; struct bgp_notify inner = {}; @@ -2270,7 +2697,8 @@ static int bgp_notify_receive(struct peer *peer, bgp_size_t size) * @param size size of the packet * @return as in summary */ -static int bgp_route_refresh_receive(struct peer *peer, bgp_size_t size) +static int bgp_route_refresh_receive(struct peer_connection *connection, + struct peer *peer, bgp_size_t size) { iana_afi_t pkt_afi; afi_t afi; @@ -2290,20 +2718,20 @@ static int bgp_route_refresh_receive(struct peer *peer, bgp_size_t size) flog_err(EC_BGP_NO_CAP, "%s [Error] BGP route refresh is not enabled", peer->host); - bgp_notify_send(peer, BGP_NOTIFY_HEADER_ERR, + bgp_notify_send(connection, BGP_NOTIFY_HEADER_ERR, BGP_NOTIFY_HEADER_BAD_MESTYPE); return BGP_Stop; } /* Status must be Established. */ - if (!peer_established(peer)) { - flog_err( - EC_BGP_INVALID_STATUS, - "%s [Error] Route refresh packet received under status %s", - peer->host, - lookup_msg(bgp_status_msg, peer->status, NULL)); - bgp_notify_send(peer, BGP_NOTIFY_FSM_ERR, - bgp_fsm_error_subcode(peer->status)); + if (!peer_established(connection)) { + flog_err(EC_BGP_INVALID_STATUS, + "%s [Error] Route refresh packet received under status %s", + peer->host, + lookup_msg(bgp_status_msg, peer->connection->status, + NULL)); + bgp_notify_send(connection, BGP_NOTIFY_FSM_ERR, + bgp_fsm_error_subcode(peer->connection->status)); return BGP_Stop; } @@ -2341,9 +2769,9 @@ static int bgp_route_refresh_receive(struct peer *peer, bgp_size_t size) zlog_err( "%s Enhanced Route Refresh message length error", peer->host); - bgp_notify_send( - peer, BGP_NOTIFY_ROUTE_REFRESH_ERR, - BGP_NOTIFY_ROUTE_REFRESH_INVALID_MSG_LEN); + bgp_notify_send(connection, + BGP_NOTIFY_ROUTE_REFRESH_ERR, + BGP_NOTIFY_ROUTE_REFRESH_INVALID_MSG_LEN); } /* When the BGP speaker receives a ROUTE-REFRESH message @@ -2359,7 +2787,7 @@ static int bgp_route_refresh_receive(struct peer *peer, bgp_size_t size) if (msg_length < 5) { zlog_info("%s ORF route refresh length error", peer->host); - bgp_notify_send(peer, BGP_NOTIFY_CEASE, + bgp_notify_send(connection, BGP_NOTIFY_CEASE, BGP_NOTIFY_SUBCODE_UNSPECIFIC); return BGP_Stop; } @@ -2374,8 +2802,7 @@ static int bgp_route_refresh_receive(struct peer *peer, bgp_size_t size) /* orf_len in bounds? */ if ((stream_pnt(s) + orf_len) > end) break; /* XXX: Notify instead?? */ - if (orf_type == ORF_TYPE_PREFIX - || orf_type == ORF_TYPE_PREFIX_OLD) { + if (orf_type == ORF_TYPE_PREFIX) { uint8_t *p_pnt = stream_pnt(s); uint8_t *p_end = stream_pnt(s) + orf_len; struct orf_prefix orfp; @@ -2400,7 +2827,8 @@ static int bgp_route_refresh_receive(struct peer *peer, bgp_size_t size) * and 7 bytes of ORF Address-filter entry from * the stream */ - if (*p_pnt & ORF_COMMON_PART_REMOVE_ALL) { + if (p_pnt < p_end && + *p_pnt & ORF_COMMON_PART_REMOVE_ALL) { if (bgp_debug_neighbor_events(peer)) zlog_debug( "%pBP rcvd Remove-All pfxlist ORF request", @@ -2594,7 +3022,7 @@ static int bgp_route_refresh_receive(struct peer *peer, bgp_size_t size) bgp_set_stale_route(peer, afi, safi); } - if (peer_established(peer)) + if (peer_established(peer->connection)) event_add_timer(bm->master, bgp_refresh_stalepath_timer_expire, paf, peer->bgp->stalepath_time, @@ -2679,6 +3107,604 @@ static int bgp_route_refresh_receive(struct peer *peer, bgp_size_t size) return BGP_PACKET_NOOP; } +static void bgp_dynamic_capability_addpath(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_ADDPATH_LEN) { + flog_warn(EC_BGP_CAPABILITY_INVALID_LENGTH, + "Add Path: Received invalid length %zu, non-multiple of 4", + len); + return; + } + + SET_FLAG(peer->cap, PEER_CAP_ADDPATH_RCV); + + while (data + CAPABILITY_CODE_ADDPATH_LEN <= end) { + afi_t afi; + safi_t safi; + iana_afi_t pkt_afi; + iana_safi_t pkt_safi; + struct bgp_addpath_capability bac; + + memcpy(&bac, data, sizeof(bac)); + pkt_afi = ntohs(bac.afi); + pkt_safi = safi_int2iana(bac.safi); + + /* If any other value (other than 1-3) is received, + * then the capability SHOULD be treated as not + * understood and ignored. + */ + if (!bac.flags || bac.flags > 3) { + flog_warn(EC_BGP_CAPABILITY_INVALID_LENGTH, + "Add Path: Received invalid send/receive value %u in Add Path capability", + bac.flags); + goto ignore; + } + + 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), + (bac.flags & BGP_ADDPATH_RX) + ? ", receive" + : "", + (bac.flags & BGP_ADDPATH_TX) + ? ", transmit" + : ""); + + 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)); + 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 AddPath capability for this AFI/SAFI", + peer->host, + iana_afi2str(pkt_afi), + iana_safi2str(pkt_safi)); + goto ignore; + } + + if (CHECK_FLAG(bac.flags, BGP_ADDPATH_RX)) + SET_FLAG(peer->af_cap[afi][safi], + PEER_CAP_ADDPATH_AF_RX_RCV); + else + UNSET_FLAG(peer->af_cap[afi][safi], + PEER_CAP_ADDPATH_AF_RX_RCV); + + if (CHECK_FLAG(bac.flags, BGP_ADDPATH_TX)) + SET_FLAG(peer->af_cap[afi][safi], + PEER_CAP_ADDPATH_AF_TX_RCV); + else + UNSET_FLAG(peer->af_cap[afi][safi], + PEER_CAP_ADDPATH_AF_TX_RCV); + +ignore: + data += CAPABILITY_CODE_ADDPATH_LEN; + } + } else { + FOREACH_AFI_SAFI (afi, safi) { + UNSET_FLAG(peer->af_cap[afi][safi], + PEER_CAP_ADDPATH_AF_RX_RCV); + UNSET_FLAG(peer->af_cap[afi][safi], + PEER_CAP_ADDPATH_AF_TX_RCV); + } + + UNSET_FLAG(peer->cap, PEER_CAP_ADDPATH_RCV); + } +} + +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) +{ + uint8_t *data = pnt + 3; + uint8_t *end = data + hdr->length; + size_t len = end - data; + + 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 = PEER_CAP_ORF_PREFIX_SM_RCV; + uint16_t rm_cap = PEER_CAP_ORF_PREFIX_RM_RCV; + int i; + + if (data + CAPABILITY_CODE_ORF_LEN > end) { + flog_warn(EC_BGP_CAPABILITY_INVALID_LENGTH, + "ORF: Received invalid length %zu, less than %d", len, + CAPABILITY_CODE_ORF_LEN); + return; + } + + /* ORF Entry header */ + memcpy(&mpc, data, sizeof(mpc)); + data += sizeof(mpc); + num = *data++; + pkt_afi = ntohs(mpc.afi); + pkt_safi = mpc.safi; + + /* Convert AFI, SAFI to internal values, check. */ + if (bgp_map_afi_safi_iana2int(pkt_afi, pkt_safi, &afi, &safi)) { + zlog_info("%pBP Addr-family %d/%d not supported. Ignoring the ORF capability", + peer, pkt_afi, pkt_safi); + return; + } + + /* validate number field */ + if (CAPABILITY_CODE_ORF_LEN + (num * 2) > hdr->length) { + zlog_info("%pBP ORF Capability entry length error, Cap length %u, num %u", + peer, hdr->length, num); + return; + } + + if (action == CAPABILITY_ACTION_UNSET) { + UNSET_FLAG(peer->af_cap[afi][safi], sm_cap); + UNSET_FLAG(peer->af_cap[afi][safi], rm_cap); + return; + } + + for (i = 0; i < num; i++) { + if (data + 1 > end) { + flog_err(EC_BGP_CAPABILITY_INVALID_LENGTH, + "%pBP ORF Capability entry length (type) error, Cap length %u, num %u", + peer, hdr->length, num); + return; + } + type = *data++; + + if (data + 1 > end) { + flog_err(EC_BGP_CAPABILITY_INVALID_LENGTH, + "%pBP ORF Capability entry length (mode) error, Cap length %u, num %u", + peer, hdr->length, num); + return; + } + mode = *data++; + + /* ORF Mode error check */ + switch (mode) { + case ORF_MODE_BOTH: + case ORF_MODE_SEND: + case ORF_MODE_RECEIVE: + break; + default: + if (bgp_debug_neighbor_events(peer)) + zlog_debug("%pBP Addr-family %d/%d has ORF type/mode %d/%d not supported", + peer, afi, safi, type, mode); + continue; + } + + if (!((afi == AFI_IP && safi == SAFI_UNICAST) || + (afi == AFI_IP && safi == SAFI_MULTICAST) || + (afi == AFI_IP6 && safi == SAFI_UNICAST))) { + if (bgp_debug_neighbor_events(peer)) + zlog_debug("%pBP Addr-family %d/%d unsupported AFI/SAFI received", + peer, afi, safi); + continue; + } + + if (bgp_debug_neighbor_events(peer)) + zlog_debug("%pBP OPEN has %s ORF capability as %s for afi/safi: %s/%s", + peer, lookup_msg(orf_type_str, type, NULL), + lookup_msg(orf_mode_str, mode, NULL), + iana_afi2str(pkt_afi), + iana_safi2str(pkt_safi)); + + 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); + UNSET_FLAG(peer->af_cap[afi][safi], rm_cap); + break; + case ORF_MODE_RECEIVE: + SET_FLAG(peer->af_cap[afi][safi], rm_cap); + UNSET_FLAG(peer->af_cap[afi][safi], sm_cap); + break; + } + } +} + +static void bgp_dynamic_capability_fqdn(uint8_t *pnt, int action, + struct capability_header *hdr, + struct peer *peer) +{ + uint8_t *data = pnt + 3; + uint8_t *end = data + hdr->length; + char str[BGP_MAX_HOSTNAME + 1] = {}; + uint8_t len; + + if (action == CAPABILITY_ACTION_SET) { + /* hostname */ + if (data + 1 > end) { + zlog_err("%pBP: Received invalid FQDN capability (host name length)", + peer); + return; + } + + len = *data; + if (data + len > end) { + zlog_err("%pBP: Received invalid FQDN capability length (host name) %d", + peer, hdr->length); + return; + } + data++; + + if (len > BGP_MAX_HOSTNAME) { + memcpy(&str, data, BGP_MAX_HOSTNAME); + str[BGP_MAX_HOSTNAME] = '\0'; + } else if (len) { + memcpy(&str, data, len); + str[len] = '\0'; + } + data += len; + + if (len) { + XFREE(MTYPE_BGP_PEER_HOST, peer->hostname); + XFREE(MTYPE_BGP_PEER_HOST, peer->domainname); + + peer->hostname = XSTRDUP(MTYPE_BGP_PEER_HOST, str); + } + + if (data + 1 > end) { + zlog_err("%pBP: Received invalid FQDN capability (domain name length)", + peer); + return; + } + + /* domainname */ + len = *data; + if (data + len > end) { + zlog_err("%pBP: Received invalid FQDN capability length (domain name) %d", + peer, len); + return; + } + data++; + + if (len > BGP_MAX_HOSTNAME) { + memcpy(&str, data, BGP_MAX_HOSTNAME); + str[BGP_MAX_HOSTNAME] = '\0'; + } else if (len) { + memcpy(&str, data, len); + str[len] = '\0'; + } + /* data += len; In case new code is ever added */ + + if (len) { + XFREE(MTYPE_BGP_PEER_HOST, peer->domainname); + + peer->domainname = XSTRDUP(MTYPE_BGP_PEER_HOST, str); + } + + SET_FLAG(peer->cap, PEER_CAP_HOSTNAME_RCV); + } else { + UNSET_FLAG(peer->cap, PEER_CAP_HOSTNAME_RCV); + XFREE(MTYPE_BGP_PEER_HOST, peer->hostname); + XFREE(MTYPE_BGP_PEER_HOST, peer->domainname); + } +} + +static void bgp_dynamic_capability_llgr(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 < BGP_CAP_LLGR_MIN_PACKET_LEN) { + zlog_err("%pBP: Received invalid Long-Lived Graceful-Restart capability length %zu", + peer, len); + return; + } + + SET_FLAG(peer->cap, PEER_CAP_LLGR_RCV); + + while (data + BGP_CAP_LLGR_MIN_PACKET_LEN <= end) { + afi_t afi; + safi_t safi; + iana_afi_t pkt_afi; + iana_safi_t pkt_safi; + struct graceful_restart_af graf; + + memcpy(&graf, data, sizeof(graf)); + pkt_afi = ntohs(graf.afi); + pkt_safi = safi_int2iana(graf.safi); + + /* Stale time is after AFI/SAFI/flags. + * It's encoded as 24 bits (= 3 bytes), so we need to + * put it into 32 bits. + */ + uint32_t stale_time; + uint8_t *stale_time_ptr = data + 4; + + stale_time = stale_time_ptr[0] << 16; + stale_time |= stale_time_ptr[1] << 8; + stale_time |= stale_time_ptr[2]; + + 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 = graf.flag; + 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); + } + + data += BGP_CAP_LLGR_MIN_PACKET_LEN; + } + } else { + FOREACH_AFI_SAFI (afi, safi) { + UNSET_FLAG(peer->af_cap[afi][safi], + PEER_CAP_LLGR_AF_RCV); + + peer->llgr[afi][safi].flags = 0; + peer->llgr[afi][safi].stale_time = + BGP_DEFAULT_LLGR_STALE_TIME; + } + + UNSET_FLAG(peer->cap, PEER_CAP_LLGR_RCV); + } +} + +static void bgp_dynamic_capability_graceful_restart(uint8_t *pnt, int action, + struct capability_header *hdr, + struct peer *peer) +{ +#define GRACEFUL_RESTART_CAPABILITY_PER_AFI_SAFI_SIZE 4 + uint16_t gr_restart_flag_time; + uint8_t *data = pnt + 3; + uint8_t *end = pnt + hdr->length; + size_t len = end - data; + afi_t afi; + safi_t safi; + + if (action == CAPABILITY_ACTION_SET) { + if (len < sizeof(gr_restart_flag_time)) { + zlog_err("%pBP: Received invalid Graceful-Restart capability length %d", + peer, hdr->length); + return; + } + + SET_FLAG(peer->cap, PEER_CAP_RESTART_RCV); + ptr_get_be16(data, &gr_restart_flag_time); + data += sizeof(gr_restart_flag_time); + + if (CHECK_FLAG(gr_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); + + if (CHECK_FLAG(gr_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(gr_restart_flag_time, 0xF000); + peer->v_gr_restart = gr_restart_flag_time; + + while (data + GRACEFUL_RESTART_CAPABILITY_PER_AFI_SAFI_SIZE <= + end) { + afi_t afi; + safi_t safi; + iana_afi_t pkt_afi; + iana_safi_t pkt_safi; + struct graceful_restart_af graf; + + memcpy(&graf, data, sizeof(graf)); + pkt_afi = ntohs(graf.afi); + pkt_safi = safi_int2iana(graf.safi); + + /* 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("%pBP: Addr-family %s/%s(afi/safi) not supported. Ignore the Graceful Restart capability for this AFI/SAFI", + peer, iana_afi2str(pkt_afi), + iana_safi2str(pkt_safi)); + } else if (!peer->afc[afi][safi]) { + if (bgp_debug_neighbor_events(peer)) + zlog_debug("%pBP: Addr-family %s/%s(afi/safi) not enabled. Ignore the Graceful Restart capability", + peer, iana_afi2str(pkt_afi), + iana_safi2str(pkt_safi)); + } else { + if (bgp_debug_neighbor_events(peer)) + zlog_debug("%pBP: Address family %s is%spreserved", + peer, + 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(graf.flag, + GRACEFUL_RESTART_F_BIT)) + SET_FLAG(peer->af_cap[afi][safi], + PEER_CAP_RESTART_AF_PRESERVE_RCV); + } + + data += GRACEFUL_RESTART_CAPABILITY_PER_AFI_SAFI_SIZE; + } + } else { + FOREACH_AFI_SAFI (afi, safi) { + UNSET_FLAG(peer->af_cap[afi][safi], + PEER_CAP_RESTART_AF_RCV); + UNSET_FLAG(peer->af_cap[afi][safi], + PEER_CAP_RESTART_AF_PRESERVE_RCV); + } + + UNSET_FLAG(peer->cap, PEER_CAP_GRACEFUL_RESTART_R_BIT_RCV); + UNSET_FLAG(peer->cap, PEER_CAP_GRACEFUL_RESTART_N_BIT_RCV); + UNSET_FLAG(peer->cap, PEER_CAP_RESTART_RCV); + } +} + +static void bgp_dynamic_capability_software_version(uint8_t *pnt, int action, + struct capability_header *hdr, + struct peer *peer) +{ + uint8_t *data = pnt + 3; + uint8_t *end = data + hdr->length; + uint8_t len = *data; + char soft_version[BGP_MAX_SOFT_VERSION + 1] = {}; + + if (action == CAPABILITY_ACTION_SET) { + if (data + len > end) { + zlog_err("%pBP: Received invalid Software Version capability length %d", + peer, len); + return; + } + data++; + + if (len > BGP_MAX_SOFT_VERSION) + len = BGP_MAX_SOFT_VERSION; + + memcpy(&soft_version, data, len); + soft_version[len] = '\0'; + + XFREE(MTYPE_BGP_SOFT_VERSION, peer->soft_version); + peer->soft_version = XSTRDUP(MTYPE_BGP_SOFT_VERSION, + soft_version); + + SET_FLAG(peer->cap, PEER_CAP_SOFT_VERSION_RCV); + } else { + UNSET_FLAG(peer->cap, PEER_CAP_SOFT_VERSION_RCV); + XFREE(MTYPE_BGP_SOFT_VERSION, peer->soft_version); + } +} + /** * Parse BGP CAPABILITY message for peer. * @@ -2697,6 +3723,7 @@ static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt, afi_t afi; iana_safi_t pkt_safi; safi_t safi; + const char *capability; end = pnt + length; @@ -2704,9 +3731,10 @@ static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt, /* We need at least action, capability code and capability * length. */ if (pnt + 3 > end) { - zlog_info("%s Capability length error", peer->host); - bgp_notify_send(peer, BGP_NOTIFY_CEASE, + zlog_err("%pBP: Capability length error", peer); + bgp_notify_send(peer->connection, BGP_NOTIFY_CEASE, BGP_NOTIFY_SUBCODE_UNSPECIFIC); + pnt += length; return BGP_Stop; } action = *pnt; @@ -2715,70 +3743,70 @@ static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt, /* Action value check. */ if (action != CAPABILITY_ACTION_SET && action != CAPABILITY_ACTION_UNSET) { - zlog_info("%s Capability Action Value error %d", - peer->host, action); - bgp_notify_send(peer, BGP_NOTIFY_CEASE, + zlog_err("%pBP: Capability Action Value error %d", peer, + action); + bgp_notify_send(peer->connection, BGP_NOTIFY_CEASE, BGP_NOTIFY_SUBCODE_UNSPECIFIC); - return BGP_Stop; + goto done; } if (bgp_debug_neighbor_events(peer)) - zlog_debug( - "%s CAPABILITY has action: %d, code: %u, length %u", - peer->host, action, hdr->code, hdr->length); - - if (hdr->length < sizeof(struct capability_mp_data)) { - zlog_info( - "%pBP Capability structure is not properly filled out, expected at least %zu bytes but header length specified is %d", - peer, sizeof(struct capability_mp_data), - hdr->length); - return BGP_Stop; - } + zlog_debug("%pBP: CAPABILITY has action: %d, code: %u, length %u", + peer, action, hdr->code, hdr->length); /* Capability length check. */ if ((pnt + hdr->length + 3) > end) { - zlog_info("%s Capability length error", peer->host); - bgp_notify_send(peer, BGP_NOTIFY_CEASE, + zlog_err("%pBP: Capability length error", peer); + bgp_notify_send(peer->connection, BGP_NOTIFY_CEASE, BGP_NOTIFY_SUBCODE_UNSPECIFIC); + pnt += length; return BGP_Stop; } - /* Fetch structure to the byte stream. */ - memcpy(&mpc, pnt + 3, sizeof(struct capability_mp_data)); - pnt += hdr->length + 3; + /* Ignore capability when override-capability is set. */ + if (CHECK_FLAG(peer->flags, PEER_FLAG_OVERRIDE_CAPABILITY)) + goto done; + + capability = lookup_msg(capcode_str, hdr->code, "Unknown"); - /* We know MP Capability Code. */ - if (hdr->code == CAPABILITY_CODE_MP) { + switch (hdr->code) { + case CAPABILITY_CODE_SOFT_VERSION: + bgp_dynamic_capability_software_version(pnt, action, + hdr, peer); + break; + case CAPABILITY_CODE_MP: + if (hdr->length < sizeof(struct capability_mp_data)) { + zlog_err("%pBP: Capability (%s) structure is not properly filled out, expected at least %zu bytes but header length specified is %d", + peer, capability, + sizeof(struct capability_mp_data), + hdr->length); + goto done; + } + + memcpy(&mpc, pnt + 3, sizeof(struct capability_mp_data)); pkt_afi = ntohs(mpc.afi); pkt_safi = mpc.safi; - /* Ignore capability when override-capability is set. */ - if (CHECK_FLAG(peer->flags, - PEER_FLAG_OVERRIDE_CAPABILITY)) - continue; - /* Convert AFI, SAFI to internal values. */ if (bgp_map_afi_safi_iana2int(pkt_afi, pkt_safi, &afi, &safi)) { if (bgp_debug_neighbor_events(peer)) - zlog_debug( - "%s Dynamic Capability MP_EXT afi/safi invalid (%s/%s)", - peer->host, - iana_afi2str(pkt_afi), - iana_safi2str(pkt_safi)); - continue; + zlog_debug("%pBP: Dynamic Capability %s afi/safi invalid (%s/%s)", + peer, capability, + iana_afi2str(pkt_afi), + iana_safi2str(pkt_safi)); + goto done; } /* Address family check. */ if (bgp_debug_neighbor_events(peer)) - zlog_debug( - "%s CAPABILITY has %s MP_EXT CAP for afi/safi: %s/%s", - peer->host, - action == CAPABILITY_ACTION_SET - ? "Advertising" - : "Removing", - iana_afi2str(pkt_afi), - iana_safi2str(pkt_safi)); + zlog_debug("%pBP: CAPABILITY has %s %s CAP for afi/safi: %s/%s", + peer, + action == CAPABILITY_ACTION_SET + ? "Advertising" + : "Removing", + capability, iana_afi2str(pkt_afi), + iana_safi2str(pkt_safi)); if (action == CAPABILITY_ACTION_SET) { peer->afc_recv[afi][safi] = 1; @@ -2794,14 +3822,76 @@ static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt, if (peer_active_nego(peer)) bgp_clear_route(peer, afi, safi); else - return BGP_Stop; + goto done; } - } else { - flog_warn( - EC_BGP_UNRECOGNIZED_CAPABILITY, - "%s unrecognized capability code: %d - ignored", - peer->host, hdr->code); + 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; + case CAPABILITY_CODE_LLGR: + bgp_dynamic_capability_llgr(pnt, action, hdr, peer); + break; + 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; + case CAPABILITY_CODE_FQDN: + bgp_dynamic_capability_fqdn(pnt, action, hdr, peer); + break; + case CAPABILITY_CODE_REFRESH: + case CAPABILITY_CODE_AS4: + case CAPABILITY_CODE_DYNAMIC: + case CAPABILITY_CODE_ENHANCED_RR: + case CAPABILITY_CODE_ENHE: + 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; + } + break; + default: + flog_warn(EC_BGP_UNRECOGNIZED_CAPABILITY, + "%pBP: unrecognized capability code: %d - ignored", + peer, hdr->code); + break; } + +done: + pnt += hdr->length + 3; } /* No FSM action necessary */ @@ -2817,7 +3907,8 @@ static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt, * @param size size of the packet * @return as in summary */ -int bgp_capability_receive(struct peer *peer, bgp_size_t size) +int bgp_capability_receive(struct peer_connection *connection, + struct peer *peer, bgp_size_t size) { uint8_t *pnt; @@ -2832,20 +3923,19 @@ int bgp_capability_receive(struct peer *peer, bgp_size_t size) flog_err(EC_BGP_NO_CAP, "%s [Error] BGP dynamic capability is not enabled", peer->host); - bgp_notify_send(peer, BGP_NOTIFY_HEADER_ERR, + bgp_notify_send(connection, BGP_NOTIFY_HEADER_ERR, BGP_NOTIFY_HEADER_BAD_MESTYPE); return BGP_Stop; } /* Status must be Established. */ - if (!peer_established(peer)) { - flog_err( - EC_BGP_NO_CAP, - "%s [Error] Dynamic capability packet received under status %s", - peer->host, - lookup_msg(bgp_status_msg, peer->status, NULL)); - bgp_notify_send(peer, BGP_NOTIFY_FSM_ERR, - bgp_fsm_error_subcode(peer->status)); + if (!peer_established(connection)) { + flog_err(EC_BGP_NO_CAP, + "%s [Error] Dynamic capability packet received under status %s", + peer->host, + lookup_msg(bgp_status_msg, connection->status, NULL)); + bgp_notify_send(connection, BGP_NOTIFY_FSM_ERR, + bgp_fsm_error_subcode(connection->status)); return BGP_Stop; } @@ -2871,17 +3961,19 @@ void bgp_process_packet(struct event *thread) { /* Yes first of all get peer pointer. */ struct peer *peer; // peer + struct peer_connection *connection; uint32_t rpkt_quanta_old; // how many packets to read int fsm_update_result; // return code of bgp_event_update() int mprc; // message processing return code - peer = EVENT_ARG(thread); + connection = EVENT_ARG(thread); + peer = connection->peer; rpkt_quanta_old = atomic_load_explicit(&peer->bgp->rpkt_quanta, memory_order_relaxed); fsm_update_result = 0; /* Guard against scheduled events that occur after peer deletion. */ - if (peer->status == Deleted || peer->status == Clearing) + if (connection->status == Deleted || connection->status == Clearing) return; unsigned int processed = 0; @@ -2891,8 +3983,8 @@ void bgp_process_packet(struct event *thread) bgp_size_t size; char notify_data_length[2]; - frr_with_mutex (&peer->io_mtx) { - peer->curr = stream_fifo_pop(peer->ibuf); + frr_with_mutex (&connection->io_mtx) { + peer->curr = stream_fifo_pop(connection->ibuf); } if (peer->curr == NULL) // no packets to process, hmm... @@ -2918,7 +4010,7 @@ void bgp_process_packet(struct event *thread) frrtrace(2, frr_bgp, open_process, peer, size); atomic_fetch_add_explicit(&peer->open_in, 1, memory_order_relaxed); - mprc = bgp_open_receive(peer, size); + mprc = bgp_open_receive(connection, peer, size); if (mprc == BGP_Stop) flog_err( EC_BGP_PKT_OPEN, @@ -2930,7 +4022,7 @@ void bgp_process_packet(struct event *thread) atomic_fetch_add_explicit(&peer->update_in, 1, memory_order_relaxed); peer->readtime = monotime(NULL); - mprc = bgp_update_receive(peer, size); + mprc = bgp_update_receive(connection, peer, size); if (mprc == BGP_Stop) flog_err( EC_BGP_UPDATE_RCV, @@ -2941,7 +4033,7 @@ void bgp_process_packet(struct event *thread) frrtrace(2, frr_bgp, notification_process, peer, size); atomic_fetch_add_explicit(&peer->notify_in, 1, memory_order_relaxed); - mprc = bgp_notify_receive(peer, size); + mprc = bgp_notify_receive(connection, peer, size); if (mprc == BGP_Stop) flog_err( EC_BGP_NOTIFY_RCV, @@ -2953,7 +4045,7 @@ void bgp_process_packet(struct event *thread) peer->readtime = monotime(NULL); atomic_fetch_add_explicit(&peer->keepalive_in, 1, memory_order_relaxed); - mprc = bgp_keepalive_receive(peer, size); + mprc = bgp_keepalive_receive(connection, peer, size); if (mprc == BGP_Stop) flog_err( EC_BGP_KEEP_RCV, @@ -2965,7 +4057,7 @@ void bgp_process_packet(struct event *thread) frrtrace(2, frr_bgp, refresh_process, peer, size); atomic_fetch_add_explicit(&peer->refresh_in, 1, memory_order_relaxed); - mprc = bgp_route_refresh_receive(peer, size); + mprc = bgp_route_refresh_receive(connection, peer, size); if (mprc == BGP_Stop) flog_err( EC_BGP_RFSH_RCV, @@ -2976,7 +4068,7 @@ void bgp_process_packet(struct event *thread) frrtrace(2, frr_bgp, capability_process, peer, size); atomic_fetch_add_explicit(&peer->dynamic_cap_in, 1, memory_order_relaxed); - mprc = bgp_capability_receive(peer, size); + mprc = bgp_capability_receive(connection, peer, size); if (mprc == BGP_Stop) flog_err( EC_BGP_CAP_RCV, @@ -3003,7 +4095,7 @@ void bgp_process_packet(struct event *thread) /* Update FSM */ if (mprc != BGP_PACKET_NOOP) - fsm_update_result = bgp_event_update(peer, mprc); + fsm_update_result = bgp_event_update(connection, mprc); else continue; @@ -3018,12 +4110,12 @@ void bgp_process_packet(struct event *thread) if (fsm_update_result != FSM_PEER_TRANSFERRED && fsm_update_result != FSM_PEER_STOPPED) { - frr_with_mutex (&peer->io_mtx) { + frr_with_mutex (&connection->io_mtx) { // more work to do, come back later - if (peer->ibuf->count > 0) + if (connection->ibuf->count > 0) event_add_event(bm->master, bgp_process_packet, - peer, 0, - &peer->t_process_packet); + connection, 0, + &connection->t_process_packet); } } } @@ -3046,18 +4138,20 @@ void bgp_send_delayed_eor(struct bgp *bgp) */ void bgp_packet_process_error(struct event *thread) { + struct peer_connection *connection; struct peer *peer; int code; - peer = EVENT_ARG(thread); + connection = EVENT_ARG(thread); + peer = connection->peer; code = EVENT_VAL(thread); if (bgp_debug_neighbor_events(peer)) - zlog_debug("%s [Event] BGP error %d on fd %d", - peer->host, code, peer->fd); + zlog_debug("%s [Event] BGP error %d on fd %d", peer->host, code, + connection->fd); /* Closed connection or error on the socket */ - if (peer_established(peer)) { + if (peer_established(connection)) { if ((CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART) || CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART_HELPER)) @@ -3068,5 +4162,5 @@ void bgp_packet_process_error(struct event *thread) peer->last_reset = PEER_DOWN_CLOSE_SESSION; } - bgp_event_update(peer, code); + bgp_event_update(connection, code); } |
