diff options
Diffstat (limited to 'bgpd/bgp_packet.c')
| -rw-r--r-- | bgpd/bgp_packet.c | 4124 |
1 files changed, 2059 insertions, 2065 deletions
diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index b1fa50974b..f6a1c417b2 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -27,7 +27,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "command.h" #include "log.h" #include "memory.h" -#include "sockunion.h" /* for inet_ntop () */ +#include "sockunion.h" /* for inet_ntop () */ #include "sockopt.h" #include "linklist.h" #include "plist.h" @@ -55,2286 +55,2280 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_updgrp.h" /* Set up BGP packet marker and packet type. */ -int -bgp_packet_set_marker (struct stream *s, u_char type) +int bgp_packet_set_marker(struct stream *s, u_char type) { - int i; + int i; - /* Fill in marker. */ - for (i = 0; i < BGP_MARKER_SIZE; i++) - stream_putc (s, 0xff); + /* Fill in marker. */ + for (i = 0; i < BGP_MARKER_SIZE; i++) + stream_putc(s, 0xff); - /* Dummy total length. This field is should be filled in later on. */ - stream_putw (s, 0); + /* Dummy total length. This field is should be filled in later on. */ + stream_putw(s, 0); - /* BGP packet type. */ - stream_putc (s, type); + /* BGP packet type. */ + stream_putc(s, type); - /* Return current stream size. */ - return stream_get_endp (s); + /* Return current stream size. */ + return stream_get_endp(s); } /* Set BGP packet header size entry. If size is zero then use current stream size. */ -int -bgp_packet_set_size (struct stream *s) +int bgp_packet_set_size(struct stream *s) { - int cp; + int cp; - /* Preserve current pointer. */ - cp = stream_get_endp (s); - stream_putw_at (s, BGP_MARKER_SIZE, cp); + /* Preserve current pointer. */ + cp = stream_get_endp(s); + stream_putw_at(s, BGP_MARKER_SIZE, cp); - return cp; + return cp; } /* Add new packet to the peer. */ -void -bgp_packet_add (struct peer *peer, struct stream *s) +void bgp_packet_add(struct peer *peer, struct stream *s) { - /* Add packet to the end of list. */ - stream_fifo_push (peer->obuf, s); + /* Add packet to the end of list. */ + stream_fifo_push(peer->obuf, s); } /* Free first packet. */ -static void -bgp_packet_delete (struct peer *peer) +static void bgp_packet_delete(struct peer *peer) { - stream_free (stream_fifo_pop (peer->obuf)); + stream_free(stream_fifo_pop(peer->obuf)); } /* Check file descriptor whether connect is established. */ -int -bgp_connect_check (struct peer *peer, int change_state) +int bgp_connect_check(struct peer *peer, int change_state) { - int status; - socklen_t slen; - int ret; - - /* Anyway I have to reset read and write thread. */ - BGP_READ_OFF (peer->t_read); - BGP_WRITE_OFF (peer->t_write); - - /* Check file descriptor. */ - slen = sizeof (status); - ret = getsockopt(peer->fd, SOL_SOCKET, SO_ERROR, (void *) &status, &slen); - - /* If getsockopt is fail, this is fatal error. */ - if (ret < 0) - { - zlog_info ("can't get sockopt for nonblocking connect"); - BGP_EVENT_ADD (peer, TCP_fatal_error); - return -1; - } - - /* When status is 0 then TCP connection is established. */ - if (status == 0) - { - BGP_EVENT_ADD (peer, TCP_connection_open); - return 1; - } - else - { - if (bgp_debug_neighbor_events(peer)) - zlog_debug ("%s [Event] Connect failed (%s)", - peer->host, safe_strerror (errno)); - if (change_state) - BGP_EVENT_ADD (peer, TCP_connection_open_failed); - return 0; - } + int status; + socklen_t slen; + int ret; + + /* Anyway I have to reset read and write thread. */ + BGP_READ_OFF(peer->t_read); + BGP_WRITE_OFF(peer->t_write); + + /* Check file descriptor. */ + slen = sizeof(status); + ret = getsockopt(peer->fd, SOL_SOCKET, SO_ERROR, (void *)&status, + &slen); + + /* If getsockopt is fail, this is fatal error. */ + if (ret < 0) { + zlog_info("can't get sockopt for nonblocking connect"); + BGP_EVENT_ADD(peer, TCP_fatal_error); + return -1; + } + + /* When status is 0 then TCP connection is established. */ + if (status == 0) { + BGP_EVENT_ADD(peer, TCP_connection_open); + return 1; + } else { + if (bgp_debug_neighbor_events(peer)) + zlog_debug("%s [Event] Connect failed (%s)", peer->host, + safe_strerror(errno)); + if (change_state) + BGP_EVENT_ADD(peer, TCP_connection_open_failed); + return 0; + } } -static struct stream * -bgp_update_packet_eor (struct peer *peer, afi_t afi, safi_t safi) +static struct stream *bgp_update_packet_eor(struct peer *peer, afi_t afi, + safi_t safi) { - struct stream *s; - iana_afi_t pkt_afi; - safi_t pkt_safi; - - if (DISABLE_BGP_ANNOUNCE) - return NULL; - - if (bgp_debug_neighbor_events(peer)) - zlog_debug ("send End-of-RIB for %s to %s", afi_safi_print (afi, safi), peer->host); - - s = stream_new (BGP_MAX_PACKET_SIZE); - - /* Make BGP update packet. */ - bgp_packet_set_marker (s, BGP_MSG_UPDATE); - - /* Unfeasible Routes Length */ - stream_putw (s, 0); - - if (afi == AFI_IP && safi == SAFI_UNICAST) - { - /* Total Path Attribute Length */ - stream_putw (s, 0); - } - else - { - /* Convert AFI, SAFI to values for packet. */ - bgp_map_afi_safi_int2iana (afi, safi, &pkt_afi, &pkt_safi); - - /* Total Path Attribute Length */ - stream_putw (s, 6); - stream_putc (s, BGP_ATTR_FLAG_OPTIONAL); - stream_putc (s, BGP_ATTR_MP_UNREACH_NLRI); - stream_putc (s, 3); - stream_putw (s, pkt_afi); - stream_putc (s, pkt_safi); - } - - bgp_packet_set_size (s); - bgp_packet_add (peer, s); - return s; + struct stream *s; + iana_afi_t pkt_afi; + safi_t pkt_safi; + + if (DISABLE_BGP_ANNOUNCE) + return NULL; + + if (bgp_debug_neighbor_events(peer)) + zlog_debug("send End-of-RIB for %s to %s", + afi_safi_print(afi, safi), peer->host); + + s = stream_new(BGP_MAX_PACKET_SIZE); + + /* Make BGP update packet. */ + bgp_packet_set_marker(s, BGP_MSG_UPDATE); + + /* Unfeasible Routes Length */ + stream_putw(s, 0); + + if (afi == AFI_IP && safi == SAFI_UNICAST) { + /* Total Path Attribute Length */ + stream_putw(s, 0); + } else { + /* Convert AFI, SAFI to values for packet. */ + bgp_map_afi_safi_int2iana(afi, safi, &pkt_afi, &pkt_safi); + + /* Total Path Attribute Length */ + stream_putw(s, 6); + stream_putc(s, BGP_ATTR_FLAG_OPTIONAL); + stream_putc(s, BGP_ATTR_MP_UNREACH_NLRI); + stream_putc(s, 3); + stream_putw(s, pkt_afi); + stream_putc(s, pkt_safi); + } + + bgp_packet_set_size(s); + bgp_packet_add(peer, s); + return s; } /* Get next packet to be written. */ -static struct stream * -bgp_write_packet (struct peer *peer) +static struct stream *bgp_write_packet(struct peer *peer) { - struct stream *s = NULL; - struct peer_af *paf; - struct bpacket *next_pkt; - afi_t afi; - safi_t safi; - - s = stream_fifo_head (peer->obuf); - if (s) - return s; - - /* - * The code beyond this part deals with update packets, proceed only - * if peer is Established and updates are not on hold (as part of - * update-delay post processing). - */ - if (peer->status != Established) - return NULL; - - if (peer->bgp && peer->bgp->main_peers_update_hold) - return NULL; - - for (afi = AFI_IP; afi < AFI_MAX; afi++) - for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) - { - paf = peer_af_find (peer, afi, safi); - if (!paf || !PAF_SUBGRP(paf)) - continue; - next_pkt = paf->next_pkt_to_send; - - /* Try to generate a packet for the peer if we are at the end of - * the list. Always try to push out WITHDRAWs first. */ - if (!next_pkt || !next_pkt->buffer) - { - next_pkt = subgroup_withdraw_packet(PAF_SUBGRP(paf)); - if (!next_pkt || !next_pkt->buffer) - subgroup_update_packet (PAF_SUBGRP(paf)); - next_pkt = paf->next_pkt_to_send; - } - - /* If we still don't have a packet to send to the peer, then - * try to find out out if we have to send eor or if not, skip to - * the next AFI, SAFI. - * Don't send the EOR prematurely... if the subgroup's coalesce - * timer is running, the adjacency-out structure is not created - * yet. + struct stream *s = NULL; + struct peer_af *paf; + struct bpacket *next_pkt; + afi_t afi; + safi_t safi; + + s = stream_fifo_head(peer->obuf); + if (s) + return s; + + /* + * The code beyond this part deals with update packets, proceed only + * if peer is Established and updates are not on hold (as part of + * update-delay post processing). */ - if (!next_pkt || !next_pkt->buffer) - { - 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) - && safi != SAFI_EVPN) - { - SET_FLAG (peer->af_sflags[afi][safi], - PEER_STATUS_EOR_SEND); - return bgp_update_packet_eor (peer, afi, safi); - } - } - continue; - } - - - /* - * Found a packet template to send, overwrite packet with appropriate - * attributes from peer and advance peer - */ - s = bpacket_reformat_for_peer (next_pkt, paf); - bpacket_queue_advance_peer (paf); - return s; - } + if (peer->status != Established) + return NULL; + + if (peer->bgp && peer->bgp->main_peers_update_hold) + return NULL; + + for (afi = AFI_IP; afi < AFI_MAX; afi++) + for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) { + paf = peer_af_find(peer, afi, safi); + if (!paf || !PAF_SUBGRP(paf)) + continue; + next_pkt = paf->next_pkt_to_send; + + /* Try to generate a packet for the peer if we are at + * the end of + * the list. Always try to push out WITHDRAWs first. */ + if (!next_pkt || !next_pkt->buffer) { + next_pkt = subgroup_withdraw_packet( + PAF_SUBGRP(paf)); + if (!next_pkt || !next_pkt->buffer) + subgroup_update_packet(PAF_SUBGRP(paf)); + next_pkt = paf->next_pkt_to_send; + } + + /* If we still don't have a packet to send to the peer, + * then + * try to find out out if we have to send eor or if not, + * skip to + * the next AFI, SAFI. + * Don't send the EOR prematurely... if the subgroup's + * coalesce + * timer is running, the adjacency-out structure is not + * created + * yet. + */ + if (!next_pkt || !next_pkt->buffer) { + 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) + && safi != SAFI_EVPN) { + SET_FLAG(peer->af_sflags[afi] + [safi], + PEER_STATUS_EOR_SEND); + return bgp_update_packet_eor( + peer, afi, safi); + } + } + continue; + } + + + /* + * Found a packet template to send, overwrite packet + * with appropriate + * attributes from peer and advance peer + */ + s = bpacket_reformat_for_peer(next_pkt, paf); + bpacket_queue_advance_peer(paf); + return s; + } - return NULL; + return NULL; } /* The next action for the peer from a write perspective */ -static void -bgp_write_proceed_actions (struct peer *peer) +static void bgp_write_proceed_actions(struct peer *peer) { - afi_t afi; - safi_t safi; - struct peer_af *paf; - struct bpacket *next_pkt; - int fullq_found = 0; - struct update_subgroup *subgrp; - - if (stream_fifo_head (peer->obuf)) - { - BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); - return; - } - - for (afi = AFI_IP; afi < AFI_MAX; afi++) - for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) - { - paf = peer_af_find (peer, afi, safi); - if (!paf) - continue; - subgrp = paf->subgroup; - if (!subgrp) - continue; - - next_pkt = paf->next_pkt_to_send; - if (next_pkt && next_pkt->buffer) - { - BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); - return; - } - - /* No packets readily available for AFI/SAFI, are there subgroup packets - * that need to be generated? */ - if (bpacket_queue_is_full(SUBGRP_INST(subgrp), - SUBGRP_PKTQ(subgrp))) - fullq_found = 1; - else if (subgroup_packets_to_build (subgrp)) - { - BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); - return; - } - - /* No packets to send, see if EOR is pending */ - if (CHECK_FLAG (peer->cap, PEER_CAP_RESTART_RCV)) - { - if (!subgrp->t_coalesce && - peer->afc_nego[afi][safi] && - peer->synctime && - !CHECK_FLAG(peer->af_sflags[afi][safi], - PEER_STATUS_EOR_SEND) && - safi != SAFI_MPLS_VPN) - { - BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); - return; - } - - } - } - if (fullq_found) - { - BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); - return; - } + afi_t afi; + safi_t safi; + struct peer_af *paf; + struct bpacket *next_pkt; + int fullq_found = 0; + struct update_subgroup *subgrp; + + if (stream_fifo_head(peer->obuf)) { + BGP_WRITE_ON(peer->t_write, bgp_write, peer->fd); + return; + } + + for (afi = AFI_IP; afi < AFI_MAX; afi++) + for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) { + paf = peer_af_find(peer, afi, safi); + if (!paf) + continue; + subgrp = paf->subgroup; + if (!subgrp) + continue; + + next_pkt = paf->next_pkt_to_send; + if (next_pkt && next_pkt->buffer) { + BGP_WRITE_ON(peer->t_write, bgp_write, + peer->fd); + return; + } + + /* No packets readily available for AFI/SAFI, are there + * subgroup packets + * that need to be generated? */ + if (bpacket_queue_is_full(SUBGRP_INST(subgrp), + SUBGRP_PKTQ(subgrp))) + fullq_found = 1; + else if (subgroup_packets_to_build(subgrp)) { + BGP_WRITE_ON(peer->t_write, bgp_write, + peer->fd); + return; + } + + /* No packets to send, see if EOR is pending */ + if (CHECK_FLAG(peer->cap, PEER_CAP_RESTART_RCV)) { + if (!subgrp->t_coalesce + && peer->afc_nego[afi][safi] + && peer->synctime + && !CHECK_FLAG(peer->af_sflags[afi][safi], + PEER_STATUS_EOR_SEND) + && safi != SAFI_MPLS_VPN) { + BGP_WRITE_ON(peer->t_write, bgp_write, + peer->fd); + return; + } + } + } + if (fullq_found) { + BGP_WRITE_ON(peer->t_write, bgp_write, peer->fd); + return; + } } /* Write packet to the peer. */ -int -bgp_write (struct thread *thread) +int bgp_write(struct thread *thread) { - struct peer *peer; - u_char type; - struct stream *s; - int num; - int update_last_write = 0; - unsigned int count = 0; - unsigned int oc = 0; - - /* Yes first of all get peer pointer. */ - peer = THREAD_ARG (thread); - peer->t_write = NULL; - - /* For non-blocking IO check. */ - if (peer->status == Connect) - { - bgp_connect_check (peer, 1); - return 0; - } - - s = bgp_write_packet (peer); - if (!s) - { - bgp_write_proceed_actions (peer); - return 0; - } - - sockopt_cork (peer->fd, 1); - - oc = peer->update_out; - - /* Nonblocking write until TCP output buffer is full. */ - do - { - int writenum; - - /* Number of bytes to be sent. */ - writenum = stream_get_endp (s) - stream_get_getp (s); - - /* Call write() system call. */ - num = write (peer->fd, STREAM_PNT (s), writenum); - if (num < 0) - { - /* write failed either retry needed or error */ - if (ERRNO_IO_RETRY(errno)) - break; - - BGP_EVENT_ADD (peer, TCP_fatal_error); - return 0; + struct peer *peer; + u_char type; + struct stream *s; + int num; + int update_last_write = 0; + unsigned int count = 0; + unsigned int oc = 0; + + /* Yes first of all get peer pointer. */ + peer = THREAD_ARG(thread); + peer->t_write = NULL; + + /* For non-blocking IO check. */ + if (peer->status == Connect) { + bgp_connect_check(peer, 1); + return 0; } - if (num != writenum) - { - /* Partial write */ - stream_forward_getp (s, num); - break; + s = bgp_write_packet(peer); + if (!s) { + bgp_write_proceed_actions(peer); + return 0; } - /* Retrieve BGP packet type. */ - stream_set_getp (s, BGP_MARKER_SIZE + 2); - type = stream_getc (s); + sockopt_cork(peer->fd, 1); - switch (type) - { - case BGP_MSG_OPEN: - peer->open_out++; - break; - case BGP_MSG_UPDATE: - peer->update_out++; - break; - case BGP_MSG_NOTIFY: - peer->notify_out++; - /* Double start timer. */ - peer->v_start *= 2; + oc = peer->update_out; - /* Overflow check. */ - if (peer->v_start >= (60 * 2)) - peer->v_start = (60 * 2); + /* Nonblocking write until TCP output buffer is full. */ + do { + int writenum; - /* Flush any existing events */ - BGP_EVENT_ADD (peer, BGP_Stop); - goto done; - - case BGP_MSG_KEEPALIVE: - peer->keepalive_out++; - break; - case BGP_MSG_ROUTE_REFRESH_NEW: - case BGP_MSG_ROUTE_REFRESH_OLD: - peer->refresh_out++; - break; - case BGP_MSG_CAPABILITY: - peer->dynamic_cap_out++; - break; - } + /* Number of bytes to be sent. */ + writenum = stream_get_endp(s) - stream_get_getp(s); - /* OK we send packet so delete it. */ - bgp_packet_delete (peer); - update_last_write = 1; - } - while (++count < peer->bgp->wpkt_quanta && - (s = bgp_write_packet (peer)) != NULL); + /* Call write() system call. */ + num = write(peer->fd, STREAM_PNT(s), writenum); + if (num < 0) { + /* write failed either retry needed or error */ + if (ERRNO_IO_RETRY(errno)) + break; - bgp_write_proceed_actions (peer); + BGP_EVENT_ADD(peer, TCP_fatal_error); + return 0; + } - done: - /* Update last_update if UPDATEs were written. */ - if (peer->update_out > oc) - peer->last_update = bgp_clock (); + if (num != writenum) { + /* Partial write */ + stream_forward_getp(s, num); + break; + } - /* If we TXed any flavor of packet update last_write */ - if (update_last_write) - peer->last_write = bgp_clock (); + /* Retrieve BGP packet type. */ + stream_set_getp(s, BGP_MARKER_SIZE + 2); + type = stream_getc(s); + + switch (type) { + case BGP_MSG_OPEN: + peer->open_out++; + break; + case BGP_MSG_UPDATE: + peer->update_out++; + break; + case BGP_MSG_NOTIFY: + peer->notify_out++; + /* Double start timer. */ + peer->v_start *= 2; + + /* Overflow check. */ + if (peer->v_start >= (60 * 2)) + peer->v_start = (60 * 2); + + /* Flush any existing events */ + BGP_EVENT_ADD(peer, BGP_Stop); + goto done; + + case BGP_MSG_KEEPALIVE: + peer->keepalive_out++; + break; + case BGP_MSG_ROUTE_REFRESH_NEW: + case BGP_MSG_ROUTE_REFRESH_OLD: + peer->refresh_out++; + break; + case BGP_MSG_CAPABILITY: + peer->dynamic_cap_out++; + break; + } - sockopt_cork (peer->fd, 0); - return 0; -} + /* OK we send packet so delete it. */ + bgp_packet_delete(peer); + update_last_write = 1; + } while (++count < peer->bgp->wpkt_quanta + && (s = bgp_write_packet(peer)) != NULL); -/* This is only for sending NOTIFICATION message to neighbor. */ -static int -bgp_write_notify (struct peer *peer) -{ - int ret, val; - u_char type; - struct stream *s; + bgp_write_proceed_actions(peer); - /* There should be at least one packet. */ - s = stream_fifo_head (peer->obuf); - if (!s) - return 0; - assert (stream_get_endp (s) >= BGP_HEADER_SIZE); +done: + /* Update last_update if UPDATEs were written. */ + if (peer->update_out > oc) + peer->last_update = bgp_clock(); - /* Stop collecting data within the socket */ - sockopt_cork (peer->fd, 0); + /* If we TXed any flavor of packet update last_write */ + if (update_last_write) + peer->last_write = bgp_clock(); - /* 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)); + sockopt_cork(peer->fd, 0); + return 0; +} - /* only connection reset/close gets counted as TCP_fatal_error, failure - * to write the entire NOTIFY doesn't get different FSM treatment */ - if (ret <= 0) - { - BGP_EVENT_ADD (peer, TCP_fatal_error); - return 0; - } +/* This is only for sending NOTIFICATION message to neighbor. */ +static int bgp_write_notify(struct peer *peer) +{ + int ret, val; + u_char type; + struct stream *s; + + /* There should be at least one packet. */ + s = stream_fifo_head(peer->obuf); + if (!s) + return 0; + assert(stream_get_endp(s) >= BGP_HEADER_SIZE); + + /* Stop collecting data within the socket */ + sockopt_cork(peer->fd, 0); + + /* 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)); + + /* only connection reset/close gets counted as TCP_fatal_error, failure + * to write the entire NOTIFY doesn't get different FSM treatment */ + if (ret <= 0) { + BGP_EVENT_ADD(peer, TCP_fatal_error); + return 0; + } - /* Disable Nagle, make NOTIFY packet go out right away */ - val = 1; - (void) setsockopt (peer->fd, IPPROTO_TCP, TCP_NODELAY, - (char *) &val, sizeof (val)); + /* Disable Nagle, make NOTIFY packet go out right away */ + val = 1; + (void)setsockopt(peer->fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, + sizeof(val)); - /* Retrieve BGP packet type. */ - stream_set_getp (s, BGP_MARKER_SIZE + 2); - type = stream_getc (s); + /* Retrieve BGP packet type. */ + stream_set_getp(s, BGP_MARKER_SIZE + 2); + type = stream_getc(s); - assert (type == BGP_MSG_NOTIFY); + assert(type == BGP_MSG_NOTIFY); - /* Type should be notify. */ - peer->notify_out++; + /* Type should be notify. */ + peer->notify_out++; - /* Double start timer. */ - peer->v_start *= 2; + /* Double start timer. */ + peer->v_start *= 2; - /* Overflow check. */ - if (peer->v_start >= (60 * 2)) - peer->v_start = (60 * 2); + /* Overflow check. */ + if (peer->v_start >= (60 * 2)) + peer->v_start = (60 * 2); - /* Handle Graceful Restart case where the state changes to - Connect instead of Idle */ - BGP_EVENT_ADD (peer, BGP_Stop); + /* Handle Graceful Restart case where the state changes to + Connect instead of Idle */ + BGP_EVENT_ADD(peer, BGP_Stop); - return 0; + return 0; } /* Make keepalive packet and send it to the peer. */ -void -bgp_keepalive_send (struct peer *peer) +void bgp_keepalive_send(struct peer *peer) { - struct stream *s; + struct stream *s; + + s = stream_new(BGP_MAX_PACKET_SIZE); - s = stream_new (BGP_MAX_PACKET_SIZE); + /* Make keepalive packet. */ + bgp_packet_set_marker(s, BGP_MSG_KEEPALIVE); - /* Make keepalive packet. */ - bgp_packet_set_marker (s, BGP_MSG_KEEPALIVE); + /* Set packet size. */ + (void)bgp_packet_set_size(s); - /* Set packet size. */ - (void)bgp_packet_set_size (s); + /* Dump packet if debug option is set. */ + /* bgp_packet_dump (s); */ - /* Dump packet if debug option is set. */ - /* bgp_packet_dump (s); */ - - if (bgp_debug_keepalive(peer)) - zlog_debug ("%s sending KEEPALIVE", peer->host); + if (bgp_debug_keepalive(peer)) + zlog_debug("%s sending KEEPALIVE", peer->host); - /* Add packet to the peer. */ - bgp_packet_add (peer, s); + /* Add packet to the peer. */ + bgp_packet_add(peer, s); - BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); + BGP_WRITE_ON(peer->t_write, bgp_write, peer->fd); } /* Make open packet and send it to the peer. */ -void -bgp_open_send (struct peer *peer) +void bgp_open_send(struct peer *peer) { - struct stream *s; - u_int16_t send_holdtime; - as_t local_as; + struct stream *s; + u_int16_t send_holdtime; + as_t local_as; - if (CHECK_FLAG (peer->config, PEER_CONFIG_TIMER)) - send_holdtime = peer->holdtime; - else - send_holdtime = peer->bgp->default_holdtime; + if (CHECK_FLAG(peer->config, PEER_CONFIG_TIMER)) + send_holdtime = peer->holdtime; + else + send_holdtime = peer->bgp->default_holdtime; - /* local-as Change */ - if (peer->change_local_as) - local_as = peer->change_local_as; - else - local_as = peer->local_as; + /* local-as Change */ + if (peer->change_local_as) + local_as = peer->change_local_as; + else + local_as = peer->local_as; - s = stream_new (BGP_MAX_PACKET_SIZE); + s = stream_new(BGP_MAX_PACKET_SIZE); - /* Make open packet. */ - bgp_packet_set_marker (s, BGP_MSG_OPEN); + /* Make open packet. */ + bgp_packet_set_marker(s, BGP_MSG_OPEN); - /* Set open packet values. */ - stream_putc (s, BGP_VERSION_4); /* BGP version */ - stream_putw (s, (local_as <= BGP_AS_MAX) ? (u_int16_t) local_as - : BGP_AS_TRANS); - stream_putw (s, send_holdtime); /* Hold Time */ - stream_put_in_addr (s, &peer->local_id); /* BGP Identifier */ + /* Set open packet values. */ + stream_putc(s, BGP_VERSION_4); /* BGP version */ + stream_putw(s, + (local_as <= BGP_AS_MAX) ? (u_int16_t)local_as + : BGP_AS_TRANS); + stream_putw(s, send_holdtime); /* Hold Time */ + stream_put_in_addr(s, &peer->local_id); /* BGP Identifier */ - /* Set capability code. */ - bgp_open_capability (s, peer); + /* Set capability code. */ + bgp_open_capability(s, peer); - /* Set BGP packet length. */ - (void)bgp_packet_set_size (s); + /* Set BGP packet length. */ + (void)bgp_packet_set_size(s); - if (bgp_debug_neighbor_events(peer)) - zlog_debug ("%s sending OPEN, version %d, my as %u, holdtime %d, id %s", - peer->host, BGP_VERSION_4, local_as, - send_holdtime, inet_ntoa (peer->local_id)); + if (bgp_debug_neighbor_events(peer)) + zlog_debug( + "%s sending OPEN, version %d, my as %u, holdtime %d, id %s", + peer->host, BGP_VERSION_4, local_as, send_holdtime, + inet_ntoa(peer->local_id)); - /* Dump packet if debug option is set. */ - /* bgp_packet_dump (s); */ + /* Dump packet if debug option is set. */ + /* bgp_packet_dump (s); */ - /* Add packet to the peer. */ - bgp_packet_add (peer, s); + /* Add packet to the peer. */ + bgp_packet_add(peer, s); - BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); + BGP_WRITE_ON(peer->t_write, bgp_write, peer->fd); } /* Send BGP notify packet with data potion. */ -void -bgp_notify_send_with_data (struct peer *peer, u_char code, u_char sub_code, - u_char *data, size_t datalen) +void bgp_notify_send_with_data(struct peer *peer, u_char code, u_char sub_code, + u_char *data, size_t datalen) { - struct stream *s; - int length; - - /* Allocate new stream. */ - s = stream_new (BGP_MAX_PACKET_SIZE); - - /* Make nitify packet. */ - bgp_packet_set_marker (s, BGP_MSG_NOTIFY); - - /* Set notify packet values. */ - stream_putc (s, code); /* BGP notify code */ - stream_putc (s, sub_code); /* BGP notify sub_code */ - - /* If notify data is present. */ - if (data) - stream_write (s, data, datalen); - - /* Set BGP packet length. */ - length = bgp_packet_set_size (s); - - /* Add packet to the peer. */ - stream_fifo_clean (peer->obuf); - bgp_packet_add (peer, s); - - /* For debug */ - { - struct bgp_notify bgp_notify; - int first = 0; - int i; - char c[4]; - - bgp_notify.code = code; - bgp_notify.subcode = sub_code; - bgp_notify.data = NULL; - bgp_notify.length = length - BGP_MSG_NOTIFY_MIN_SIZE; - bgp_notify.raw_data = data; - - peer->notify.code = bgp_notify.code; - peer->notify.subcode = bgp_notify.subcode; - - if (bgp_notify.length) - { - bgp_notify.data = XMALLOC (MTYPE_TMP, bgp_notify.length * 3); - for (i = 0; i < bgp_notify.length; i++) - if (first) - { - sprintf (c, " %02x", data[i]); - strcat (bgp_notify.data, c); - } - else - { - first = 1; - sprintf (c, "%02x", data[i]); - strcpy (bgp_notify.data, c); - } - } - bgp_notify_print (peer, &bgp_notify, "sending"); - - if (bgp_notify.data) - { - XFREE (MTYPE_TMP, bgp_notify.data); - bgp_notify.data = NULL; - bgp_notify.length = 0; - } - } - - /* peer reset cause */ - if (code == BGP_NOTIFY_CEASE) - { - if (sub_code == BGP_NOTIFY_CEASE_ADMIN_RESET) - peer->last_reset = PEER_DOWN_USER_RESET; - else if (sub_code == BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN) - peer->last_reset = PEER_DOWN_USER_SHUTDOWN; - else - peer->last_reset = PEER_DOWN_NOTIFY_SEND; - } - else - peer->last_reset = PEER_DOWN_NOTIFY_SEND; - - /* Call immediately. */ - BGP_WRITE_OFF (peer->t_write); - - bgp_write_notify (peer); + struct stream *s; + int length; + + /* Allocate new stream. */ + s = stream_new(BGP_MAX_PACKET_SIZE); + + /* Make nitify packet. */ + bgp_packet_set_marker(s, BGP_MSG_NOTIFY); + + /* Set notify packet values. */ + stream_putc(s, code); /* BGP notify code */ + stream_putc(s, sub_code); /* BGP notify sub_code */ + + /* If notify data is present. */ + if (data) + stream_write(s, data, datalen); + + /* Set BGP packet length. */ + length = bgp_packet_set_size(s); + + /* Add packet to the peer. */ + stream_fifo_clean(peer->obuf); + bgp_packet_add(peer, s); + + /* For debug */ + { + struct bgp_notify bgp_notify; + int first = 0; + int i; + char c[4]; + + bgp_notify.code = code; + bgp_notify.subcode = sub_code; + bgp_notify.data = NULL; + bgp_notify.length = length - BGP_MSG_NOTIFY_MIN_SIZE; + bgp_notify.raw_data = data; + + peer->notify.code = bgp_notify.code; + peer->notify.subcode = bgp_notify.subcode; + + if (bgp_notify.length) { + bgp_notify.data = + XMALLOC(MTYPE_TMP, bgp_notify.length * 3); + for (i = 0; i < bgp_notify.length; i++) + if (first) { + sprintf(c, " %02x", data[i]); + strcat(bgp_notify.data, c); + } else { + first = 1; + sprintf(c, "%02x", data[i]); + strcpy(bgp_notify.data, c); + } + } + bgp_notify_print(peer, &bgp_notify, "sending"); + + if (bgp_notify.data) { + XFREE(MTYPE_TMP, bgp_notify.data); + bgp_notify.data = NULL; + bgp_notify.length = 0; + } + } + + /* peer reset cause */ + if (code == BGP_NOTIFY_CEASE) { + if (sub_code == BGP_NOTIFY_CEASE_ADMIN_RESET) + peer->last_reset = PEER_DOWN_USER_RESET; + else if (sub_code == BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN) + peer->last_reset = PEER_DOWN_USER_SHUTDOWN; + else + peer->last_reset = PEER_DOWN_NOTIFY_SEND; + } else + peer->last_reset = PEER_DOWN_NOTIFY_SEND; + + /* Call immediately. */ + BGP_WRITE_OFF(peer->t_write); + + bgp_write_notify(peer); } /* Send BGP notify packet. */ -void -bgp_notify_send (struct peer *peer, u_char code, u_char sub_code) +void bgp_notify_send(struct peer *peer, u_char code, u_char sub_code) { - bgp_notify_send_with_data (peer, code, sub_code, NULL, 0); + bgp_notify_send_with_data(peer, code, sub_code, NULL, 0); } /* Send route refresh message to the peer. */ -void -bgp_route_refresh_send (struct peer *peer, afi_t afi, safi_t safi, - u_char orf_type, u_char when_to_refresh, int remove) +void bgp_route_refresh_send(struct peer *peer, afi_t afi, safi_t safi, + u_char orf_type, u_char when_to_refresh, int remove) { - struct stream *s; - struct bgp_filter *filter; - int orf_refresh = 0; - iana_afi_t pkt_afi; - safi_t pkt_safi; - - if (DISABLE_BGP_ANNOUNCE) - return; - - filter = &peer->filter[afi][safi]; - - /* Convert AFI, SAFI to values for packet. */ - bgp_map_afi_safi_int2iana (afi, safi, &pkt_afi, &pkt_safi); - - s = stream_new (BGP_MAX_PACKET_SIZE); - - /* Make BGP update packet. */ - if (CHECK_FLAG (peer->cap, PEER_CAP_REFRESH_NEW_RCV)) - bgp_packet_set_marker (s, BGP_MSG_ROUTE_REFRESH_NEW); - else - bgp_packet_set_marker (s, BGP_MSG_ROUTE_REFRESH_OLD); - - /* Encode Route Refresh message. */ - stream_putw (s, pkt_afi); - stream_putc (s, 0); - stream_putc (s, pkt_safi); - - if (orf_type == ORF_TYPE_PREFIX - || orf_type == ORF_TYPE_PREFIX_OLD) - if (remove || filter->plist[FILTER_IN].plist) - { - u_int16_t orf_len; - unsigned long orfp; - - orf_refresh = 1; - stream_putc (s, when_to_refresh); - stream_putc (s, orf_type); - orfp = stream_get_endp (s); - stream_putw (s, 0); - - if (remove) - { - UNSET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_ORF_PREFIX_SEND); - stream_putc (s, ORF_COMMON_PART_REMOVE_ALL); - if (bgp_debug_neighbor_events(peer)) - zlog_debug ("%s sending REFRESH_REQ to remove ORF(%d) (%s) for afi/safi: %d/%d", - peer->host, orf_type, - (when_to_refresh == REFRESH_DEFER ? "defer" : "immediate"), - pkt_afi, pkt_safi); - } + struct stream *s; + struct bgp_filter *filter; + int orf_refresh = 0; + iana_afi_t pkt_afi; + safi_t pkt_safi; + + if (DISABLE_BGP_ANNOUNCE) + return; + + filter = &peer->filter[afi][safi]; + + /* Convert AFI, SAFI to values for packet. */ + bgp_map_afi_safi_int2iana(afi, safi, &pkt_afi, &pkt_safi); + + s = stream_new(BGP_MAX_PACKET_SIZE); + + /* Make BGP update packet. */ + if (CHECK_FLAG(peer->cap, PEER_CAP_REFRESH_NEW_RCV)) + bgp_packet_set_marker(s, BGP_MSG_ROUTE_REFRESH_NEW); else - { - SET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_ORF_PREFIX_SEND); - prefix_bgp_orf_entry (s, filter->plist[FILTER_IN].plist, - ORF_COMMON_PART_ADD, ORF_COMMON_PART_PERMIT, - ORF_COMMON_PART_DENY); - if (bgp_debug_neighbor_events(peer)) - zlog_debug ("%s sending REFRESH_REQ with pfxlist ORF(%d) (%s) for afi/safi: %d/%d", - peer->host, orf_type, - (when_to_refresh == REFRESH_DEFER ? "defer" : "immediate"), - pkt_afi, pkt_safi); - } - - /* Total ORF Entry Len. */ - orf_len = stream_get_endp (s) - orfp - 2; - stream_putw_at (s, orfp, orf_len); - } - - /* Set packet size. */ - (void)bgp_packet_set_size (s); - - if (bgp_debug_neighbor_events(peer)) - { - if (! orf_refresh) - zlog_debug ("%s sending REFRESH_REQ for afi/safi: %d/%d", - peer->host, pkt_afi, pkt_safi); - } - - /* Add packet to the peer. */ - bgp_packet_add (peer, s); - - BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); + bgp_packet_set_marker(s, BGP_MSG_ROUTE_REFRESH_OLD); + + /* Encode Route Refresh message. */ + stream_putw(s, pkt_afi); + stream_putc(s, 0); + stream_putc(s, pkt_safi); + + if (orf_type == ORF_TYPE_PREFIX || orf_type == ORF_TYPE_PREFIX_OLD) + if (remove || filter->plist[FILTER_IN].plist) { + u_int16_t orf_len; + unsigned long orfp; + + orf_refresh = 1; + stream_putc(s, when_to_refresh); + stream_putc(s, orf_type); + orfp = stream_get_endp(s); + stream_putw(s, 0); + + if (remove) { + UNSET_FLAG(peer->af_sflags[afi][safi], + PEER_STATUS_ORF_PREFIX_SEND); + stream_putc(s, ORF_COMMON_PART_REMOVE_ALL); + if (bgp_debug_neighbor_events(peer)) + zlog_debug( + "%s sending REFRESH_REQ to remove ORF(%d) (%s) for afi/safi: %d/%d", + peer->host, orf_type, + (when_to_refresh == REFRESH_DEFER + ? "defer" + : "immediate"), + pkt_afi, pkt_safi); + } else { + SET_FLAG(peer->af_sflags[afi][safi], + PEER_STATUS_ORF_PREFIX_SEND); + prefix_bgp_orf_entry( + s, filter->plist[FILTER_IN].plist, + ORF_COMMON_PART_ADD, + ORF_COMMON_PART_PERMIT, + ORF_COMMON_PART_DENY); + if (bgp_debug_neighbor_events(peer)) + zlog_debug( + "%s sending REFRESH_REQ with pfxlist ORF(%d) (%s) for afi/safi: %d/%d", + peer->host, orf_type, + (when_to_refresh == REFRESH_DEFER + ? "defer" + : "immediate"), + pkt_afi, pkt_safi); + } + + /* Total ORF Entry Len. */ + orf_len = stream_get_endp(s) - orfp - 2; + stream_putw_at(s, orfp, orf_len); + } + + /* Set packet size. */ + (void)bgp_packet_set_size(s); + + if (bgp_debug_neighbor_events(peer)) { + if (!orf_refresh) + zlog_debug("%s sending REFRESH_REQ for afi/safi: %d/%d", + peer->host, pkt_afi, pkt_safi); + } + + /* Add packet to the peer. */ + bgp_packet_add(peer, s); + + BGP_WRITE_ON(peer->t_write, bgp_write, peer->fd); } /* Send capability message to the peer. */ -void -bgp_capability_send (struct peer *peer, afi_t afi, safi_t safi, - int capability_code, int action) +void bgp_capability_send(struct peer *peer, afi_t afi, safi_t safi, + int capability_code, int action) { - struct stream *s; - iana_afi_t pkt_afi; - safi_t pkt_safi; + struct stream *s; + iana_afi_t pkt_afi; + safi_t pkt_safi; + + /* Convert AFI, SAFI to values for packet. */ + bgp_map_afi_safi_int2iana(afi, safi, &pkt_afi, &pkt_safi); + + s = stream_new(BGP_MAX_PACKET_SIZE); + + /* Make BGP update packet. */ + bgp_packet_set_marker(s, BGP_MSG_CAPABILITY); + + /* Encode MP_EXT capability. */ + if (capability_code == CAPABILITY_CODE_MP) { + stream_putc(s, action); + stream_putc(s, CAPABILITY_CODE_MP); + stream_putc(s, CAPABILITY_CODE_MP_LEN); + stream_putw(s, pkt_afi); + stream_putc(s, 0); + stream_putc(s, pkt_safi); + + if (bgp_debug_neighbor_events(peer)) + zlog_debug( + "%s sending CAPABILITY has %s MP_EXT CAP for afi/safi: %d/%d", + peer->host, + action == CAPABILITY_ACTION_SET ? "Advertising" + : "Removing", + pkt_afi, pkt_safi); + } + + /* Set packet size. */ + (void)bgp_packet_set_size(s); - /* Convert AFI, SAFI to values for packet. */ - bgp_map_afi_safi_int2iana (afi, safi, &pkt_afi, &pkt_safi); + /* Add packet to the peer. */ + bgp_packet_add(peer, s); - s = stream_new (BGP_MAX_PACKET_SIZE); + BGP_WRITE_ON(peer->t_write, bgp_write, peer->fd); +} - /* Make BGP update packet. */ - bgp_packet_set_marker (s, BGP_MSG_CAPABILITY); +/* RFC1771 6.8 Connection collision detection. */ +static int bgp_collision_detect(struct peer *new, struct in_addr remote_id) +{ + struct peer *peer; + + /* Upon receipt of an OPEN message, the local system must examine + all of its connections that are in the OpenConfirm state. A BGP + speaker may also examine connections in an OpenSent state if it + knows the BGP Identifier of the peer by means outside of the + protocol. If among these connections there is a connection to a + remote BGP speaker whose BGP Identifier equals the one in the + OPEN message, then the local system performs the following + collision resolution procedure: */ + + if ((peer = new->doppelganger) != NULL) { + /* 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->status == Established || peer->status == Clearing) { + bgp_notify_send(new, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_COLLISION_RESOLUTION); + return (-1); + } else if ((peer->status == OpenConfirm) + || (peer->status == OpenSent)) { + /* 1. The BGP Identifier of the local system is compared + to + the BGP Identifier of the remote system (as specified + in + the OPEN message). */ + + if (ntohl(peer->local_id.s_addr) + < ntohl(remote_id.s_addr)) + if (!CHECK_FLAG(peer->sflags, + PEER_STATUS_ACCEPT_PEER)) { + /* 2. If the value of the local BGP + Identifier is less + than the remote one, the local system + closes BGP + connection that already exists (the + one that is + already in the OpenConfirm state), + and accepts BGP + connection initiated by the remote + system. */ + bgp_notify_send( + peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_COLLISION_RESOLUTION); + return 1; + } else { + bgp_notify_send( + new, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_COLLISION_RESOLUTION); + return -1; + } + else { + /* 3. Otherwise, the local system closes newly + created + BGP connection (the one associated with the + newly + received OPEN message), and continues to use + the + existing one (the one that is already in the + OpenConfirm state). */ + if (CHECK_FLAG(peer->sflags, + PEER_STATUS_ACCEPT_PEER)) { + bgp_notify_send( + peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_COLLISION_RESOLUTION); + return 1; + } else { + bgp_notify_send( + new, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_COLLISION_RESOLUTION); + return -1; + } + } + } + } + return 0; +} - /* Encode MP_EXT capability. */ - if (capability_code == CAPABILITY_CODE_MP) - { - stream_putc (s, action); - stream_putc (s, CAPABILITY_CODE_MP); - stream_putc (s, CAPABILITY_CODE_MP_LEN); - stream_putw (s, pkt_afi); - stream_putc (s, 0); - stream_putc (s, pkt_safi); +static int bgp_open_receive(struct peer *peer, bgp_size_t size) +{ + int ret; + u_char version; + u_char optlen; + u_int16_t holdtime; + u_int16_t send_holdtime; + as_t remote_as; + as_t as4 = 0; + struct in_addr remote_id; + int mp_capability; + u_int8_t notify_data_remote_as[2]; + u_int8_t notify_data_remote_as4[4]; + u_int8_t notify_data_remote_id[4]; + u_int16_t *holdtime_ptr; + + /* Parse open packet. */ + version = stream_getc(peer->ibuf); + memcpy(notify_data_remote_as, stream_pnt(peer->ibuf), 2); + remote_as = stream_getw(peer->ibuf); + holdtime_ptr = (u_int16_t *)stream_pnt(peer->ibuf); + holdtime = stream_getw(peer->ibuf); + memcpy(notify_data_remote_id, stream_pnt(peer->ibuf), 4); + remote_id.s_addr = stream_get_ipv4(peer->ibuf); + + /* Receive OPEN message log */ + if (bgp_debug_neighbor_events(peer)) + zlog_debug( + "%s rcv OPEN, version %d, remote-as (in open) %u," + " holdtime %d, id %s", + peer->host, version, remote_as, holdtime, + inet_ntoa(remote_id)); + + /* BEGIN to read the capability here, but dont do it yet */ + mp_capability = 0; + optlen = stream_getc(peer->ibuf); + + if (optlen != 0) { + /* If not enough bytes, it is an error. */ + if (STREAM_READABLE(peer->ibuf) < optlen) { + bgp_notify_send(peer, BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_MALFORMED_ATTR); + return -1; + } - if (bgp_debug_neighbor_events(peer)) - zlog_debug ("%s sending CAPABILITY has %s MP_EXT CAP for afi/safi: %d/%d", - peer->host, action == CAPABILITY_ACTION_SET ? - "Advertising" : "Removing", pkt_afi, pkt_safi); - } + /* We need the as4 capability value *right now* because + * if it is there, we have not got the remote_as yet, and + * without + * that we do not know which peer is connecting to us now. + */ + as4 = peek_for_as4_capability(peer, optlen); + memcpy(notify_data_remote_as4, &as4, 4); + } - /* Set packet size. */ - (void)bgp_packet_set_size (s); + /* Just in case we have a silly peer who sends AS4 capability set to 0 + */ + if (CHECK_FLAG(peer->cap, PEER_CAP_AS4_RCV) && !as4) { + zlog_err("%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_OPEN_BAD_PEER_AS, + notify_data_remote_as4, 4); + return -1; + } - /* Add packet to the peer. */ - bgp_packet_add (peer, s); + if (remote_as == BGP_AS_TRANS) { + /* Take the AS4 from the capability. We must have received the + * capability now! Otherwise we have a asn16 peer who uses + * BGP_AS_TRANS, for some unknown reason. + */ + if (as4 == BGP_AS_TRANS) { + zlog_err( + "%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_OPEN_BAD_PEER_AS, + notify_data_remote_as4, 4); + return -1; + } - BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); -} + if (!as4 && BGP_DEBUG(as4, AS4)) + zlog_debug( + "%s [AS4] OPEN remote_as is AS_TRANS, but no AS4." + " Odd, but proceeding.", + peer->host); + else if (as4 < BGP_AS_MAX && BGP_DEBUG(as4, AS4)) + zlog_debug( + "%s [AS4] OPEN remote_as is AS_TRANS, but AS4 (%u) fits " + "in 2-bytes, very odd peer.", + peer->host, as4); + if (as4) + remote_as = as4; + } else { + /* We may have a partner with AS4 who has an asno < BGP_AS_MAX + */ + /* If we have got the capability, peer->as4cap must match + * remote_as */ + if (CHECK_FLAG(peer->cap, PEER_CAP_AS4_RCV) + && as4 != remote_as) { + /* raise error, log this, close session */ + zlog_err( + "%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_OPEN_BAD_PEER_AS, + notify_data_remote_as4, 4); + return -1; + } + } -/* RFC1771 6.8 Connection collision detection. */ -static int -bgp_collision_detect (struct peer *new, struct in_addr remote_id) -{ - struct peer *peer; - - /* Upon receipt of an OPEN message, the local system must examine - all of its connections that are in the OpenConfirm state. A BGP - speaker may also examine connections in an OpenSent state if it - knows the BGP Identifier of the peer by means outside of the - protocol. If among these connections there is a connection to a - remote BGP speaker whose BGP Identifier equals the one in the - OPEN message, then the local system performs the following - collision resolution procedure: */ - - if ((peer = new->doppelganger) != NULL) - { - /* 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->status == Established || peer->status == Clearing) - { - bgp_notify_send (new, BGP_NOTIFY_CEASE, - BGP_NOTIFY_CEASE_COLLISION_RESOLUTION); - return (-1); - } - else if ((peer->status == OpenConfirm) || (peer->status == OpenSent)) - { - /* 1. The BGP Identifier of the local system is compared to - the BGP Identifier of the remote system (as specified in - the OPEN message). */ - - if (ntohl (peer->local_id.s_addr) < ntohl (remote_id.s_addr)) - if (!CHECK_FLAG(peer->sflags, PEER_STATUS_ACCEPT_PEER)) - { - /* 2. If the value of the local BGP Identifier is less - than the remote one, the local system closes BGP - connection that already exists (the one that is - already in the OpenConfirm state), and accepts BGP - connection initiated by the remote system. */ - bgp_notify_send (peer, BGP_NOTIFY_CEASE, - BGP_NOTIFY_CEASE_COLLISION_RESOLUTION); - return 1; - } - else - { - bgp_notify_send (new, BGP_NOTIFY_CEASE, - BGP_NOTIFY_CEASE_COLLISION_RESOLUTION); + /* remote router-id check. */ + if (remote_id.s_addr == 0 || IPV4_CLASS_DE(ntohl(remote_id.s_addr)) + || ntohl(peer->local_id.s_addr) == ntohl(remote_id.s_addr)) { + if (bgp_debug_neighbor_events(peer)) + zlog_debug("%s bad OPEN, wrong router identifier %s", + peer->host, inet_ntoa(remote_id)); + bgp_notify_send_with_data(peer, BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_BAD_BGP_IDENT, + notify_data_remote_id, 4); + return -1; + } + + /* Set remote router-id */ + peer->remote_id = remote_id; + + /* Peer BGP version check. */ + if (version != BGP_VERSION_4) { + u_int16_t maxver = htons(BGP_VERSION_4); + /* XXX this reply may not be correct if version < 4 XXX */ + if (bgp_debug_neighbor_events(peer)) + zlog_debug( + "%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_OPEN_UNSUP_VERSION, + (u_int8_t *)&maxver, 2); + return -1; + } + + /* Check neighbor as number. */ + if (peer->as_type == AS_UNSPECIFIED) { + if (bgp_debug_neighbor_events(peer)) + zlog_debug( + "%s bad OPEN, remote AS is unspecified currently", + peer->host); + bgp_notify_send_with_data(peer, BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_BAD_PEER_AS, + notify_data_remote_as, 2); return -1; - } - else - { - /* 3. Otherwise, the local system closes newly created - BGP connection (the one associated with the newly - received OPEN message), and continues to use the - existing one (the one that is already in the - OpenConfirm state). */ - if (CHECK_FLAG(peer->sflags, PEER_STATUS_ACCEPT_PEER)) - { - bgp_notify_send (peer, BGP_NOTIFY_CEASE, - BGP_NOTIFY_CEASE_COLLISION_RESOLUTION); - return 1; + } else if (peer->as_type == AS_INTERNAL) { + if (remote_as != peer->bgp->as) { + if (bgp_debug_neighbor_events(peer)) + 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_OPEN_BAD_PEER_AS, + notify_data_remote_as, 2); + return -1; } - else - { - bgp_notify_send (new, BGP_NOTIFY_CEASE, - BGP_NOTIFY_CEASE_COLLISION_RESOLUTION); - return -1; + peer->as = peer->local_as; + } else if (peer->as_type == AS_EXTERNAL) { + if (remote_as == peer->bgp->as) { + if (bgp_debug_neighbor_events(peer)) + 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_OPEN_BAD_PEER_AS, + notify_data_remote_as, 2); + return -1; } - } + peer->as = remote_as; + } else if ((peer->as_type == AS_SPECIFIED) && (remote_as != peer->as)) { + 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_OPEN_BAD_PEER_AS, + notify_data_remote_as, 2); + return -1; } - } - return 0; -} -static int -bgp_open_receive (struct peer *peer, bgp_size_t size) -{ - int ret; - u_char version; - u_char optlen; - u_int16_t holdtime; - u_int16_t send_holdtime; - as_t remote_as; - as_t as4 = 0; - struct in_addr remote_id; - int mp_capability; - u_int8_t notify_data_remote_as[2]; - u_int8_t notify_data_remote_as4[4]; - u_int8_t notify_data_remote_id[4]; - u_int16_t *holdtime_ptr; - - /* Parse open packet. */ - version = stream_getc (peer->ibuf); - memcpy (notify_data_remote_as, stream_pnt (peer->ibuf), 2); - remote_as = stream_getw (peer->ibuf); - holdtime_ptr = (u_int16_t *)stream_pnt (peer->ibuf); - holdtime = stream_getw (peer->ibuf); - memcpy (notify_data_remote_id, stream_pnt (peer->ibuf), 4); - remote_id.s_addr = stream_get_ipv4 (peer->ibuf); - - /* Receive OPEN message log */ - if (bgp_debug_neighbor_events(peer)) - zlog_debug ("%s rcv OPEN, version %d, remote-as (in open) %u," - " holdtime %d, id %s", - peer->host, version, remote_as, holdtime, - inet_ntoa (remote_id)); - - /* BEGIN to read the capability here, but dont do it yet */ - mp_capability = 0; - optlen = stream_getc (peer->ibuf); - - if (optlen != 0) - { - /* If not enough bytes, it is an error. */ - if (STREAM_READABLE(peer->ibuf) < optlen) - { - bgp_notify_send (peer, BGP_NOTIFY_OPEN_ERR, - BGP_NOTIFY_OPEN_MALFORMED_ATTR); - return -1; - } - - /* We need the as4 capability value *right now* because - * if it is there, we have not got the remote_as yet, and without - * that we do not know which peer is connecting to us now. - */ - as4 = peek_for_as4_capability (peer, optlen); - memcpy (notify_data_remote_as4, &as4, 4); - } - - /* Just in case we have a silly peer who sends AS4 capability set to 0 */ - if (CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV) && !as4) - { - zlog_err ("%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_OPEN_BAD_PEER_AS, - notify_data_remote_as4, 4); - return -1; - } - - if (remote_as == BGP_AS_TRANS) - { - /* Take the AS4 from the capability. We must have received the - * capability now! Otherwise we have a asn16 peer who uses - * BGP_AS_TRANS, for some unknown reason. + /* From the rfc: Upon receipt of an OPEN message, a BGP speaker MUST + calculate the value of the Hold Timer by using the smaller of its + configured Hold Time and the Hold Time received in the OPEN message. + The Hold Time MUST be either zero or at least three seconds. An + implementation may reject connections on the basis of the Hold Time. */ - if (as4 == BGP_AS_TRANS) - { - zlog_err ("%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_OPEN_BAD_PEER_AS, - notify_data_remote_as4, 4); - return -1; - } - - if (!as4 && BGP_DEBUG (as4, AS4)) - zlog_debug ("%s [AS4] OPEN remote_as is AS_TRANS, but no AS4." - " Odd, but proceeding.", peer->host); - else if (as4 < BGP_AS_MAX && BGP_DEBUG (as4, AS4)) - zlog_debug ("%s [AS4] OPEN remote_as is AS_TRANS, but AS4 (%u) fits " - "in 2-bytes, very odd peer.", peer->host, as4); - if (as4) - remote_as = as4; - } - else - { - /* We may have a partner with AS4 who has an asno < BGP_AS_MAX */ - /* If we have got the capability, peer->as4cap must match remote_as */ - if (CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV) - && as4 != remote_as) - { - /* raise error, log this, close session */ - zlog_err ("%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_OPEN_BAD_PEER_AS, - notify_data_remote_as4, 4); - return -1; + + if (holdtime < 3 && holdtime != 0) { + bgp_notify_send_with_data(peer, BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_UNACEP_HOLDTIME, + (u_char *)holdtime_ptr, 2); + return -1; } - } - - /* remote router-id check. */ - if (remote_id.s_addr == 0 - || IPV4_CLASS_DE (ntohl (remote_id.s_addr)) - || ntohl (peer->local_id.s_addr) == ntohl (remote_id.s_addr)) - { - if (bgp_debug_neighbor_events(peer)) - zlog_debug ("%s bad OPEN, wrong router identifier %s", - peer->host, inet_ntoa (remote_id)); - bgp_notify_send_with_data (peer, - BGP_NOTIFY_OPEN_ERR, - BGP_NOTIFY_OPEN_BAD_BGP_IDENT, - notify_data_remote_id, 4); - return -1; - } - - /* Set remote router-id */ - peer->remote_id = remote_id; - - /* Peer BGP version check. */ - if (version != BGP_VERSION_4) - { - u_int16_t maxver = htons(BGP_VERSION_4); - /* XXX this reply may not be correct if version < 4 XXX */ - if (bgp_debug_neighbor_events(peer)) - zlog_debug ("%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_OPEN_UNSUP_VERSION, - (u_int8_t *) &maxver, 2); - return -1; - } - - /* Check neighbor as number. */ - if (peer->as_type == AS_UNSPECIFIED) - { - if (bgp_debug_neighbor_events(peer)) - zlog_debug("%s bad OPEN, remote AS is unspecified currently", peer->host); - bgp_notify_send_with_data(peer, - BGP_NOTIFY_OPEN_ERR, - BGP_NOTIFY_OPEN_BAD_PEER_AS, - notify_data_remote_as, 2); - return -1; - } - else if (peer->as_type == AS_INTERNAL) - { - if (remote_as != peer->bgp->as) - { - if (bgp_debug_neighbor_events(peer)) - 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_OPEN_BAD_PEER_AS, - notify_data_remote_as, 2); - return -1; + + /* From the rfc: A reasonable maximum time between KEEPALIVE messages + would be one third of the Hold Time interval. KEEPALIVE messages + MUST NOT be sent more frequently than one per second. An + implementation MAY adjust the rate at which it sends KEEPALIVE + messages as a function of the Hold Time interval. */ + + if (CHECK_FLAG(peer->config, PEER_CONFIG_TIMER)) + send_holdtime = peer->holdtime; + else + send_holdtime = peer->bgp->default_holdtime; + + if (holdtime < send_holdtime) + peer->v_holdtime = holdtime; + else + peer->v_holdtime = send_holdtime; + + if (CHECK_FLAG(peer->config, PEER_CONFIG_TIMER)) + peer->v_keepalive = peer->keepalive; + else + peer->v_keepalive = peer->v_holdtime / 3; + + /* Open option part parse. */ + if (optlen != 0) { + if ((ret = bgp_open_option_parse(peer, optlen, &mp_capability)) + < 0) + return ret; + } else { + if (bgp_debug_neighbor_events(peer)) + zlog_debug("%s rcvd OPEN w/ OPTION parameter len: 0", + peer->host); } - peer->as = peer->local_as; - } - else if (peer->as_type == AS_EXTERNAL) - { - if (remote_as == peer->bgp->as) - { - if (bgp_debug_neighbor_events(peer)) - 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_OPEN_BAD_PEER_AS, - notify_data_remote_as, 2); - return -1; + + /* + * Assume that the peer supports the locally configured set of + * AFI/SAFIs if the peer did not send us any Mulitiprotocol + * capabilities, or if 'override-capability' is configured. + */ + if (!mp_capability + || CHECK_FLAG(peer->flags, PEER_FLAG_OVERRIDE_CAPABILITY)) { + peer->afc_nego[AFI_IP][SAFI_UNICAST] = + peer->afc[AFI_IP][SAFI_UNICAST]; + peer->afc_nego[AFI_IP][SAFI_MULTICAST] = + peer->afc[AFI_IP][SAFI_MULTICAST]; + peer->afc_nego[AFI_IP6][SAFI_UNICAST] = + peer->afc[AFI_IP6][SAFI_UNICAST]; + peer->afc_nego[AFI_IP6][SAFI_MULTICAST] = + peer->afc[AFI_IP6][SAFI_MULTICAST]; } - peer->as = remote_as; - } - else if ((peer->as_type == AS_SPECIFIED) && (remote_as != peer->as)) - { - 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_OPEN_BAD_PEER_AS, - notify_data_remote_as, 2); - return -1; - } - - /* From the rfc: Upon receipt of an OPEN message, a BGP speaker MUST - calculate the value of the Hold Timer by using the smaller of its - configured Hold Time and the Hold Time received in the OPEN message. - The Hold Time MUST be either zero or at least three seconds. An - implementation may reject connections on the basis of the Hold Time. */ - - if (holdtime < 3 && holdtime != 0) - { - bgp_notify_send_with_data (peer, - BGP_NOTIFY_OPEN_ERR, - BGP_NOTIFY_OPEN_UNACEP_HOLDTIME, - (u_char *)holdtime_ptr, 2); - return -1; - } - - /* From the rfc: A reasonable maximum time between KEEPALIVE messages - would be one third of the Hold Time interval. KEEPALIVE messages - MUST NOT be sent more frequently than one per second. An - implementation MAY adjust the rate at which it sends KEEPALIVE - messages as a function of the Hold Time interval. */ - - if (CHECK_FLAG (peer->config, PEER_CONFIG_TIMER)) - send_holdtime = peer->holdtime; - else - send_holdtime = peer->bgp->default_holdtime; - - if (holdtime < send_holdtime) - peer->v_holdtime = holdtime; - else - peer->v_holdtime = send_holdtime; - - if (CHECK_FLAG (peer->config, PEER_CONFIG_TIMER)) - peer->v_keepalive = peer->keepalive; - else - peer->v_keepalive = peer->v_holdtime / 3; - - /* Open option part parse. */ - if (optlen != 0) - { - if ((ret = bgp_open_option_parse (peer, optlen, &mp_capability)) < 0) - return ret; - } - else - { - if (bgp_debug_neighbor_events(peer)) - zlog_debug ("%s rcvd OPEN w/ OPTION parameter len: 0", - peer->host); - } - - /* - * Assume that the peer supports the locally configured set of - * AFI/SAFIs if the peer did not send us any Mulitiprotocol - * capabilities, or if 'override-capability' is configured. - */ - if (! mp_capability || - CHECK_FLAG (peer->flags, PEER_FLAG_OVERRIDE_CAPABILITY)) - { - peer->afc_nego[AFI_IP][SAFI_UNICAST] = peer->afc[AFI_IP][SAFI_UNICAST]; - peer->afc_nego[AFI_IP][SAFI_MULTICAST] = peer->afc[AFI_IP][SAFI_MULTICAST]; - peer->afc_nego[AFI_IP6][SAFI_UNICAST] = peer->afc[AFI_IP6][SAFI_UNICAST]; - peer->afc_nego[AFI_IP6][SAFI_MULTICAST] = peer->afc[AFI_IP6][SAFI_MULTICAST]; - } - - /* When collision is detected and this peer is closed. Retrun - immidiately. */ - ret = bgp_collision_detect (peer, remote_id); - if (ret < 0) - return ret; - - /* Get sockname. */ - if ((ret = bgp_getsockname (peer)) < 0) - { - zlog_err("%s: bgp_getsockname() failed for peer: %s", __FUNCTION__, - peer->host); - return (ret); - } - - /* Verify valid local address present based on negotiated address-families. */ - if (peer->afc_nego[AFI_IP][SAFI_UNICAST] || - peer->afc_nego[AFI_IP][SAFI_MULTICAST] || - peer->afc_nego[AFI_IP][SAFI_MPLS_VPN] || - peer->afc_nego[AFI_IP][SAFI_ENCAP]) - { - if (!peer->nexthop.v4.s_addr) - { -#if defined (HAVE_CUMULUS) - zlog_err ("%s: No local IPv4 addr resetting connection, fd %d", - peer->host, peer->fd); - bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_SUBCODE_UNSPECIFIC); - return -1; + + /* When collision is detected and this peer is closed. Retrun + immidiately. */ + ret = bgp_collision_detect(peer, remote_id); + if (ret < 0) + return ret; + + /* Get sockname. */ + if ((ret = bgp_getsockname(peer)) < 0) { + zlog_err("%s: bgp_getsockname() failed for peer: %s", + __FUNCTION__, peer->host); + return (ret); + } + + /* Verify valid local address present based on negotiated + * address-families. */ + if (peer->afc_nego[AFI_IP][SAFI_UNICAST] + || peer->afc_nego[AFI_IP][SAFI_MULTICAST] + || peer->afc_nego[AFI_IP][SAFI_MPLS_VPN] + || peer->afc_nego[AFI_IP][SAFI_ENCAP]) { + if (!peer->nexthop.v4.s_addr) { +#if defined(HAVE_CUMULUS) + zlog_err( + "%s: No local IPv4 addr resetting connection, fd %d", + peer->host, peer->fd); + bgp_notify_send(peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_SUBCODE_UNSPECIFIC); + return -1; #endif - } - } - if (peer->afc_nego[AFI_IP6][SAFI_UNICAST] || - 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_err ("%s: No local IPv6 addr resetting connection, fd %d", - peer->host, peer->fd); - bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_SUBCODE_UNSPECIFIC); - return -1; + } + } + if (peer->afc_nego[AFI_IP6][SAFI_UNICAST] + || 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_err( + "%s: No local IPv6 addr resetting connection, fd %d", + peer->host, peer->fd); + bgp_notify_send(peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_SUBCODE_UNSPECIFIC); + return -1; #endif - } - } - peer->rtt = sockopt_tcp_rtt (peer->fd); - - if ((ret = bgp_event_update(peer, Receive_OPEN_message)) < 0) - { - zlog_err("%s: BGP event update failed for peer: %s", __FUNCTION__, - peer->host); - /* DD: bgp send notify and reset state */ - return (ret); - } - - peer->packet_size = 0; - if (peer->ibuf) - stream_reset (peer->ibuf); - - return 0; + } + } + peer->rtt = sockopt_tcp_rtt(peer->fd); + + if ((ret = bgp_event_update(peer, Receive_OPEN_message)) < 0) { + zlog_err("%s: BGP event update failed for peer: %s", + __FUNCTION__, peer->host); + /* DD: bgp send notify and reset state */ + return (ret); + } + + peer->packet_size = 0; + if (peer->ibuf) + stream_reset(peer->ibuf); + + return 0; } -/* Called when there is a change in the EOR(implicit or explicit) status of a peer. +/* Called when there is a change in the EOR(implicit or explicit) status of a + peer. Ends the update-delay if all expected peers are done with EORs. */ -void -bgp_check_update_delay(struct bgp *bgp) +void bgp_check_update_delay(struct bgp *bgp) { - struct listnode *node, *nnode; - struct peer *peer = NULL; - - if (bgp_debug_neighbor_events(peer)) - zlog_debug ("Checking update delay, T: %d R: %d I:%d E: %d", bgp->established, - bgp->restarted_peers, bgp->implicit_eors, bgp->explicit_eors); - - if (bgp->established <= - bgp->restarted_peers + bgp->implicit_eors + bgp->explicit_eors) - { - /* This is an extra sanity check to make sure we wait for all the - eligible configured peers. This check is performed if establish wait - timer is on, or establish wait option is not given with the - update-delay command */ - if (bgp->t_establish_wait || - (bgp->v_establish_wait == bgp->v_update_delay)) - for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) - { - if (CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE) - && !CHECK_FLAG (peer->flags, PEER_FLAG_SHUTDOWN) - && !peer->update_delay_over) - { - if (bgp_debug_neighbor_events(peer)) - zlog_debug (" Peer %s pending, continuing read-only mode", - peer->host); - return; - } - } - - zlog_info ("Update delay ended, restarted: %d, EORs implicit: %d, explicit: %d", - bgp->restarted_peers, bgp->implicit_eors, bgp->explicit_eors); - bgp_update_delay_end(bgp); - } + struct listnode *node, *nnode; + struct peer *peer = NULL; + + if (bgp_debug_neighbor_events(peer)) + zlog_debug("Checking update delay, T: %d R: %d I:%d E: %d", + bgp->established, bgp->restarted_peers, + bgp->implicit_eors, bgp->explicit_eors); + + if (bgp->established + <= bgp->restarted_peers + bgp->implicit_eors + bgp->explicit_eors) { + /* This is an extra sanity check to make sure we wait for all + the + eligible configured peers. This check is performed if + establish wait + timer is on, or establish wait option is not given with the + update-delay command */ + if (bgp->t_establish_wait + || (bgp->v_establish_wait == bgp->v_update_delay)) + for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { + if (CHECK_FLAG(peer->flags, + PEER_FLAG_CONFIG_NODE) + && !CHECK_FLAG(peer->flags, + PEER_FLAG_SHUTDOWN) + && !peer->update_delay_over) { + if (bgp_debug_neighbor_events(peer)) + zlog_debug( + " Peer %s pending, continuing read-only mode", + peer->host); + return; + } + } + + zlog_info( + "Update delay ended, restarted: %d, EORs implicit: %d, explicit: %d", + bgp->restarted_peers, bgp->implicit_eors, + bgp->explicit_eors); + bgp_update_delay_end(bgp); + } } /* Called if peer is known to have restarted. The restart-state bit in Graceful-Restart capability is used for that */ -void -bgp_update_restarted_peers (struct peer *peer) +void bgp_update_restarted_peers(struct peer *peer) { - if (!bgp_update_delay_active(peer->bgp)) return; /* BGP update delay has ended */ - if (peer->update_delay_over) return; /* This peer has already been considered */ - - if (bgp_debug_neighbor_events(peer)) - zlog_debug ("Peer %s: Checking restarted", peer->host); - - if (peer->status == Established) - { - peer->update_delay_over = 1; - peer->bgp->restarted_peers++; - bgp_check_update_delay(peer->bgp); - } + if (!bgp_update_delay_active(peer->bgp)) + return; /* BGP update delay has ended */ + if (peer->update_delay_over) + return; /* This peer has already been considered */ + + if (bgp_debug_neighbor_events(peer)) + zlog_debug("Peer %s: Checking restarted", peer->host); + + if (peer->status == Established) { + peer->update_delay_over = 1; + peer->bgp->restarted_peers++; + bgp_check_update_delay(peer->bgp); + } } /* Called as peer receives a keep-alive. Determines if this occurence can be taken as an implicit EOR for this peer. NOTE: The very first keep-alive after the Established state of a peer is - considered implicit EOR for the update-delay purposes */ -void -bgp_update_implicit_eors (struct peer *peer) + considered implicit EOR for the update-delay purposes */ +void bgp_update_implicit_eors(struct peer *peer) { - if (!bgp_update_delay_active(peer->bgp)) return; /* BGP update delay has ended */ - if (peer->update_delay_over) return; /* This peer has already been considered */ - - if (bgp_debug_neighbor_events(peer)) - zlog_debug ("Peer %s: Checking implicit EORs", peer->host); - - if (peer->status == Established) - { - peer->update_delay_over = 1; - peer->bgp->implicit_eors++; - bgp_check_update_delay(peer->bgp); - } + if (!bgp_update_delay_active(peer->bgp)) + return; /* BGP update delay has ended */ + if (peer->update_delay_over) + return; /* This peer has already been considered */ + + if (bgp_debug_neighbor_events(peer)) + zlog_debug("Peer %s: Checking implicit EORs", peer->host); + + if (peer->status == Established) { + peer->update_delay_over = 1; + peer->bgp->implicit_eors++; + bgp_check_update_delay(peer->bgp); + } } /* Should be called only when there is a change in the EOR_RECEIVED status for any afi/safi on a peer */ -static void -bgp_update_explicit_eors (struct peer *peer) +static void bgp_update_explicit_eors(struct peer *peer) { - afi_t afi; - safi_t safi; - - if (!bgp_update_delay_active(peer->bgp)) return; /* BGP update delay has ended */ - if (peer->update_delay_over) return; /* This peer has already been considered */ - - if (bgp_debug_neighbor_events(peer)) - zlog_debug ("Peer %s: Checking explicit EORs", peer->host); - - for (afi = AFI_IP; afi < AFI_MAX; afi++) - for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) - { - if (peer->afc_nego[afi][safi] && - !CHECK_FLAG(peer->af_sflags[afi][safi], PEER_STATUS_EOR_RECEIVED)) - { - if (bgp_debug_neighbor_events(peer)) - zlog_debug (" afi %d safi %d didnt receive EOR", afi, safi); - return; - } - } - - peer->update_delay_over = 1; - peer->bgp->explicit_eors++; - bgp_check_update_delay(peer->bgp); + afi_t afi; + safi_t safi; + + if (!bgp_update_delay_active(peer->bgp)) + return; /* BGP update delay has ended */ + if (peer->update_delay_over) + return; /* This peer has already been considered */ + + if (bgp_debug_neighbor_events(peer)) + zlog_debug("Peer %s: Checking explicit EORs", peer->host); + + for (afi = AFI_IP; afi < AFI_MAX; afi++) + for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) { + if (peer->afc_nego[afi][safi] + && !CHECK_FLAG(peer->af_sflags[afi][safi], + PEER_STATUS_EOR_RECEIVED)) { + if (bgp_debug_neighbor_events(peer)) + zlog_debug( + " afi %d safi %d didnt receive EOR", + afi, safi); + return; + } + } + + peer->update_delay_over = 1; + peer->bgp->explicit_eors++; + bgp_check_update_delay(peer->bgp); } -/* Frontend for NLRI parsing, to fan-out to AFI/SAFI specific parsers - * mp_withdraw, if set, is used to nullify attr structure on most of the calling safi function +/* Frontend for NLRI parsing, to fan-out to AFI/SAFI specific parsers + * mp_withdraw, if set, is used to nullify attr structure on most of the calling + * safi function * and for evpn, passed as parameter */ -int -bgp_nlri_parse (struct peer *peer, struct attr *attr, struct bgp_nlri *packet, int mp_withdraw) +int bgp_nlri_parse(struct peer *peer, struct attr *attr, + struct bgp_nlri *packet, int mp_withdraw) { - switch (packet->safi) - { - case SAFI_UNICAST: - case SAFI_MULTICAST: - return bgp_nlri_parse_ip (peer, mp_withdraw?NULL:attr, packet); - case SAFI_MPLS_VPN: - return bgp_nlri_parse_vpn (peer, mp_withdraw?NULL:attr, packet); - case SAFI_EVPN: - return bgp_nlri_parse_evpn (peer, attr, packet, mp_withdraw); - } - return -1; + switch (packet->safi) { + case SAFI_UNICAST: + case SAFI_MULTICAST: + return bgp_nlri_parse_ip(peer, mp_withdraw ? NULL : attr, + packet); + case SAFI_MPLS_VPN: + return bgp_nlri_parse_vpn(peer, mp_withdraw ? NULL : attr, + packet); + case SAFI_EVPN: + return bgp_nlri_parse_evpn(peer, attr, packet, mp_withdraw); + } + return -1; } /* Parse BGP Update packet and make attribute object. */ -static int -bgp_update_receive (struct peer *peer, bgp_size_t size) +static int bgp_update_receive(struct peer *peer, bgp_size_t size) { - int ret, nlri_ret; - u_char *end; - struct stream *s; - struct attr attr; - struct attr_extra extra; - bgp_size_t attribute_len; - bgp_size_t update_len; - bgp_size_t withdraw_len; - - enum NLRI_TYPES { - NLRI_UPDATE, - NLRI_WITHDRAW, - NLRI_MP_UPDATE, - NLRI_MP_WITHDRAW, - NLRI_TYPE_MAX - }; - struct bgp_nlri nlris[NLRI_TYPE_MAX]; - - /* Status must be Established. */ - if (peer->status != Established) - { - zlog_err ("%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, 0); - return -1; - } - - /* Set initial values. */ - memset (&attr, 0, sizeof (struct attr)); - memset (&extra, 0, sizeof (struct attr_extra)); - memset (&nlris, 0, sizeof (nlris)); - attr.extra = &extra; - memset (peer->rcvd_attr_str, 0, BUFSIZ); - peer->rcvd_attr_printed = 0; - - s = peer->ibuf; - end = stream_pnt (s) + size; - - /* RFC1771 6.3 If the Unfeasible Routes Length or Total Attribute - Length is too large (i.e., if Unfeasible Routes Length + Total - Attribute Length + 23 exceeds the message Length), then the Error - Subcode is set to Malformed Attribute List. */ - if (stream_pnt (s) + 2 > end) - { - zlog_err ("%s [Error] Update packet error" - " (packet length is short for unfeasible length)", - peer->host); - bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_MAL_ATTR); - return -1; - } - - /* Unfeasible Route Length. */ - withdraw_len = stream_getw (s); - - /* Unfeasible Route Length check. */ - if (stream_pnt (s) + withdraw_len > end) - { - zlog_err ("%s [Error] Update packet error" - " (packet unfeasible length overflow %d)", - peer->host, withdraw_len); - bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_MAL_ATTR); - return -1; - } - - /* Unfeasible Route packet format check. */ - if (withdraw_len > 0) - { - nlris[NLRI_WITHDRAW].afi = AFI_IP; - nlris[NLRI_WITHDRAW].safi = SAFI_UNICAST; - nlris[NLRI_WITHDRAW].nlri = stream_pnt (s); - nlris[NLRI_WITHDRAW].length = withdraw_len; - stream_forward_getp (s, withdraw_len); - } - - /* Attribute total length check. */ - if (stream_pnt (s) + 2 > end) - { - zlog_warn ("%s [Error] Packet Error" - " (update packet is short for attribute length)", - peer->host); - bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_MAL_ATTR); - return -1; - } - - /* Fetch attribute total length. */ - attribute_len = stream_getw (s); - - /* Attribute length check. */ - if (stream_pnt (s) + attribute_len > end) - { - zlog_warn ("%s [Error] Packet Error" - " (update packet attribute length overflow %d)", - peer->host, attribute_len); - bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_MAL_ATTR); - return -1; - } - - /* Certain attribute parsing errors should not be considered bad enough - * to reset the session for, most particularly any partial/optional - * attributes that have 'tunneled' over speakers that don't understand - * them. Instead we withdraw only the prefix concerned. - * - * Complicates the flow a little though.. - */ - bgp_attr_parse_ret_t attr_parse_ret = BGP_ATTR_PARSE_PROCEED; - /* This define morphs the update case into a withdraw when lower levels - * have signalled an error condition where this is best. - */ + int ret, nlri_ret; + u_char *end; + struct stream *s; + struct attr attr; + struct attr_extra extra; + bgp_size_t attribute_len; + bgp_size_t update_len; + bgp_size_t withdraw_len; + + enum NLRI_TYPES { + NLRI_UPDATE, + NLRI_WITHDRAW, + NLRI_MP_UPDATE, + NLRI_MP_WITHDRAW, + NLRI_TYPE_MAX + }; + struct bgp_nlri nlris[NLRI_TYPE_MAX]; + + /* Status must be Established. */ + if (peer->status != Established) { + zlog_err("%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, 0); + return -1; + } + + /* Set initial values. */ + memset(&attr, 0, sizeof(struct attr)); + memset(&extra, 0, sizeof(struct attr_extra)); + memset(&nlris, 0, sizeof(nlris)); + attr.extra = &extra; + memset(peer->rcvd_attr_str, 0, BUFSIZ); + peer->rcvd_attr_printed = 0; + + s = peer->ibuf; + end = stream_pnt(s) + size; + + /* RFC1771 6.3 If the Unfeasible Routes Length or Total Attribute + Length is too large (i.e., if Unfeasible Routes Length + Total + Attribute Length + 23 exceeds the message Length), then the Error + Subcode is set to Malformed Attribute List. */ + if (stream_pnt(s) + 2 > end) { + zlog_err( + "%s [Error] Update packet error" + " (packet length is short for unfeasible length)", + peer->host); + bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_MAL_ATTR); + return -1; + } + + /* Unfeasible Route Length. */ + withdraw_len = stream_getw(s); + + /* Unfeasible Route Length check. */ + if (stream_pnt(s) + withdraw_len > end) { + zlog_err( + "%s [Error] Update packet error" + " (packet unfeasible length overflow %d)", + peer->host, withdraw_len); + bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_MAL_ATTR); + return -1; + } + + /* Unfeasible Route packet format check. */ + if (withdraw_len > 0) { + nlris[NLRI_WITHDRAW].afi = AFI_IP; + nlris[NLRI_WITHDRAW].safi = SAFI_UNICAST; + nlris[NLRI_WITHDRAW].nlri = stream_pnt(s); + nlris[NLRI_WITHDRAW].length = withdraw_len; + stream_forward_getp(s, withdraw_len); + } + + /* Attribute total length check. */ + if (stream_pnt(s) + 2 > end) { + zlog_warn( + "%s [Error] Packet Error" + " (update packet is short for attribute length)", + peer->host); + bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_MAL_ATTR); + return -1; + } + + /* Fetch attribute total length. */ + attribute_len = stream_getw(s); + + /* Attribute length check. */ + if (stream_pnt(s) + attribute_len > end) { + zlog_warn( + "%s [Error] Packet Error" + " (update packet attribute length overflow %d)", + peer->host, attribute_len); + bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_MAL_ATTR); + return -1; + } + + /* Certain attribute parsing errors should not be considered bad enough + * to reset the session for, most particularly any partial/optional + * attributes that have 'tunneled' over speakers that don't understand + * them. Instead we withdraw only the prefix concerned. + * + * Complicates the flow a little though.. + */ + bgp_attr_parse_ret_t attr_parse_ret = BGP_ATTR_PARSE_PROCEED; +/* This define morphs the update case into a withdraw when lower levels + * have signalled an error condition where this is best. + */ #define NLRI_ATTR_ARG (attr_parse_ret != BGP_ATTR_PARSE_WITHDRAW ? &attr : NULL) - /* Parse attribute when it exists. */ - if (attribute_len) - { - attr_parse_ret = bgp_attr_parse (peer, &attr, attribute_len, - &nlris[NLRI_MP_UPDATE], &nlris[NLRI_MP_WITHDRAW]); - if (attr_parse_ret == BGP_ATTR_PARSE_ERROR) - { - bgp_attr_unintern_sub (&attr); - return -1; + /* Parse attribute when it exists. */ + if (attribute_len) { + attr_parse_ret = bgp_attr_parse(peer, &attr, attribute_len, + &nlris[NLRI_MP_UPDATE], + &nlris[NLRI_MP_WITHDRAW]); + if (attr_parse_ret == BGP_ATTR_PARSE_ERROR) { + bgp_attr_unintern_sub(&attr); + return -1; + } + } + + /* Logging the attribute. */ + if (attr_parse_ret == BGP_ATTR_PARSE_WITHDRAW + || BGP_DEBUG(update, UPDATE_IN) + || BGP_DEBUG(update, UPDATE_PREFIX)) { + ret = bgp_dump_attr(peer, &attr, peer->rcvd_attr_str, BUFSIZ); + + if (attr_parse_ret == BGP_ATTR_PARSE_WITHDRAW) + zlog_err( + "%s rcvd UPDATE with errors in attr(s)!! Withdrawing route.", + peer->host); + + if (ret && bgp_debug_update(peer, NULL, NULL, 1)) { + zlog_debug("%s rcvd UPDATE w/ attr: %s", peer->host, + peer->rcvd_attr_str); + peer->rcvd_attr_printed = 1; + } + } + + /* Network Layer Reachability Information. */ + update_len = end - stream_pnt(s); + + if (update_len) { + /* Set NLRI portion to structure. */ + nlris[NLRI_UPDATE].afi = AFI_IP; + nlris[NLRI_UPDATE].safi = SAFI_UNICAST; + nlris[NLRI_UPDATE].nlri = stream_pnt(s); + nlris[NLRI_UPDATE].length = update_len; + stream_forward_getp(s, update_len); + } + + if (BGP_DEBUG(update, UPDATE_IN)) + zlog_debug("%s rcvd UPDATE wlen %d attrlen %d alen %d", + peer->host, withdraw_len, attribute_len, update_len); + + /* Parse any given NLRIs */ + for (int i = NLRI_UPDATE; i < NLRI_TYPE_MAX; i++) { + if (!nlris[i].nlri) + continue; + + /* NLRI is processed iff the peer if configured for the specific + * afi/safi */ + if (!peer->afc[nlris[i].afi][nlris[i].safi]) { + zlog_info( + "%s [Info] UPDATE for non-enabled AFI/SAFI %u/%u", + peer->host, nlris[i].afi, nlris[i].safi); + continue; + } + + /* EoR handled later */ + if (nlris[i].length == 0) + continue; + + switch (i) { + case NLRI_UPDATE: + case NLRI_MP_UPDATE: + nlri_ret = bgp_nlri_parse(peer, NLRI_ATTR_ARG, + &nlris[i], 0); + break; + case NLRI_WITHDRAW: + case NLRI_MP_WITHDRAW: + nlri_ret = bgp_nlri_parse(peer, &attr, &nlris[i], 1); + break; + default: + nlri_ret = -1; + } + + if (nlri_ret < 0) { + zlog_err("%s [Error] Error parsing NLRI", peer->host); + if (peer->status == Established) + bgp_notify_send( + peer, BGP_NOTIFY_UPDATE_ERR, + i <= NLRI_WITHDRAW + ? BGP_NOTIFY_UPDATE_INVAL_NETWORK + : BGP_NOTIFY_UPDATE_OPT_ATTR_ERR); + bgp_attr_unintern_sub(&attr); + return -1; + } + } + + /* EoR checks + * + * 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) { + afi_t afi = 0; + safi_t safi; + + /* Non-MP IPv4/Unicast is a completely emtpy UPDATE - already + * checked + * update and withdraw NLRI lengths are 0. + */ + if (!attribute_len) { + afi = AFI_IP; + safi = SAFI_UNICAST; + } else if (attr.flag & ATTR_FLAG_BIT(BGP_ATTR_MP_UNREACH_NLRI) + && nlris[NLRI_MP_WITHDRAW].length == 0) { + afi = nlris[NLRI_MP_WITHDRAW].afi; + safi = nlris[NLRI_MP_WITHDRAW].safi; + } + + if (afi && peer->afc[afi][safi]) { + /* End-of-RIB received */ + if (!CHECK_FLAG(peer->af_sflags[afi][safi], + PEER_STATUS_EOR_RECEIVED)) { + SET_FLAG(peer->af_sflags[afi][safi], + PEER_STATUS_EOR_RECEIVED); + bgp_update_explicit_eors(peer); + } + + /* NSF delete stale route */ + if (peer->nsf[afi][safi]) + bgp_clear_stale_route(peer, afi, safi); + + if (bgp_debug_neighbor_events(peer)) { + zlog_debug("rcvd End-of-RIB for %s from %s", + afi_safi_print(afi, safi), + peer->host); + } + } } - } - - /* Logging the attribute. */ - if (attr_parse_ret == BGP_ATTR_PARSE_WITHDRAW || - BGP_DEBUG (update, UPDATE_IN) || - BGP_DEBUG (update, UPDATE_PREFIX)) - { - ret = bgp_dump_attr (peer, &attr, peer->rcvd_attr_str, BUFSIZ); - - if (attr_parse_ret == BGP_ATTR_PARSE_WITHDRAW) - zlog_err ("%s rcvd UPDATE with errors in attr(s)!! Withdrawing route.", - peer->host); - - if (ret && bgp_debug_update(peer, NULL, NULL, 1)) - { - zlog_debug ("%s rcvd UPDATE w/ attr: %s", peer->host, peer->rcvd_attr_str); - peer->rcvd_attr_printed = 1; - } - } - - /* Network Layer Reachability Information. */ - update_len = end - stream_pnt (s); - - if (update_len) - { - /* Set NLRI portion to structure. */ - nlris[NLRI_UPDATE].afi = AFI_IP; - nlris[NLRI_UPDATE].safi = SAFI_UNICAST; - nlris[NLRI_UPDATE].nlri = stream_pnt (s); - nlris[NLRI_UPDATE].length = update_len; - stream_forward_getp (s, update_len); - } - - if (BGP_DEBUG (update, UPDATE_IN)) - zlog_debug("%s rcvd UPDATE wlen %d attrlen %d alen %d", - peer->host, withdraw_len, attribute_len, update_len); - - /* Parse any given NLRIs */ - for (int i = NLRI_UPDATE; i < NLRI_TYPE_MAX; i++) - { - if (!nlris[i].nlri) - continue; - - /* NLRI is processed iff the peer if configured for the specific afi/safi */ - if (!peer->afc[nlris[i].afi][nlris[i].safi]) - { - zlog_info ("%s [Info] UPDATE for non-enabled AFI/SAFI %u/%u", - peer->host, nlris[i].afi, nlris[i].safi); - continue; - } - - /* EoR handled later */ - if (nlris[i].length == 0) - continue; - - switch (i) - { - case NLRI_UPDATE: - case NLRI_MP_UPDATE: - nlri_ret = bgp_nlri_parse (peer, NLRI_ATTR_ARG, &nlris[i], 0); - break; - case NLRI_WITHDRAW: - case NLRI_MP_WITHDRAW: - nlri_ret = bgp_nlri_parse (peer, &attr, &nlris[i], 1); - break; - default: - nlri_ret = -1; - } - - if (nlri_ret < 0) - { - zlog_err("%s [Error] Error parsing NLRI", peer->host); - if (peer->status == Established) - bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR, - i <= NLRI_WITHDRAW ? BGP_NOTIFY_UPDATE_INVAL_NETWORK - : BGP_NOTIFY_UPDATE_OPT_ATTR_ERR); - bgp_attr_unintern_sub (&attr); - return -1; - } - } - - /* EoR checks - * - * 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) - { - afi_t afi = 0; - safi_t safi; - - /* Non-MP IPv4/Unicast is a completely emtpy UPDATE - already checked - * update and withdraw NLRI lengths are 0. - */ - if (!attribute_len) - { - afi = AFI_IP; - safi = SAFI_UNICAST; - } - else if (attr.flag & ATTR_FLAG_BIT (BGP_ATTR_MP_UNREACH_NLRI) - && nlris[NLRI_MP_WITHDRAW].length == 0) - { - afi = nlris[NLRI_MP_WITHDRAW].afi; - safi = nlris[NLRI_MP_WITHDRAW].safi; - } - - if (afi && peer->afc[afi][safi]) - { - /* End-of-RIB received */ - if (!CHECK_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_EOR_RECEIVED)) - { - SET_FLAG (peer->af_sflags[afi][safi], - PEER_STATUS_EOR_RECEIVED); - bgp_update_explicit_eors(peer); - } - - /* NSF delete stale route */ - if (peer->nsf[afi][safi]) - bgp_clear_stale_route (peer, afi, safi); - - if (bgp_debug_neighbor_events(peer)) - { - zlog_debug ("rcvd End-of-RIB for %s from %s", - afi_safi_print (afi, safi), peer->host); - } - } - } - - /* Everything is done. We unintern temporary structures which - interned in bgp_attr_parse(). */ - bgp_attr_unintern_sub (&attr); - - /* If peering is stopped due to some reason, do not generate BGP - event. */ - if (peer->status != Established) - return 0; - - /* Increment packet counter. */ - peer->update_in++; - peer->update_time = bgp_clock (); - - /* Rearm holdtime timer */ - BGP_TIMER_OFF (peer->t_holdtime); - bgp_timer_set (peer); - - return 0; + + /* Everything is done. We unintern temporary structures which + interned in bgp_attr_parse(). */ + bgp_attr_unintern_sub(&attr); + + /* If peering is stopped due to some reason, do not generate BGP + event. */ + if (peer->status != Established) + return 0; + + /* Increment packet counter. */ + peer->update_in++; + peer->update_time = bgp_clock(); + + /* Rearm holdtime timer */ + BGP_TIMER_OFF(peer->t_holdtime); + bgp_timer_set(peer); + + return 0; } /* Notify message treatment function. */ -static void -bgp_notify_receive (struct peer *peer, bgp_size_t size) +static void bgp_notify_receive(struct peer *peer, bgp_size_t size) { - struct bgp_notify bgp_notify; - - if (peer->notify.data) - { - XFREE (MTYPE_TMP, peer->notify.data); - peer->notify.data = NULL; - peer->notify.length = 0; - } - - bgp_notify.code = stream_getc (peer->ibuf); - bgp_notify.subcode = stream_getc (peer->ibuf); - bgp_notify.length = size - 2; - bgp_notify.data = NULL; - - /* Preserv notify code and sub code. */ - peer->notify.code = bgp_notify.code; - peer->notify.subcode = bgp_notify.subcode; - /* For further diagnostic record returned Data. */ - if (bgp_notify.length) - { - peer->notify.length = size - 2; - peer->notify.data = XMALLOC (MTYPE_TMP, size - 2); - memcpy (peer->notify.data, stream_pnt (peer->ibuf), size - 2); - } - - /* For debug */ - { - int i; - int first = 0; - char c[4]; - - if (bgp_notify.length) - { - bgp_notify.data = XMALLOC (MTYPE_TMP, bgp_notify.length * 3); - for (i = 0; i < bgp_notify.length; i++) - if (first) - { - sprintf (c, " %02x", stream_getc (peer->ibuf)); - strcat (bgp_notify.data, c); - } - else - { - first = 1; - sprintf (c, "%02x", stream_getc (peer->ibuf)); - strcpy (bgp_notify.data, c); - } - bgp_notify.raw_data = (u_char*)peer->notify.data; - } - - bgp_notify_print(peer, &bgp_notify, "received"); - if (bgp_notify.data) - { - XFREE (MTYPE_TMP, bgp_notify.data); - bgp_notify.data = NULL; - bgp_notify.length = 0; - } - } - - /* peer count update */ - peer->notify_in++; - - peer->last_reset = PEER_DOWN_NOTIFY_RECEIVED; - - /* We have to check for Notify with Unsupported Optional Parameter. - in that case we fallback to open without the capability option. - But this done in bgp_stop. We just mark it here to avoid changing - the fsm tables. */ - if (bgp_notify.code == BGP_NOTIFY_OPEN_ERR && - bgp_notify.subcode == BGP_NOTIFY_OPEN_UNSUP_PARAM ) - UNSET_FLAG (peer->sflags, PEER_STATUS_CAPABILITY_OPEN); - - BGP_EVENT_ADD (peer, Receive_NOTIFICATION_message); + struct bgp_notify bgp_notify; + + if (peer->notify.data) { + XFREE(MTYPE_TMP, peer->notify.data); + peer->notify.data = NULL; + peer->notify.length = 0; + } + + bgp_notify.code = stream_getc(peer->ibuf); + bgp_notify.subcode = stream_getc(peer->ibuf); + bgp_notify.length = size - 2; + bgp_notify.data = NULL; + + /* Preserv notify code and sub code. */ + peer->notify.code = bgp_notify.code; + peer->notify.subcode = bgp_notify.subcode; + /* For further diagnostic record returned Data. */ + if (bgp_notify.length) { + peer->notify.length = size - 2; + peer->notify.data = XMALLOC(MTYPE_TMP, size - 2); + memcpy(peer->notify.data, stream_pnt(peer->ibuf), size - 2); + } + + /* For debug */ + { + int i; + int first = 0; + char c[4]; + + if (bgp_notify.length) { + bgp_notify.data = + XMALLOC(MTYPE_TMP, bgp_notify.length * 3); + for (i = 0; i < bgp_notify.length; i++) + if (first) { + sprintf(c, " %02x", + stream_getc(peer->ibuf)); + strcat(bgp_notify.data, c); + } else { + first = 1; + sprintf(c, "%02x", + stream_getc(peer->ibuf)); + strcpy(bgp_notify.data, c); + } + bgp_notify.raw_data = (u_char *)peer->notify.data; + } + + bgp_notify_print(peer, &bgp_notify, "received"); + if (bgp_notify.data) { + XFREE(MTYPE_TMP, bgp_notify.data); + bgp_notify.data = NULL; + bgp_notify.length = 0; + } + } + + /* peer count update */ + peer->notify_in++; + + peer->last_reset = PEER_DOWN_NOTIFY_RECEIVED; + + /* We have to check for Notify with Unsupported Optional Parameter. + in that case we fallback to open without the capability option. + But this done in bgp_stop. We just mark it here to avoid changing + the fsm tables. */ + if (bgp_notify.code == BGP_NOTIFY_OPEN_ERR + && bgp_notify.subcode == BGP_NOTIFY_OPEN_UNSUP_PARAM) + UNSET_FLAG(peer->sflags, PEER_STATUS_CAPABILITY_OPEN); + + BGP_EVENT_ADD(peer, Receive_NOTIFICATION_message); } /* Keepalive treatment function -- get keepalive send keepalive */ -static void -bgp_keepalive_receive (struct peer *peer, bgp_size_t size) +static void bgp_keepalive_receive(struct peer *peer, bgp_size_t size) { - if (bgp_debug_keepalive(peer)) - zlog_debug ("%s KEEPALIVE rcvd", peer->host); - - BGP_EVENT_ADD (peer, Receive_KEEPALIVE_message); + if (bgp_debug_keepalive(peer)) + zlog_debug("%s KEEPALIVE rcvd", peer->host); + + BGP_EVENT_ADD(peer, Receive_KEEPALIVE_message); } /* Route refresh message is received. */ -static void -bgp_route_refresh_receive (struct peer *peer, bgp_size_t size) +static void bgp_route_refresh_receive(struct peer *peer, bgp_size_t size) { - iana_afi_t pkt_afi; - afi_t afi; - safi_t pkt_safi, safi; - struct stream *s; - struct peer_af *paf; - struct update_group *updgrp; - struct peer *updgrp_peer; - - /* If peer does not have the capability, send notification. */ - if (! CHECK_FLAG (peer->cap, PEER_CAP_REFRESH_ADV)) - { - zlog_err ("%s [Error] BGP route refresh is not enabled", - peer->host); - bgp_notify_send (peer, - BGP_NOTIFY_HEADER_ERR, - BGP_NOTIFY_HEADER_BAD_MESTYPE); - return; - } - - /* Status must be Established. */ - if (peer->status != Established) - { - zlog_err ("%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, 0); - return; - } - - s = peer->ibuf; - - /* Parse packet. */ - pkt_afi = stream_getw (s); - (void)stream_getc (s); - pkt_safi = stream_getc (s); - - if (bgp_debug_update(peer, NULL, NULL, 0)) - zlog_debug ("%s rcvd REFRESH_REQ for afi/safi: %d/%d", - peer->host, pkt_afi, pkt_safi); - - /* Convert AFI, SAFI to internal values and check. */ - if (bgp_map_afi_safi_iana2int (pkt_afi, pkt_safi, &afi, &safi)) - { - zlog_info ("%s REFRESH_REQ for unrecognized afi/safi: %d/%d - ignored", - peer->host, pkt_afi, pkt_safi); - return; - } - - if (size != BGP_MSG_ROUTE_REFRESH_MIN_SIZE - BGP_HEADER_SIZE) - { - u_char *end; - u_char when_to_refresh; - u_char orf_type; - u_int16_t orf_len; - - if (size - (BGP_MSG_ROUTE_REFRESH_MIN_SIZE - BGP_HEADER_SIZE) < 5) - { - zlog_info ("%s ORF route refresh length error", peer->host); - bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0); - return; - } - - when_to_refresh = stream_getc (s); - end = stream_pnt (s) + (size - 5); - - while ((stream_pnt (s) + 2) < end) - { - orf_type = stream_getc (s); - orf_len = stream_getw (s); - - /* 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) - { - uint8_t *p_pnt = stream_pnt (s); - uint8_t *p_end = stream_pnt (s) + orf_len; - struct orf_prefix orfp; - u_char common = 0; - u_int32_t seq; - int psize; - char name[BUFSIZ]; - int ret = CMD_SUCCESS; - - if (bgp_debug_neighbor_events(peer)) - { - zlog_debug ("%s rcvd Prefixlist ORF(%d) length %d", - peer->host, orf_type, orf_len); + iana_afi_t pkt_afi; + afi_t afi; + safi_t pkt_safi, safi; + struct stream *s; + struct peer_af *paf; + struct update_group *updgrp; + struct peer *updgrp_peer; + + /* If peer does not have the capability, send notification. */ + if (!CHECK_FLAG(peer->cap, PEER_CAP_REFRESH_ADV)) { + zlog_err("%s [Error] BGP route refresh is not enabled", + peer->host); + bgp_notify_send(peer, BGP_NOTIFY_HEADER_ERR, + BGP_NOTIFY_HEADER_BAD_MESTYPE); + return; + } + + /* Status must be Established. */ + if (peer->status != Established) { + zlog_err( + "%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, 0); + return; + } + + s = peer->ibuf; + + /* Parse packet. */ + pkt_afi = stream_getw(s); + (void)stream_getc(s); + pkt_safi = stream_getc(s); + + if (bgp_debug_update(peer, NULL, NULL, 0)) + zlog_debug("%s rcvd REFRESH_REQ for afi/safi: %d/%d", + peer->host, pkt_afi, pkt_safi); + + /* Convert AFI, SAFI to internal values and check. */ + if (bgp_map_afi_safi_iana2int(pkt_afi, pkt_safi, &afi, &safi)) { + zlog_info( + "%s REFRESH_REQ for unrecognized afi/safi: %d/%d - ignored", + peer->host, pkt_afi, pkt_safi); + return; + } + + if (size != BGP_MSG_ROUTE_REFRESH_MIN_SIZE - BGP_HEADER_SIZE) { + u_char *end; + u_char when_to_refresh; + u_char orf_type; + u_int16_t orf_len; + + if (size - (BGP_MSG_ROUTE_REFRESH_MIN_SIZE - BGP_HEADER_SIZE) + < 5) { + zlog_info("%s ORF route refresh length error", + peer->host); + bgp_notify_send(peer, BGP_NOTIFY_CEASE, 0); + return; } - /* we're going to read at least 1 byte of common ORF header, - * and 7 bytes of ORF Address-filter entry from the stream - */ - if (orf_len < 7) - break; - - /* ORF prefix-list name */ - sprintf (name, "%s.%d.%d", peer->host, afi, safi); - - while (p_pnt < p_end) - { - /* If the ORF entry is malformed, want to read as much of it - * as possible without going beyond the bounds of the entry, - * to maximise debug information. - */ - int ok; - memset (&orfp, 0, sizeof (struct orf_prefix)); - common = *p_pnt++; - /* after ++: p_pnt <= p_end */ - if (common & ORF_COMMON_PART_REMOVE_ALL) - { - if (bgp_debug_neighbor_events(peer)) - zlog_debug ("%s rcvd Remove-All pfxlist ORF request", peer->host); - prefix_bgp_orf_remove_all (afi, name); - break; - } - ok = ((u_int32_t)(p_end - p_pnt) >= sizeof(u_int32_t)) ; - if (ok) - { - memcpy (&seq, p_pnt, sizeof (u_int32_t)); - p_pnt += sizeof (u_int32_t); - orfp.seq = ntohl (seq); - } - else - p_pnt = p_end ; - - if ((ok = (p_pnt < p_end))) - orfp.ge = *p_pnt++ ; /* value checked in prefix_bgp_orf_set() */ - if ((ok = (p_pnt < p_end))) - orfp.le = *p_pnt++ ; /* value checked in prefix_bgp_orf_set() */ - if ((ok = (p_pnt < p_end))) - orfp.p.prefixlen = *p_pnt++ ; - orfp.p.family = afi2family (afi); /* afi checked already */ - - psize = PSIZE (orfp.p.prefixlen); /* 0 if not ok */ - if (psize > prefix_blen(&orfp.p)) /* valid for family ? */ - { - ok = 0 ; - psize = prefix_blen(&orfp.p) ; - } - if (psize > (p_end - p_pnt)) /* valid for packet ? */ - { - ok = 0 ; - psize = p_end - p_pnt ; - } - - if (psize > 0) - memcpy (&orfp.p.u.prefix, p_pnt, psize); - p_pnt += psize; - - if (bgp_debug_neighbor_events(peer)) - { - char buf[INET6_BUFSIZ]; - - zlog_debug ("%s rcvd %s %s seq %u %s/%d ge %d le %d%s", - peer->host, - (common & ORF_COMMON_PART_REMOVE ? "Remove" : "Add"), - (common & ORF_COMMON_PART_DENY ? "deny" : "permit"), - orfp.seq, - inet_ntop (orfp.p.family, &orfp.p.u.prefix, buf, INET6_BUFSIZ), - orfp.p.prefixlen, orfp.ge, orfp.le, - ok ? "" : " MALFORMED"); - } - - if (ok) - ret = prefix_bgp_orf_set (name, afi, &orfp, - (common & ORF_COMMON_PART_DENY ? 0 : 1 ), - (common & ORF_COMMON_PART_REMOVE ? 0 : 1)); - - if (!ok || (ok && ret != CMD_SUCCESS)) - { - zlog_info ("%s Received misformatted prefixlist ORF." - " Remove All pfxlist", peer->host); - prefix_bgp_orf_remove_all (afi, name); - break; - } + when_to_refresh = stream_getc(s); + end = stream_pnt(s) + (size - 5); + + while ((stream_pnt(s) + 2) < end) { + orf_type = stream_getc(s); + orf_len = stream_getw(s); + + /* 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) { + uint8_t *p_pnt = stream_pnt(s); + uint8_t *p_end = stream_pnt(s) + orf_len; + struct orf_prefix orfp; + u_char common = 0; + u_int32_t seq; + int psize; + char name[BUFSIZ]; + int ret = CMD_SUCCESS; + + if (bgp_debug_neighbor_events(peer)) { + zlog_debug( + "%s rcvd Prefixlist ORF(%d) length %d", + peer->host, orf_type, orf_len); + } + + /* we're going to read at least 1 byte of common + * ORF header, + * and 7 bytes of ORF Address-filter entry from + * the stream + */ + if (orf_len < 7) + break; + + /* ORF prefix-list name */ + sprintf(name, "%s.%d.%d", peer->host, afi, + safi); + + while (p_pnt < p_end) { + /* If the ORF entry is malformed, want + * to read as much of it + * as possible without going beyond the + * bounds of the entry, + * to maximise debug information. + */ + int ok; + memset(&orfp, 0, + sizeof(struct orf_prefix)); + common = *p_pnt++; + /* after ++: p_pnt <= p_end */ + if (common + & ORF_COMMON_PART_REMOVE_ALL) { + if (bgp_debug_neighbor_events( + peer)) + zlog_debug( + "%s rcvd Remove-All pfxlist ORF request", + peer->host); + prefix_bgp_orf_remove_all(afi, + name); + break; + } + ok = ((u_int32_t)(p_end - p_pnt) + >= sizeof(u_int32_t)); + if (ok) { + memcpy(&seq, p_pnt, + sizeof(u_int32_t)); + p_pnt += sizeof(u_int32_t); + orfp.seq = ntohl(seq); + } else + p_pnt = p_end; + + if ((ok = (p_pnt < p_end))) + orfp.ge = + *p_pnt++; /* value + checked in + prefix_bgp_orf_set() + */ + if ((ok = (p_pnt < p_end))) + orfp.le = + *p_pnt++; /* value + checked in + prefix_bgp_orf_set() + */ + if ((ok = (p_pnt < p_end))) + orfp.p.prefixlen = *p_pnt++; + orfp.p.family = afi2family( + afi); /* afi checked already */ + + psize = PSIZE( + orfp.p.prefixlen); /* 0 if not + ok */ + if (psize + > prefix_blen( + &orfp.p)) /* valid for + family ? */ + { + ok = 0; + psize = prefix_blen(&orfp.p); + } + if (psize + > (p_end - p_pnt)) /* valid for + packet ? */ + { + ok = 0; + psize = p_end - p_pnt; + } + + if (psize > 0) + memcpy(&orfp.p.u.prefix, p_pnt, + psize); + p_pnt += psize; + + if (bgp_debug_neighbor_events(peer)) { + char buf[INET6_BUFSIZ]; + + zlog_debug( + "%s rcvd %s %s seq %u %s/%d ge %d le %d%s", + peer->host, + (common & ORF_COMMON_PART_REMOVE + ? "Remove" + : "Add"), + (common & ORF_COMMON_PART_DENY + ? "deny" + : "permit"), + orfp.seq, + inet_ntop( + orfp.p.family, + &orfp.p.u.prefix, + buf, + INET6_BUFSIZ), + orfp.p.prefixlen, + orfp.ge, orfp.le, + ok ? "" : " MALFORMED"); + } + + if (ok) + ret = prefix_bgp_orf_set( + name, afi, &orfp, + (common & ORF_COMMON_PART_DENY + ? 0 + : 1), + (common & ORF_COMMON_PART_REMOVE + ? 0 + : 1)); + + if (!ok || (ok && ret != CMD_SUCCESS)) { + zlog_info( + "%s Received misformatted prefixlist ORF." + " Remove All pfxlist", + peer->host); + prefix_bgp_orf_remove_all(afi, + name); + break; + } + } + + peer->orf_plist[afi][safi] = + prefix_bgp_orf_lookup(afi, name); + } + stream_forward_getp(s, orf_len); } + if (bgp_debug_neighbor_events(peer)) + zlog_debug("%s rcvd Refresh %s ORF request", peer->host, + when_to_refresh == REFRESH_DEFER + ? "Defer" + : "Immediate"); + if (when_to_refresh == REFRESH_DEFER) + return; + } - peer->orf_plist[afi][safi] = prefix_bgp_orf_lookup (afi, name); - } - stream_forward_getp (s, orf_len); + /* First update is deferred until ORF or ROUTE-REFRESH is received */ + if (CHECK_FLAG(peer->af_sflags[afi][safi], + PEER_STATUS_ORF_WAIT_REFRESH)) + UNSET_FLAG(peer->af_sflags[afi][safi], + PEER_STATUS_ORF_WAIT_REFRESH); + + paf = peer_af_find(peer, afi, safi); + if (paf && paf->subgroup) { + if (peer->orf_plist[afi][safi]) { + updgrp = PAF_UPDGRP(paf); + updgrp_peer = UPDGRP_PEER(updgrp); + updgrp_peer->orf_plist[afi][safi] = + peer->orf_plist[afi][safi]; + } + + /* If the peer is configured for default-originate clear the + * SUBGRP_STATUS_DEFAULT_ORIGINATE flag so that we will + * re-advertise the + * default + */ + if (CHECK_FLAG(paf->subgroup->sflags, + SUBGRP_STATUS_DEFAULT_ORIGINATE)) + UNSET_FLAG(paf->subgroup->sflags, + SUBGRP_STATUS_DEFAULT_ORIGINATE); } - if (bgp_debug_neighbor_events(peer)) - zlog_debug ("%s rcvd Refresh %s ORF request", peer->host, - when_to_refresh == REFRESH_DEFER ? "Defer" : "Immediate"); - if (when_to_refresh == REFRESH_DEFER) - return; - } - - /* First update is deferred until ORF or ROUTE-REFRESH is received */ - if (CHECK_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_ORF_WAIT_REFRESH)) - UNSET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_ORF_WAIT_REFRESH); - - paf = peer_af_find (peer, afi, safi); - if (paf && paf->subgroup) - { - if (peer->orf_plist[afi][safi]) - { - updgrp = PAF_UPDGRP(paf); - updgrp_peer = UPDGRP_PEER(updgrp); - updgrp_peer->orf_plist[afi][safi] = peer->orf_plist[afi][safi]; - } - - /* If the peer is configured for default-originate clear the - * SUBGRP_STATUS_DEFAULT_ORIGINATE flag so that we will re-advertise the - * default - */ - if (CHECK_FLAG (paf->subgroup->sflags, SUBGRP_STATUS_DEFAULT_ORIGINATE)) - UNSET_FLAG (paf->subgroup->sflags, SUBGRP_STATUS_DEFAULT_ORIGINATE); - } - - /* Perform route refreshment to the peer */ - bgp_announce_route (peer, afi, safi); + + /* Perform route refreshment to the peer */ + bgp_announce_route(peer, afi, safi); } -static int -bgp_capability_msg_parse (struct peer *peer, u_char *pnt, bgp_size_t length) +static int bgp_capability_msg_parse(struct peer *peer, u_char *pnt, + bgp_size_t length) { - u_char *end; - struct capability_mp_data mpc; - struct capability_header *hdr; - u_char action; - iana_afi_t pkt_afi; - afi_t afi; - safi_t pkt_safi, safi; - - end = pnt + length; - - while (pnt < end) - { - /* 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, 0); - return -1; - } - action = *pnt; - hdr = (struct capability_header *)(pnt + 1); - - /* 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, 0); - return -1; - } - - if (bgp_debug_neighbor_events(peer)) - zlog_debug ("%s CAPABILITY has action: %d, code: %u, length %u", - peer->host, 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, 0); - return -1; - } - - /* Fetch structure to the byte stream. */ - memcpy (&mpc, pnt + 3, sizeof (struct capability_mp_data)); - - /* We know MP Capability Code. */ - if (hdr->code == CAPABILITY_CODE_MP) - { - 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 " - "(%u/%u)", peer->host, pkt_afi, pkt_safi); - continue; - } - - /* Address family check. */ - if (bgp_debug_neighbor_events(peer)) - zlog_debug ("%s CAPABILITY has %s MP_EXT CAP for afi/safi: %u/%u", - peer->host, - action == CAPABILITY_ACTION_SET - ? "Advertising" : "Removing", - pkt_afi, pkt_safi); - - if (action == CAPABILITY_ACTION_SET) - { - peer->afc_recv[afi][safi] = 1; - if (peer->afc[afi][safi]) - { - peer->afc_nego[afi][safi] = 1; - bgp_announce_route (peer, afi, safi); - } - } - else - { - peer->afc_recv[afi][safi] = 0; - peer->afc_nego[afi][safi] = 0; - - if (peer_active_nego (peer)) - bgp_clear_route (peer, afi, safi); - else - BGP_EVENT_ADD (peer, BGP_Stop); - } - } - else - { - zlog_warn ("%s unrecognized capability code: %d - ignored", - peer->host, hdr->code); - } - pnt += hdr->length + 3; - } - return 0; + u_char *end; + struct capability_mp_data mpc; + struct capability_header *hdr; + u_char action; + iana_afi_t pkt_afi; + afi_t afi; + safi_t pkt_safi, safi; + + end = pnt + length; + + while (pnt < end) { + /* 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, 0); + return -1; + } + action = *pnt; + hdr = (struct capability_header *)(pnt + 1); + + /* 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, 0); + return -1; + } + + if (bgp_debug_neighbor_events(peer)) + zlog_debug( + "%s CAPABILITY has action: %d, code: %u, length %u", + peer->host, 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, 0); + return -1; + } + + /* Fetch structure to the byte stream. */ + memcpy(&mpc, pnt + 3, sizeof(struct capability_mp_data)); + + /* We know MP Capability Code. */ + if (hdr->code == CAPABILITY_CODE_MP) { + 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 " + "(%u/%u)", + peer->host, pkt_afi, pkt_safi); + continue; + } + + /* Address family check. */ + if (bgp_debug_neighbor_events(peer)) + zlog_debug( + "%s CAPABILITY has %s MP_EXT CAP for afi/safi: %u/%u", + peer->host, + action == CAPABILITY_ACTION_SET + ? "Advertising" + : "Removing", + pkt_afi, pkt_safi); + + if (action == CAPABILITY_ACTION_SET) { + peer->afc_recv[afi][safi] = 1; + if (peer->afc[afi][safi]) { + peer->afc_nego[afi][safi] = 1; + bgp_announce_route(peer, afi, safi); + } + } else { + peer->afc_recv[afi][safi] = 0; + peer->afc_nego[afi][safi] = 0; + + if (peer_active_nego(peer)) + bgp_clear_route(peer, afi, safi); + else + BGP_EVENT_ADD(peer, BGP_Stop); + } + } else { + zlog_warn( + "%s unrecognized capability code: %d - ignored", + peer->host, hdr->code); + } + pnt += hdr->length + 3; + } + return 0; } -/* Dynamic Capability is received. +/* Dynamic Capability is received. * * This is exported for unit-test purposes */ -int -bgp_capability_receive (struct peer *peer, bgp_size_t size) +int bgp_capability_receive(struct peer *peer, bgp_size_t size) { - u_char *pnt; - - /* Fetch pointer. */ - pnt = stream_pnt (peer->ibuf); - - 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)) - { - zlog_err ("%s [Error] BGP dynamic capability is not enabled", - peer->host); - bgp_notify_send (peer, - BGP_NOTIFY_HEADER_ERR, - BGP_NOTIFY_HEADER_BAD_MESTYPE); - return -1; - } - - /* Status must be Established. */ - if (peer->status != Established) - { - zlog_err ("%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, 0); - return -1; - } - - /* Parse packet. */ - return bgp_capability_msg_parse (peer, pnt, size); + u_char *pnt; + + /* Fetch pointer. */ + pnt = stream_pnt(peer->ibuf); + + 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)) { + zlog_err("%s [Error] BGP dynamic capability is not enabled", + peer->host); + bgp_notify_send(peer, BGP_NOTIFY_HEADER_ERR, + BGP_NOTIFY_HEADER_BAD_MESTYPE); + return -1; + } + + /* Status must be Established. */ + if (peer->status != Established) { + zlog_err( + "%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, 0); + return -1; + } + + /* Parse packet. */ + return bgp_capability_msg_parse(peer, pnt, size); } /* BGP read utility function. */ -static int -bgp_read_packet (struct peer *peer) +static int bgp_read_packet(struct peer *peer) { - int nbytes; - int readsize; + int nbytes; + int readsize; - readsize = peer->packet_size - stream_get_endp (peer->ibuf); + readsize = peer->packet_size - stream_get_endp(peer->ibuf); - /* If size is zero then return. */ - if (! readsize) - return 0; + /* If size is zero then return. */ + if (!readsize) + return 0; - /* Read packet from fd. */ - nbytes = stream_read_try (peer->ibuf, peer->fd, readsize); + /* Read packet from fd. */ + nbytes = stream_read_try(peer->ibuf, peer->fd, readsize); - /* If read byte is smaller than zero then error occured. */ - if (nbytes < 0) - { - /* Transient error should retry */ - if (nbytes == -2) - return -1; + /* If read byte is smaller than zero then error occured. */ + if (nbytes < 0) { + /* Transient error should retry */ + if (nbytes == -2) + return -1; - zlog_err ("%s [Error] bgp_read_packet error: %s", - peer->host, safe_strerror (errno)); + zlog_err("%s [Error] bgp_read_packet error: %s", peer->host, + safe_strerror(errno)); - if (peer->status == Established) - { - if (CHECK_FLAG (peer->sflags, PEER_STATUS_NSF_MODE)) - { - peer->last_reset = PEER_DOWN_NSF_CLOSE_SESSION; - SET_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT); - } - else - peer->last_reset = PEER_DOWN_CLOSE_SESSION; - } + if (peer->status == Established) { + if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_MODE)) { + peer->last_reset = PEER_DOWN_NSF_CLOSE_SESSION; + SET_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT); + } else + peer->last_reset = PEER_DOWN_CLOSE_SESSION; + } - BGP_EVENT_ADD (peer, TCP_fatal_error); - return -1; - } + BGP_EVENT_ADD(peer, TCP_fatal_error); + return -1; + } - /* When read byte is zero : clear bgp peer and return */ - if (nbytes == 0) - { - if (bgp_debug_neighbor_events(peer)) - zlog_debug ("%s [Event] BGP connection closed fd %d", - peer->host, peer->fd); + /* When read byte is zero : clear bgp peer and return */ + if (nbytes == 0) { + if (bgp_debug_neighbor_events(peer)) + zlog_debug("%s [Event] BGP connection closed fd %d", + peer->host, peer->fd); + + if (peer->status == Established) { + if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_MODE)) { + peer->last_reset = PEER_DOWN_NSF_CLOSE_SESSION; + SET_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT); + } else + peer->last_reset = PEER_DOWN_CLOSE_SESSION; + } - if (peer->status == Established) - { - if (CHECK_FLAG (peer->sflags, PEER_STATUS_NSF_MODE)) - { - peer->last_reset = PEER_DOWN_NSF_CLOSE_SESSION; - SET_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT); - } - else - peer->last_reset = PEER_DOWN_CLOSE_SESSION; + BGP_EVENT_ADD(peer, TCP_connection_closed); + return -1; } - BGP_EVENT_ADD (peer, TCP_connection_closed); - return -1; - } - - /* We read partial packet. */ - if (stream_get_endp (peer->ibuf) != peer->packet_size) - return -1; + /* We read partial packet. */ + if (stream_get_endp(peer->ibuf) != peer->packet_size) + return -1; - return 0; + return 0; } /* Marker check. */ -static int -bgp_marker_all_one (struct stream *s, int length) +static int bgp_marker_all_one(struct stream *s, int length) { - int i; + int i; - for (i = 0; i < length; i++) - if (s->data[i] != 0xff) - return 0; + for (i = 0; i < length; i++) + if (s->data[i] != 0xff) + return 0; - return 1; + return 1; } /* Starting point of packet process function. */ -int -bgp_read (struct thread *thread) +int bgp_read(struct thread *thread) { - int ret; - u_char type = 0; - struct peer *peer; - bgp_size_t size; - char notify_data_length[2]; - u_int32_t notify_out; - - /* Yes first of all get peer pointer. */ - peer = THREAD_ARG (thread); - peer->t_read = NULL; - - /* Note notify_out so we can check later to see if we sent another one */ - notify_out = peer->notify_out; - - /* For non-blocking IO check. */ - if (peer->status == Connect) - { - bgp_connect_check (peer, 1); - goto done; - } - else - { - if (peer->fd < 0) - { - zlog_err ("bgp_read peer's fd is negative value %d", peer->fd); - return -1; + int ret; + u_char type = 0; + struct peer *peer; + bgp_size_t size; + char notify_data_length[2]; + u_int32_t notify_out; + + /* Yes first of all get peer pointer. */ + peer = THREAD_ARG(thread); + peer->t_read = NULL; + + /* Note notify_out so we can check later to see if we sent another one + */ + notify_out = peer->notify_out; + + /* For non-blocking IO check. */ + if (peer->status == Connect) { + bgp_connect_check(peer, 1); + goto done; + } else { + if (peer->fd < 0) { + zlog_err("bgp_read peer's fd is negative value %d", + peer->fd); + return -1; + } + BGP_READ_ON(peer->t_read, bgp_read, peer->fd); } - BGP_READ_ON (peer->t_read, bgp_read, peer->fd); - } - - /* Read packet header to determine type of the packet */ - if (peer->packet_size == 0) - peer->packet_size = BGP_HEADER_SIZE; - - if (stream_get_endp (peer->ibuf) < BGP_HEADER_SIZE) - { - ret = bgp_read_packet (peer); - - /* Header read error or partial read packet. */ - if (ret < 0) - goto done; - - /* Get size and type. */ - stream_forward_getp (peer->ibuf, BGP_MARKER_SIZE); - memcpy (notify_data_length, stream_pnt (peer->ibuf), 2); - size = stream_getw (peer->ibuf); - type = stream_getc (peer->ibuf); - - /* Marker check */ - if (((type == BGP_MSG_OPEN) || (type == BGP_MSG_KEEPALIVE)) - && ! bgp_marker_all_one (peer->ibuf, BGP_MARKER_SIZE)) - { - bgp_notify_send (peer, - BGP_NOTIFY_HEADER_ERR, - BGP_NOTIFY_HEADER_NOT_SYNC); - goto done; + + /* Read packet header to determine type of the packet */ + if (peer->packet_size == 0) + peer->packet_size = BGP_HEADER_SIZE; + + if (stream_get_endp(peer->ibuf) < BGP_HEADER_SIZE) { + ret = bgp_read_packet(peer); + + /* Header read error or partial read packet. */ + if (ret < 0) + goto done; + + /* Get size and type. */ + stream_forward_getp(peer->ibuf, BGP_MARKER_SIZE); + memcpy(notify_data_length, stream_pnt(peer->ibuf), 2); + size = stream_getw(peer->ibuf); + type = stream_getc(peer->ibuf); + + /* Marker check */ + if (((type == BGP_MSG_OPEN) || (type == BGP_MSG_KEEPALIVE)) + && !bgp_marker_all_one(peer->ibuf, BGP_MARKER_SIZE)) { + bgp_notify_send(peer, BGP_NOTIFY_HEADER_ERR, + BGP_NOTIFY_HEADER_NOT_SYNC); + goto done; + } + + /* BGP type check. */ + if (type != BGP_MSG_OPEN && type != BGP_MSG_UPDATE + && type != BGP_MSG_NOTIFY && type != BGP_MSG_KEEPALIVE + && type != BGP_MSG_ROUTE_REFRESH_NEW + && type != BGP_MSG_ROUTE_REFRESH_OLD + && type != BGP_MSG_CAPABILITY) { + if (bgp_debug_neighbor_events(peer)) + zlog_debug("%s unknown message type 0x%02x", + peer->host, type); + bgp_notify_send_with_data(peer, BGP_NOTIFY_HEADER_ERR, + BGP_NOTIFY_HEADER_BAD_MESTYPE, + &type, 1); + goto done; + } + /* Mimimum packet length check. */ + if ((size < BGP_HEADER_SIZE) || (size > BGP_MAX_PACKET_SIZE) + || (type == BGP_MSG_OPEN && size < BGP_MSG_OPEN_MIN_SIZE) + || (type == BGP_MSG_UPDATE + && size < BGP_MSG_UPDATE_MIN_SIZE) + || (type == BGP_MSG_NOTIFY + && size < BGP_MSG_NOTIFY_MIN_SIZE) + || (type == BGP_MSG_KEEPALIVE + && size != BGP_MSG_KEEPALIVE_MIN_SIZE) + || (type == BGP_MSG_ROUTE_REFRESH_NEW + && size < BGP_MSG_ROUTE_REFRESH_MIN_SIZE) + || (type == BGP_MSG_ROUTE_REFRESH_OLD + && size < BGP_MSG_ROUTE_REFRESH_MIN_SIZE) + || (type == BGP_MSG_CAPABILITY + && size < BGP_MSG_CAPABILITY_MIN_SIZE)) { + if (bgp_debug_neighbor_events(peer)) + zlog_debug("%s bad message length - %d for %s", + peer->host, size, + type == 128 + ? "ROUTE-REFRESH" + : bgp_type_str[(int)type]); + bgp_notify_send_with_data(peer, BGP_NOTIFY_HEADER_ERR, + BGP_NOTIFY_HEADER_BAD_MESLEN, + (u_char *)notify_data_length, + 2); + goto done; + } + + /* Adjust size to message length. */ + peer->packet_size = size; } - /* BGP type check. */ - if (type != BGP_MSG_OPEN && type != BGP_MSG_UPDATE - && type != BGP_MSG_NOTIFY && type != BGP_MSG_KEEPALIVE - && type != BGP_MSG_ROUTE_REFRESH_NEW - && type != BGP_MSG_ROUTE_REFRESH_OLD - && type != BGP_MSG_CAPABILITY) - { - if (bgp_debug_neighbor_events(peer)) - zlog_debug ("%s unknown message type 0x%02x", - peer->host, type); - bgp_notify_send_with_data (peer, - BGP_NOTIFY_HEADER_ERR, - BGP_NOTIFY_HEADER_BAD_MESTYPE, - &type, 1); - goto done; + ret = bgp_read_packet(peer); + if (ret < 0) + goto done; + + /* Get size and type again. */ + (void)stream_getw_from(peer->ibuf, BGP_MARKER_SIZE); + type = stream_getc_from(peer->ibuf, BGP_MARKER_SIZE + 2); + + /* BGP packet dump function. */ + bgp_dump_packet(peer, type, peer->ibuf); + + size = (peer->packet_size - BGP_HEADER_SIZE); + + /* Read rest of the packet and call each sort of packet routine */ + switch (type) { + case BGP_MSG_OPEN: + peer->open_in++; + bgp_open_receive(peer, size); /* XXX return value ignored! */ + break; + case BGP_MSG_UPDATE: + peer->readtime = monotime(NULL); + bgp_update_receive(peer, size); + break; + case BGP_MSG_NOTIFY: + bgp_notify_receive(peer, size); + break; + case BGP_MSG_KEEPALIVE: + peer->readtime = monotime(NULL); + bgp_keepalive_receive(peer, size); + break; + case BGP_MSG_ROUTE_REFRESH_NEW: + case BGP_MSG_ROUTE_REFRESH_OLD: + peer->refresh_in++; + bgp_route_refresh_receive(peer, size); + break; + case BGP_MSG_CAPABILITY: + peer->dynamic_cap_in++; + bgp_capability_receive(peer, size); + break; } - /* Mimimum packet length check. */ - if ((size < BGP_HEADER_SIZE) - || (size > BGP_MAX_PACKET_SIZE) - || (type == BGP_MSG_OPEN && size < BGP_MSG_OPEN_MIN_SIZE) - || (type == BGP_MSG_UPDATE && size < BGP_MSG_UPDATE_MIN_SIZE) - || (type == BGP_MSG_NOTIFY && size < BGP_MSG_NOTIFY_MIN_SIZE) - || (type == BGP_MSG_KEEPALIVE && size != BGP_MSG_KEEPALIVE_MIN_SIZE) - || (type == BGP_MSG_ROUTE_REFRESH_NEW && size < BGP_MSG_ROUTE_REFRESH_MIN_SIZE) - || (type == BGP_MSG_ROUTE_REFRESH_OLD && size < BGP_MSG_ROUTE_REFRESH_MIN_SIZE) - || (type == BGP_MSG_CAPABILITY && size < BGP_MSG_CAPABILITY_MIN_SIZE)) - { - if (bgp_debug_neighbor_events(peer)) - zlog_debug ("%s bad message length - %d for %s", - peer->host, size, - type == 128 ? "ROUTE-REFRESH" : - bgp_type_str[(int) type]); - bgp_notify_send_with_data (peer, - BGP_NOTIFY_HEADER_ERR, - BGP_NOTIFY_HEADER_BAD_MESLEN, - (u_char *) notify_data_length, 2); - goto done; + + /* If reading this packet caused us to send a NOTIFICATION then store a + * copy + * of the packet for troubleshooting purposes + */ + if (notify_out < peer->notify_out) { + memcpy(peer->last_reset_cause, peer->ibuf->data, + peer->packet_size); + peer->last_reset_cause_size = peer->packet_size; + notify_out = peer->notify_out; + } + + /* Clear input buffer. */ + peer->packet_size = 0; + if (peer->ibuf) + stream_reset(peer->ibuf); + +done: + /* If reading this packet caused us to send a NOTIFICATION then store a + * copy + * of the packet for troubleshooting purposes + */ + if (notify_out < peer->notify_out) { + memcpy(peer->last_reset_cause, peer->ibuf->data, + peer->packet_size); + peer->last_reset_cause_size = peer->packet_size; } - /* Adjust size to message length. */ - peer->packet_size = size; - } - - ret = bgp_read_packet (peer); - if (ret < 0) - goto done; - - /* Get size and type again. */ - (void)stream_getw_from (peer->ibuf, BGP_MARKER_SIZE); - type = stream_getc_from (peer->ibuf, BGP_MARKER_SIZE + 2); - - /* BGP packet dump function. */ - bgp_dump_packet (peer, type, peer->ibuf); - - size = (peer->packet_size - BGP_HEADER_SIZE); - - /* Read rest of the packet and call each sort of packet routine */ - switch (type) - { - case BGP_MSG_OPEN: - peer->open_in++; - bgp_open_receive (peer, size); /* XXX return value ignored! */ - break; - case BGP_MSG_UPDATE: - peer->readtime = monotime (NULL); - bgp_update_receive (peer, size); - break; - case BGP_MSG_NOTIFY: - bgp_notify_receive (peer, size); - break; - case BGP_MSG_KEEPALIVE: - peer->readtime = monotime (NULL); - bgp_keepalive_receive (peer, size); - break; - case BGP_MSG_ROUTE_REFRESH_NEW: - case BGP_MSG_ROUTE_REFRESH_OLD: - peer->refresh_in++; - bgp_route_refresh_receive (peer, size); - break; - case BGP_MSG_CAPABILITY: - peer->dynamic_cap_in++; - bgp_capability_receive (peer, size); - break; - } - - /* If reading this packet caused us to send a NOTIFICATION then store a copy - * of the packet for troubleshooting purposes - */ - if (notify_out < peer->notify_out) - { - memcpy(peer->last_reset_cause, peer->ibuf->data, peer->packet_size); - peer->last_reset_cause_size = peer->packet_size; - notify_out = peer->notify_out; - } - - /* Clear input buffer. */ - peer->packet_size = 0; - if (peer->ibuf) - stream_reset (peer->ibuf); - - done: - /* If reading this packet caused us to send a NOTIFICATION then store a copy - * of the packet for troubleshooting purposes - */ - if (notify_out < peer->notify_out) - { - memcpy(peer->last_reset_cause, peer->ibuf->data, peer->packet_size); - peer->last_reset_cause_size = peer->packet_size; - } - - return 0; + return 0; } |
