diff options
Diffstat (limited to 'bgpd/bgp_msg_write.c')
-rw-r--r-- | bgpd/bgp_msg_write.c | 2902 |
1 files changed, 639 insertions, 2263 deletions
diff --git a/bgpd/bgp_msg_write.c b/bgpd/bgp_msg_write.c index d15f1a26..fbc7ba0e 100644 --- a/bgpd/bgp_msg_write.c +++ b/bgpd/bgp_msg_write.c @@ -21,6 +21,11 @@ * Boston, MA 02111-1307, USA. */ +#include "bgpd/bgp_common.h" +#include "bgpd/bgp_msg_write.h" +#include "bgpd/bgp_route_refresh.h" + + #include <zebra.h> #include "thread.h" @@ -54,2423 +59,794 @@ #include "bgpd/bgp_advertise.h" #include "bgpd/bgp_vty.h" -int stream_put_prefix (struct stream *, struct prefix *); - -/* Set up BGP packet marker and packet type. */ -static int -bgp_packet_set_marker (struct stream *s, u_char type) -{ - int i; - - /* 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); - - /* BGP packet type. */ - stream_putc (s, type); - - /* Return current stream size. */ - return stream_get_endp (s); -} - -/* Set BGP packet header size entry. If size is zero then use current - stream size. */ -static int -bgp_packet_set_size (struct stream *s) -{ - int cp; - - /* Preserve current pointer. */ - cp = stream_get_endp (s); - stream_putw_at (s, BGP_MARKER_SIZE, cp); +/*============================================================================== + * BGP Engine BGP Message encoding and sending. + * + * + */ - return cp; -} +/*============================================================================== + * NOTIFICATION and KEEPALIVE + */ -/* Add new packet to the peer. */ -static void -bgp_packet_add (struct peer *peer, struct stream *s) +/*------------------------------------------------------------------------------ + * Make NOTIFICATION message and dispatch. + * + * NB: the write buffers will have been flushed -- so expect success ! + * + * Returns: 2 => written to TCP -- it's gone + * 1 => written to wbuff -- waiting for socket + * 0 => nothing written -- wbuff was not empty ! + * -1 => failed -- error event generated + */ +extern int +bgp_msg_write_notification(bgp_connection connection, bgp_notify notification) { - /* Add packet to the end of list. */ - stream_fifo_push (peer->obuf, s); -} + struct stream *s = connection->obuf ; + int length; -/* Free first packet. */ -static void -bgp_packet_delete (struct peer *peer) -{ - stream_free (stream_fifo_pop (peer->obuf)); -} + assert(notification != NULL) ; -/* Check file descriptor whether connect is established. */ -static void -bgp_connect_check (struct peer *peer) -{ - int status; - socklen_t slen; - int ret; + /* Make NOTIFY message header */ + bgp_packet_set_marker (s, BGP_MSG_NOTIFY); - /* Anyway I have to reset read and write thread. */ - BGP_READ_OFF (peer->t_read); - BGP_WRITE_OFF (peer->t_write); + /* Set notify code and subcode */ + stream_putc(s, bgp_notify_get_code(notification)) ; + stream_putc(s, bgp_notify_get_subcode(notification)) ; - /* Check file descriptor. */ - slen = sizeof (status); - ret = getsockopt(peer->fd, SOL_SOCKET, SO_ERROR, (void *) &status, &slen); + /* Copy the data portion, if any. */ + length = bgp_notify_get_length(notification) ; + if (length != 0) + stream_write(s, bgp_notify_get_data(notification), length) ; - /* If getsockopt is fail, this is fatal error. */ - if (ret < 0) - { - zlog (peer->log, LOG_INFO, "can't get sockopt for nonblocking connect"); - BGP_EVENT_ADD (peer, TCP_fatal_error); - return; - } + /* Set and get BGP packet length. */ + length = bgp_packet_set_size(s); - /* When status is 0 then TCP connection is established. */ - if (status == 0) - { - BGP_EVENT_ADD (peer, TCP_connection_open); - } - else - { - if (BGP_DEBUG (events, EVENTS)) - plog_debug (peer->log, "%s [Event] Connect failed (%s)", - peer->host, safe_strerror (errno)); - BGP_EVENT_ADD (peer, TCP_connection_open_failed); - } -} + /* Logging */ + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug("%s send message type %d, length (incl. header) %d", + connection->host, BGP_MSG_NOTIFY, length) ; -/* Make BGP update packet. */ -static struct stream * -bgp_update_packet (struct peer *peer, afi_t afi, safi_t safi) + /* For debug */ + { + bgp_notify text_form ; + const char* form ; + char c[4] ; + char* p ; + + length = bgp_notify_get_length(notification) ; + p = bgp_notify_get_data(notification) ; + + /* Make new copy of notification, with data portion large enough + * for the data rendered as hex characters. + */ + text_form = bgp_notify_new(bgp_notify_get_code(notification), + bgp_notify_get_subcode(notification), + (length * 3)) ; + form = "%02x" ; + while (length--) + { + sprintf (c, form, *p++) ; + text_form = bgp_notify_append_data(text_form, c, strlen(c)) ; + form = " %02x" ; + } ; + text_form = bgp_notify_append_data(text_form, "\0", 1) ; + + /* TODO: restore bgp_notify_print */ +#if 0 + bgp_notify_print(peer, text_form, "sending") ; +#endif + bgp_notify_free(&text_form) ; + } ; + + /* Finally -- write the obuf away */ + return bgp_connection_write(connection, s) ; +} ; + +/*------------------------------------------------------------------------------ + * Make KEEPALIVE message and dispatch. + * + * NB: does nothing if the write buffer is not empty. This is not a problem, + * the KEEPALIVE is redundant if there is stuff waiting to go ! + * + * KEEPALIVE is sent in response to OPEN, and that MUST be sent. But if the + * buffers are full at that point, something is broken ! + * + * Returns: 2 => written to TCP -- it's gone + * 1 => written to wbuff -- waiting for socket + * 0 => nothing written -- wbuff was not empty ! + * -1 => failed -- error event generated + */ +extern int +bgp_msg_send_keepalive(bgp_connection connection) { - struct stream *s; - struct bgp_adj_out *adj; - struct bgp_advertise *adv; - struct stream *packet; - struct bgp_node *rn = NULL; - struct bgp_info *binfo = NULL; - bgp_size_t total_attr_len = 0; - unsigned long pos; - char buf[BUFSIZ]; - - s = peer->work; - stream_reset (s); - - adv = FIFO_HEAD (&peer->sync[afi][safi]->update); - - while (adv) - { - assert (adv->rn); - rn = adv->rn; - adj = adv->adj; - if (adv->binfo) - binfo = adv->binfo; - - /* When remaining space can't include NLRI and it's length. */ - if (STREAM_REMAIN (s) <= BGP_NLRI_LENGTH + PSIZE (rn->p.prefixlen)) - break; - - /* If packet is empty, set attribute. */ - if (stream_empty (s)) - { - struct prefix_rd *prd = NULL; - u_char *tag = NULL; - struct peer *from = NULL; - - if (rn->prn) - prd = (struct prefix_rd *) &rn->prn->p; - if (binfo && binfo->extra) - { - tag = binfo->extra->tag; - from = binfo->peer; - } - - bgp_packet_set_marker (s, BGP_MSG_UPDATE); - stream_putw (s, 0); - pos = stream_get_endp (s); - stream_putw (s, 0); - total_attr_len = bgp_packet_attribute (NULL, peer, s, - adv->baa->attr, - &rn->p, afi, safi, - from, prd, tag); - stream_putw_at (s, pos, total_attr_len); - } - - if (afi == AFI_IP && safi == SAFI_UNICAST) - stream_put_prefix (s, &rn->p); - - if (BGP_DEBUG (update, UPDATE_OUT)) - zlog (peer->log, LOG_DEBUG, "%s send UPDATE %s/%d", - peer->host, - inet_ntop (rn->p.family, &(rn->p.u.prefix), buf, BUFSIZ), - rn->p.prefixlen); - - /* Synchnorize attribute. */ - if (adj->attr) - bgp_attr_unintern (adj->attr); - else - peer->scount[afi][safi]++; - - adj->attr = bgp_attr_intern (adv->baa->attr); - - adv = bgp_advertise_clean (peer, adj, afi, safi); - - if (! (afi == AFI_IP && safi == SAFI_UNICAST)) - break; - } + struct stream *s = connection->obuf ; + int length; - if (! stream_empty (s)) - { - bgp_packet_set_size (s); - packet = stream_dup (s); - bgp_packet_add (peer, packet); - BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); - stream_reset (s); - return packet; - } - return NULL; -} + if (!bgp_connection_write_empty(connection)) + return 0 ; -static struct stream * -bgp_update_packet_eor (struct peer *peer, afi_t afi, safi_t safi) -{ - struct stream *s; - struct stream *packet; + /* Make KEEPALIVE message -- comprises header only */ + bgp_packet_set_marker(s, BGP_MSG_KEEPALIVE); + length = bgp_packet_set_size(s); - if (DISABLE_BGP_ANNOUNCE) - return NULL; + /* Dump packet if debug option is set. */ + /* bgp_packet_dump (s); */ + if (BGP_DEBUG (keepalive, KEEPALIVE)) + zlog_debug ("%s sending KEEPALIVE", connection->host); if (BGP_DEBUG (normal, NORMAL)) - zlog_debug ("send End-of-RIB for %s to %s", afi_safi_print (afi, safi), peer->host); - - s = stream_new (BGP_MAX_PACKET_SIZE); + zlog_debug ("%s send message type %d, length (incl. header) %d", + connection->host, BGP_MSG_KEEPALIVE, length); - /* Make BGP update packet. */ - bgp_packet_set_marker (s, BGP_MSG_UPDATE); + /* Finally -- write the obuf away */ + return bgp_connection_write(connection, s) ; +} ; - /* Unfeasible Routes Length */ - stream_putw (s, 0); +/*============================================================================== + * OPEN message -- transform bgp_open_state into BGP message + */ - if (afi == AFI_IP && safi == SAFI_UNICAST) - { - /* Total Path Attribute Length */ - stream_putw (s, 0); - } - else - { - /* 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, afi); - stream_putc (s, safi); - } +static void +bgp_open_options(struct stream *s, bgp_open_state open_state) ; - bgp_packet_set_size (s); - packet = stream_dup (s); - bgp_packet_add (peer, packet); - stream_free (s); - return packet; -} +static void +bgp_open_capability_orf (struct stream *s, iAFI_t afi, iSAFI_t safi, + u_char cap_code, u_char orf_type, u_char mode) ; -/* Make BGP withdraw packet. */ -static struct stream * -bgp_withdraw_packet (struct peer *peer, afi_t afi, safi_t safi) +/*------------------------------------------------------------------------------ + * Make OPEN message and dispatch. + * + * OPEN is the first message to be sent. If the buffers are not empty, + * something is badly wrong ! + * + * Returns: 2 => written to TCP -- it's gone + * 1 => written to wbuff -- waiting for socket + * 0 => nothing written -- wbuff was too full !!! + * -1 => failed -- error event generated + */ +extern int +bgp_msg_send_open(bgp_connection connection, bgp_open_state open_state) { - struct stream *s; - struct stream *packet; - struct bgp_adj_out *adj; - struct bgp_advertise *adv; - struct bgp_node *rn; - unsigned long pos; - bgp_size_t unfeasible_len; - bgp_size_t total_attr_len; - char buf[BUFSIZ]; - - s = peer->work; - stream_reset (s); - - while ((adv = FIFO_HEAD (&peer->sync[afi][safi]->withdraw)) != NULL) - { - assert (adv->rn); - adj = adv->adj; - rn = adv->rn; - - if (STREAM_REMAIN (s) - < (BGP_NLRI_LENGTH + BGP_TOTAL_ATTR_LEN + PSIZE (rn->p.prefixlen))) - break; - - if (stream_empty (s)) - { - bgp_packet_set_marker (s, BGP_MSG_UPDATE); - stream_putw (s, 0); - } - - if (afi == AFI_IP && safi == SAFI_UNICAST) - stream_put_prefix (s, &rn->p); - else - { - struct prefix_rd *prd = NULL; - - if (rn->prn) - prd = (struct prefix_rd *) &rn->prn->p; - pos = stream_get_endp (s); - stream_putw (s, 0); - total_attr_len - = bgp_packet_withdraw (peer, s, &rn->p, afi, safi, prd, NULL); + struct stream *s = connection->obuf ; + int length ; - /* Set total path attribute length. */ - stream_putw_at (s, pos, total_attr_len); - } + assert(bgp_connection_write_empty(connection)) ; - if (BGP_DEBUG (update, UPDATE_OUT)) - zlog (peer->log, LOG_DEBUG, "%s send UPDATE %s/%d -- unreachable", - peer->host, - inet_ntop (rn->p.family, &(rn->p.u.prefix), buf, BUFSIZ), - rn->p.prefixlen); - - peer->scount[afi][safi]--; - - bgp_adj_out_remove (rn, adj, peer, afi, safi); - bgp_unlock_node (rn); - - if (! (afi == AFI_IP && safi == SAFI_UNICAST)) - break; - } - - if (! stream_empty (s)) - { - if (afi == AFI_IP && safi == SAFI_UNICAST) - { - unfeasible_len - = stream_get_endp (s) - BGP_HEADER_SIZE - BGP_UNFEASIBLE_LEN; - stream_putw_at (s, BGP_HEADER_SIZE, unfeasible_len); - stream_putw (s, 0); - } - bgp_packet_set_size (s); - packet = stream_dup (s); - bgp_packet_add (peer, packet); - stream_reset (s); - return packet; - } + /* Make OPEN message header */ + bgp_packet_set_marker(s, BGP_MSG_OPEN) ; - return NULL; -} + /* Set OPEN message fixed part */ + stream_putc(s, BGP_VERSION_4) ; + stream_putw(s, (open_state->my_as <= BGP_AS_MAX) + ? (u_int16_t) open_state->my_as : BGP_AS_TRANS) ; + stream_putw(s, open_state->holdtime) ; + stream_putl(s, open_state->bgp_id) ; -void -bgp_default_update_send (struct peer *peer, struct attr *attr, - afi_t afi, safi_t safi, struct peer *from) -{ - struct stream *s; - struct stream *packet; - struct prefix p; - unsigned long pos; - bgp_size_t total_attr_len; - char attrstr[BUFSIZ]; - char buf[BUFSIZ]; - - if (DISABLE_BGP_ANNOUNCE) - return; + /* Set OPEN message options */ + bgp_open_options(s, open_state) ; - if (afi == AFI_IP) - str2prefix ("0.0.0.0/0", &p); -#ifdef HAVE_IPV6 - else - str2prefix ("::/0", &p); -#endif /* HAVE_IPV6 */ + /* Set BGP message length. */ + length = bgp_packet_set_size(s) ; - /* Logging the attribute. */ - if (BGP_DEBUG (update, UPDATE_OUT)) + if (BGP_DEBUG (normal, NORMAL)) { - bgp_dump_attr (peer, attr, attrstr, BUFSIZ); - zlog (peer->log, LOG_DEBUG, "%s send UPDATE %s/%d %s", - peer->host, inet_ntop(p.family, &(p.u.prefix), buf, BUFSIZ), - p.prefixlen, attrstr); - } - - s = stream_new (BGP_MAX_PACKET_SIZE); - - /* Make BGP update packet. */ - bgp_packet_set_marker (s, BGP_MSG_UPDATE); + struct in_addr bgp_id ; + char buf[INET_ADDRSTRLEN] ; - /* Unfeasible Routes Length. */ - stream_putw (s, 0); + bgp_id.s_addr = htonl(open_state->bgp_id) ; + inet_ntop(AF_INET, &bgp_id.s_addr, buf, INET_ADDRSTRLEN) ; - /* Make place for total attribute length. */ - pos = stream_get_endp (s); - stream_putw (s, 0); - total_attr_len = bgp_packet_attribute (NULL, peer, s, attr, &p, afi, safi, from, NULL, NULL); + zlog_debug ("%s sending OPEN, version %d, my as %u, holdtime %d, id %s", + connection->host, BGP_VERSION_4, open_state->my_as, + open_state->holdtime, buf) ; - /* Set Total Path Attribute Length. */ - stream_putw_at (s, pos, total_attr_len); + } ; - /* NLRI set. */ - if (p.family == AF_INET && safi == SAFI_UNICAST) - stream_put_prefix (s, &p); - - /* Set size. */ - bgp_packet_set_size (s); - - packet = stream_dup (s); - stream_free (s); + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s send message type %d, length (incl. header) %d", + connection->host, BGP_MSG_OPEN, length); /* Dump packet if debug option is set. */ -#ifdef DEBUG - /* bgp_packet_dump (packet); */ -#endif /* DEBUG */ - - /* Add packet to the peer. */ - bgp_packet_add (peer, packet); + /* bgp_packet_dump (s); */ - BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); + /* Finally -- write the obuf away */ + return bgp_connection_write(connection, s) ; } -void -bgp_default_withdraw_send (struct peer *peer, afi_t afi, safi_t safi) +enum { - struct stream *s; - struct stream *packet; - struct prefix p; - unsigned long pos; - unsigned long cp; - bgp_size_t unfeasible_len; - bgp_size_t total_attr_len; - char buf[BUFSIZ]; - - if (DISABLE_BGP_ANNOUNCE) - return; - - if (afi == AFI_IP) - str2prefix ("0.0.0.0/0", &p); + have_ipv6 = #ifdef HAVE_IPV6 - else - str2prefix ("::/0", &p); -#endif /* HAVE_IPV6 */ - - total_attr_len = 0; - pos = 0; - - if (BGP_DEBUG (update, UPDATE_OUT)) - zlog (peer->log, LOG_DEBUG, "%s send UPDATE %s/%d -- unreachable", - peer->host, inet_ntop(p.family, &(p.u.prefix), buf, BUFSIZ), - p.prefixlen); - - s = stream_new (BGP_MAX_PACKET_SIZE); - - /* Make BGP update packet. */ - bgp_packet_set_marker (s, BGP_MSG_UPDATE); + 1 +#else + 0 +#endif +} ; + +/*------------------------------------------------------------------------------ + * Add options to given encoded OPEN message. + * + * Supports the status quo: only Capability Options. + * + * Creates an empty options part of there are no capabilities to set. + */ +static void +bgp_open_options(struct stream *s, bgp_open_state open_state) +{ + u_char len ; + unsigned long cp ; + qafx_num_t qafx ; - /* Unfeasible Routes Length. */; + /* Remember current pointer for Opt Parm Len. */ cp = stream_get_endp (s); - stream_putw (s, 0); - /* Withdrawn Routes. */ - if (p.family == AF_INET && safi == SAFI_UNICAST) - { - stream_put_prefix (s, &p); + /* Opt Parm Len. */ + stream_putc(s, 0); - unfeasible_len = stream_get_endp (s) - cp - 2; + /* If do not send capability, quit now -- zero options. */ + if (!open_state->can_capability) + return; - /* Set unfeasible len. */ - stream_putw_at (s, cp, unfeasible_len); + /* TODO: RFC 5492 (2009): SHOULD send only one Capability Option !! */ + /* RFC 3392 (2002): silent on the matter */ + /* RFC 2842 (2000): silent on the matter */ - /* Set total path attribute length. */ - stream_putw (s, 0); - } - else + /* Send capability for every AFI/SAFI supported. */ + for (qafx = qafx_num_first ; qafx <= qafx_num_last ; ++qafx) { - pos = stream_get_endp (s); - stream_putw (s, 0); - total_attr_len = bgp_packet_withdraw (peer, s, &p, afi, safi, NULL, NULL); - - /* Set total path attribute length. */ - stream_putw_at (s, pos, total_attr_len); - } - - bgp_packet_set_size (s); - - packet = stream_dup (s); - stream_free (s); - - /* Add packet to the peer. */ - bgp_packet_add (peer, packet); - - BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); -} - -/* Get next packet to be written. */ -static struct stream * -bgp_write_packet (struct peer *peer) -{ - afi_t afi; - safi_t safi; - struct stream *s = NULL; - struct bgp_advertise *adv; - - s = stream_fifo_head (peer->obuf); - if (s) - return s; - - for (afi = AFI_IP; afi < AFI_MAX; afi++) - for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) - { - adv = FIFO_HEAD (&peer->sync[afi][safi]->withdraw); - if (adv) - { - s = bgp_withdraw_packet (peer, afi, safi); - if (s) - return s; - } - } - - for (afi = AFI_IP; afi < AFI_MAX; afi++) - for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) - { - adv = FIFO_HEAD (&peer->sync[afi][safi]->update); - if (adv) - { - if (adv->binfo && adv->binfo->uptime < peer->synctime) - { - if (CHECK_FLAG (adv->binfo->peer->cap, PEER_CAP_RESTART_RCV) - && CHECK_FLAG (adv->binfo->peer->cap, PEER_CAP_RESTART_ADV) - && ! CHECK_FLAG (adv->binfo->flags, BGP_INFO_STALE) - && safi != SAFI_MPLS_VPN) - { - if (CHECK_FLAG (adv->binfo->peer->af_sflags[afi][safi], - PEER_STATUS_EOR_RECEIVED)) - s = bgp_update_packet (peer, afi, safi); - } - else - s = bgp_update_packet (peer, afi, safi); - } - - if (s) - return s; - } - - if (CHECK_FLAG (peer->cap, PEER_CAP_RESTART_RCV)) - { - if (peer->afc_nego[afi][safi] && peer->synctime - && ! CHECK_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_EOR_SEND) - && safi != SAFI_MPLS_VPN) - { - SET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_EOR_SEND); - return bgp_update_packet_eor (peer, afi, safi); - } - } - } - - return NULL; -} - -/* Is there partially written packet or updates we can send right - now. */ -static int -bgp_write_proceed (struct peer *peer) -{ - afi_t afi; - safi_t safi; - struct bgp_advertise *adv; - - if (stream_fifo_head (peer->obuf)) - return 1; + if (open_state->can_mp_ext & qafx_bit(qafx)) + { + iAFI_t afi = get_iAFI(qafx) ; + iSAFI_t safi = get_iSAFI(qafx) ; - for (afi = AFI_IP; afi < AFI_MAX; afi++) - for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) - if (FIFO_HEAD (&peer->sync[afi][safi]->withdraw)) - return 1; + if (!have_ipv6 && (afi == iAFI_IP6)) + continue ; - for (afi = AFI_IP; afi < AFI_MAX; afi++) - for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) - if ((adv = FIFO_HEAD (&peer->sync[afi][safi]->update)) != NULL) - if (adv->binfo->uptime < peer->synctime) - return 1; + stream_putc (s, BGP_OPEN_OPT_CAP); + stream_putc (s, CAPABILITY_CODE_MP_LEN + 2); + stream_putc (s, CAPABILITY_CODE_MP); + stream_putc (s, CAPABILITY_CODE_MP_LEN); + stream_putw (s, afi); + stream_putc (s, 0); + stream_putc (s, safi); + } ; + } ; - return 0; -} + /* Route refresh. */ -/* Write packet to the peer. */ -int -bgp_write (struct thread *thread) -{ - struct peer *peer; - u_char type; - struct stream *s; - int num; - unsigned int count = 0; - int write_errno; - - /* Yes first of all get peer pointer. */ - peer = THREAD_ARG (thread); - peer->t_write = NULL; - - /* For non-blocking IO check. */ - if (peer->status == Connect) + if (open_state->can_r_refresh & bgp_cap_form_old) { - bgp_connect_check (peer); - return 0; - } + stream_putc (s, BGP_OPEN_OPT_CAP) ; + stream_putc (s, CAPABILITY_CODE_REFRESH_LEN + 2) ; + stream_putc (s, CAPABILITY_CODE_REFRESH_OLD) ; + stream_putc (s, CAPABILITY_CODE_REFRESH_LEN) ; + } ; - /* Nonblocking write until TCP output buffer is full. */ - while (1) + if (open_state->can_r_refresh & bgp_cap_form_old) { - int writenum; - int val; + stream_putc (s, BGP_OPEN_OPT_CAP) ; + stream_putc (s, CAPABILITY_CODE_REFRESH_LEN + 2) ; + stream_putc (s, CAPABILITY_CODE_REFRESH) ; + stream_putc (s, CAPABILITY_CODE_REFRESH_LEN) ; + } ; - s = bgp_write_packet (peer); - if (! s) - return 0; + /* AS4 */ - /* XXX: FIXME, the socket should be NONBLOCK from the start - * status shouldnt need to be toggled on each write - */ - val = fcntl (peer->fd, F_GETFL, 0); - fcntl (peer->fd, F_SETFL, val|O_NONBLOCK); - - /* 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); - write_errno = errno; - fcntl (peer->fd, F_SETFL, val); - if (num <= 0) - { - /* Partial write. */ - if (write_errno == EWOULDBLOCK || write_errno == EAGAIN) - break; - - BGP_EVENT_ADD (peer, TCP_fatal_error); - return 0; - } - if (num != writenum) - { - stream_forward_getp (s, num); - - if (write_errno == EAGAIN) - break; - - continue; - } - - /* 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); - return 0; - 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; - } - - /* OK we send packet so delete it. */ - bgp_packet_delete (peer); - - if (++count >= BGP_WRITE_PACKET_MAX) - break; - } - - if (bgp_write_proceed (peer)) - BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); - - return 0; -} - -/* This is only for sending NOTIFICATION message to neighbor. */ -static int -bgp_write_notify (struct peer *peer) -{ - int ret; - 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); - - /* I'm not sure fd is writable. */ - ret = writen (peer->fd, STREAM_DATA (s), stream_get_endp (s)); - if (ret <= 0) + if (open_state->can_as4) { - BGP_EVENT_ADD (peer, TCP_fatal_error); - return 0; - } - - /* Retrieve BGP packet type. */ - stream_set_getp (s, BGP_MARKER_SIZE + 2); - type = stream_getc (s); - - assert (type == BGP_MSG_NOTIFY); - - /* Type should be notify. */ - peer->notify_out++; - - /* Double start timer. */ - peer->v_start *= 2; - - /* Overflow check. */ - if (peer->v_start >= (60 * 2)) - peer->v_start = (60 * 2); - - BGP_EVENT_ADD (peer, BGP_Stop); - - return 0; -} - -/* Make keepalive packet and send it to the peer. */ -void -bgp_keepalive_send (struct peer *peer) -{ - struct stream *s; - int length; - - s = stream_new (BGP_MAX_PACKET_SIZE); - - /* Make keepalive packet. */ - bgp_packet_set_marker (s, BGP_MSG_KEEPALIVE); - - /* Set packet size. */ - length = bgp_packet_set_size (s); - - /* Dump packet if debug option is set. */ - /* bgp_packet_dump (s); */ - - if (BGP_DEBUG (keepalive, KEEPALIVE)) - zlog_debug ("%s sending KEEPALIVE", peer->host); - if (BGP_DEBUG (normal, NORMAL)) - zlog_debug ("%s send message type %d, length (incl. header) %d", - peer->host, BGP_MSG_KEEPALIVE, length); - - /* Add packet to the peer. */ - bgp_packet_add (peer, s); - - 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) -{ - struct stream *s; - int length; - 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; - - /* local-as Change */ - if (peer->change_local_as) - local_as = peer->change_local_as; - else - local_as = peer->local_as; + stream_putc (s, BGP_OPEN_OPT_CAP); + stream_putc (s, CAPABILITY_CODE_AS4_LEN + 2); + stream_putc (s, CAPABILITY_CODE_AS4); + stream_putc (s, CAPABILITY_CODE_AS4_LEN); + stream_putl (s, open_state->my_as) ; + } ; - s = stream_new (BGP_MAX_PACKET_SIZE); + /* ORF Capabilities */ - /* 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 capability code. */ - bgp_open_capability (s, peer); - - /* Set BGP packet length. */ - length = bgp_packet_set_size (s); - - if (BGP_DEBUG (normal, NORMAL)) - 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 (normal, NORMAL)) - zlog_debug ("%s send message type %d, length (incl. header) %d", - peer->host, BGP_MSG_OPEN, length); - - /* Dump packet if debug option is set. */ - /* bgp_packet_dump (s); */ - - /* Add packet to the peer. */ - bgp_packet_add (peer, s); - - 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) -{ - 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; - - 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); - } - - if (BGP_DEBUG (normal, NORMAL)) - zlog_debug ("%s send message type %d, length (incl. header) %d", - peer->host, BGP_MSG_NOTIFY, length); - - /* peer reset cause */ - if (sub_code != BGP_NOTIFY_CEASE_CONFIG_CHANGE) + for (qafx = qafx_num_first ; qafx <= qafx_num_last ; ++qafx) { - 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; - } + u_char mode = 0 ; - /* Call imidiately. */ - BGP_WRITE_OFF (peer->t_write); + if (open_state->can_orf_prefix_send & qafx_bit(qafx)) + mode |= ORF_MODE_SEND ; + if (open_state->can_orf_prefix_recv & qafx_bit(qafx)) + mode |= ORF_MODE_RECEIVE ; - bgp_write_notify (peer); -} + confirm((ORF_MODE_SEND | ORF_MODE_RECEIVE) == ORF_MODE_BOTH) ; -/* Send BGP notify packet. */ -void -bgp_notify_send (struct peer *peer, u_char code, u_char sub_code) + if (mode != 0) + { + iAFI_t afi = get_iAFI(qafx) ; + iSAFI_t safi = get_iSAFI(qafx) ; + + if (!have_ipv6 && (afi == iAFI_IP6)) + continue ; + + if (open_state->can_orf_prefix & bgp_cap_form_old) + bgp_open_capability_orf(s, afi, safi, CAPABILITY_CODE_ORF_OLD, + ORF_TYPE_PREFIX_OLD, mode) ; + + if (open_state->can_orf_prefix & bgp_cap_form_new) + bgp_open_capability_orf(s, afi, safi, CAPABILITY_CODE_ORF, + ORF_TYPE_PREFIX, mode) ; + } ; + } ; + + /* Dynamic capability. */ + if (open_state->can_dynamic) + { + stream_putc (s, BGP_OPEN_OPT_CAP); + stream_putc (s, CAPABILITY_CODE_DYNAMIC_LEN + 2); + stream_putc (s, CAPABILITY_CODE_DYNAMIC); + stream_putc (s, CAPABILITY_CODE_DYNAMIC_LEN); + } ; + + /* Graceful restart capability */ + if (open_state->can_g_restart) + { + stream_putc (s, BGP_OPEN_OPT_CAP); + stream_putc (s, CAPABILITY_CODE_RESTART_LEN + 2); + stream_putc (s, CAPABILITY_CODE_RESTART); + stream_putc (s, CAPABILITY_CODE_RESTART_LEN); + stream_putw (s, open_state->restart_time); + } ; + + /* TODO: restarting flag ?? */ + /* TODO: graceful restart preserving forwarding state */ + + /* Total Opt Parm Len. */ + len = stream_get_endp (s) - cp - 1; + stream_putc_at (s, cp, len); +} ; + +/*------------------------------------------------------------------------------ + * Add an ORF capability to the given encoded OPEN message. + * + * Supports the status quo: only prefix-list filtering ! + */ +static void +bgp_open_capability_orf (struct stream *s, iAFI_t afi, iSAFI_t safi, + u_char cap_code, u_char orf_type, u_char mode) { - bgp_notify_send_with_data (peer, code, sub_code, NULL, 0); -} + u_char cap_len; + u_char orf_len; + unsigned long capp; + unsigned long orfp; + unsigned long numberp; -/* 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) -{ - struct stream *s; - struct stream *packet; - int length; - struct bgp_filter *filter; - int orf_refresh = 0; + int number_of_orfs = 0; - if (DISABLE_BGP_ANNOUNCE) - return; + stream_putc (s, BGP_OPEN_OPT_CAP); + capp = stream_get_endp (s); /* Set Capability Len Pointer */ + stream_putc (s, 0); /* Capability Length */ + stream_putc (s, cap_code); /* Capability Code */ + orfp = stream_get_endp (s); /* Set ORF Len Pointer */ + stream_putc (s, 0); /* ORF Length */ - filter = &peer->filter[afi][safi]; - - /* Adjust safi code. */ - if (safi == SAFI_MPLS_VPN) - safi = BGP_SAFI_VPNV4; - - 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, afi); stream_putc (s, 0); stream_putc (s, safi); - if (orf_type == ORF_TYPE_PREFIX - || orf_type == ORF_TYPE_PREFIX_OLD) - if (remove || filter->plist[FILTER_IN].ref) - { - 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 (normal, NORMAL)) - 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"), - afi, safi); - } - else - { - SET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_ORF_PREFIX_SEND); - prefix_bgp_orf_entry (s, filter->plist[FILTER_IN].ref, - ORF_COMMON_PART_ADD, ORF_COMMON_PART_PERMIT, - ORF_COMMON_PART_DENY); - if (BGP_DEBUG (normal, NORMAL)) - 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"), - afi, safi); - } - - /* Total ORF Entry Len. */ - orf_len = stream_get_endp (s) - orfp - 2; - stream_putw_at (s, orfp, orf_len); - } - - /* Set packet size. */ - length = bgp_packet_set_size (s); - - if (BGP_DEBUG (normal, NORMAL)) - { - if (! orf_refresh) - zlog_debug ("%s sending REFRESH_REQ for afi/safi: %d/%d", - peer->host, afi, safi); - zlog_debug ("%s send message type %d, length (incl. header) %d", - peer->host, CHECK_FLAG (peer->cap, PEER_CAP_REFRESH_NEW_RCV) ? - BGP_MSG_ROUTE_REFRESH_NEW : BGP_MSG_ROUTE_REFRESH_OLD, length); - } - - /* Make real packet. */ - packet = stream_dup (s); - stream_free (s); - - /* Add packet to the peer. */ - bgp_packet_add (peer, packet); - - 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) -{ - struct stream *s; - struct stream *packet; - int length; - - /* Adjust safi code. */ - if (safi == SAFI_MPLS_VPN) - safi = BGP_SAFI_VPNV4; + numberp = stream_get_endp (s); /* Set Number Pointer */ + stream_putc (s, 0); /* Number of ORFs */ - s = stream_new (BGP_MAX_PACKET_SIZE); + /* Address Prefix ORF */ + stream_putc (s, orf_type) ; /* type of ORF */ + stream_putc (s, mode) ; - /* Make BGP update packet. */ - bgp_packet_set_marker (s, BGP_MSG_CAPABILITY); + number_of_orfs++; - /* 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, afi); - stream_putc (s, 0); - stream_putc (s, safi); - - if (BGP_DEBUG (normal, NORMAL)) - zlog_debug ("%s sending CAPABILITY has %s MP_EXT CAP for afi/safi: %d/%d", - peer->host, action == CAPABILITY_ACTION_SET ? - "Advertising" : "Removing", afi, safi); - } + /* Total Number of ORFs. */ + stream_putc_at (s, numberp, number_of_orfs); - /* Set packet size. */ - length = bgp_packet_set_size (s); + /* Total ORF Len. */ + orf_len = stream_get_endp (s) - orfp - 1; + stream_putc_at (s, orfp, orf_len); - /* Make real packet. */ - packet = stream_dup (s); - stream_free (s); + /* Total Capability Len. */ + cap_len = stream_get_endp (s) - capp - 1; + stream_putc_at (s, capp, cap_len); +} ; - /* Add packet to the peer. */ - bgp_packet_add (peer, packet); - - if (BGP_DEBUG (normal, NORMAL)) - zlog_debug ("%s send message type %d, length (incl. header) %d", - peer->host, BGP_MSG_CAPABILITY, length); - - BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); -} +/*============================================================================== + * ROUTE-REFRESH -- transform bgp_prefix_orf_update into BGP message + */ -/* RFC1771 6.8 Connection collision detection. */ static int -bgp_collision_detect (struct peer *new, struct in_addr remote_id) -{ - struct peer *peer; - struct listnode *node, *nnode; - struct bgp *bgp; - - bgp = bgp_get_default (); - if (! bgp) - return 0; - - /* 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: */ - - for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) - { - /* Under OpenConfirm status, local peer structure already hold - remote router ID. */ - - if (peer != new - && (peer->status == OpenConfirm || peer->status == OpenSent) - && sockunion_same (&peer->su, &new->su)) - { - /* 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)) - { - /* 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. */ - - if (peer->fd >= 0) - bgp_notify_send (peer, 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 (new->fd >= 0) - bgp_notify_send (new, BGP_NOTIFY_CEASE, - BGP_NOTIFY_CEASE_COLLISION_RESOLUTION); - return -1; - } - } - } - return 0; -} - +bgp_msg_orf_part(struct stream* s, bgp_connection connection, + bgp_route_refresh rr) ; 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 peer *realpeer; - struct in_addr remote_id; - int capability; - u_int8_t notify_data_remote_as[2]; - u_int8_t notify_data_remote_id[4]; - - realpeer = NULL; - - /* 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 = 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 (normal, NORMAL)) - 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 */ - capability = 0; - optlen = stream_getc (peer->ibuf); +bgp_msg_orf_unknown(struct stream* s, bgp_orf_unknown_entry orf_unknown, + bgp_size_t left) ; +static int +bgp_msg_orf_remove_all(struct stream* s, bgp_size_t left) ; - if (optlen != 0) - { - /* 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); - } +static int +bgp_msg_orf_prefix(struct stream* s, uint8_t common, + bgp_orf_prefix_entry orf_prefix, bgp_size_t left) ; - /* 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 (peer, BGP_NOTIFY_OPEN_ERR, - BGP_NOTIFY_OPEN_BAD_PEER_AS); - return -1; - } +/*------------------------------------------------------------------------------ + * Make Route-Refresh message(s) and dispatch. + * + * May return before all required messages have been sent, if the write + * buffer is or becomes full. The "next" entry in the "bgp_prefix_orf_update" + * allows the process to be continued, later. + * + * If has to send more than one message, then all but the last will be set + * "defer". The last will be set as per the defer flag. + * + * Supports the status quo, only Address-Prefix ORF. + * + * Returns: > 0 => all written + * 0 => unable to write everything + * < 0 => failed -- error event generated + */ +extern int +bgp_msg_send_route_refresh(bgp_connection connection, bgp_route_refresh rr) +{ + struct stream *s = connection->obuf ; + uint8_t msg_type ; + flag_t done ; + bgp_size_t msg_len ; + int ret ; - 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 (peer, BGP_NOTIFY_OPEN_ERR, - BGP_NOTIFY_OPEN_BAD_PEER_AS); - 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 (peer, BGP_NOTIFY_OPEN_ERR, - BGP_NOTIFY_OPEN_BAD_PEER_AS); - return -1; - } - } + msg_type = connection->route_refresh_pre ? BGP_MT_ROUTE_REFRESH_pre + : BGP_MT_ROUTE_REFRESH ; + done = (bgp_orf_get_count(rr) == 0) ; - /* Lookup peer from Open packet. */ - if (CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER)) + do { - int as = 0; - - realpeer = peer_lookup_with_open (&peer->su, remote_as, &remote_id, &as); - - if (! realpeer) - { - /* Peer's source IP address is check in bgp_accept(), so this - must be AS number mismatch or remote-id configuration - mismatch. */ - if (as) - { - if (BGP_DEBUG (normal, NORMAL)) - 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); - } - else - { - if (BGP_DEBUG (normal, NORMAL)) - 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; - } - } + if (bgp_connection_write_full(connection)) + return 0 ; - /* When collision is detected and this peer is closed. Retrun - immidiately. */ - ret = bgp_collision_detect (peer, remote_id); - if (ret < 0) - return ret; + /* Construct BGP message header for new/old form ROUTE-REFRESH */ + bgp_packet_set_marker(s, msg_type) ; - /* Hack part. */ - if (CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER)) - { - if (realpeer->status == Established - && CHECK_FLAG (realpeer->sflags, PEER_STATUS_NSF_MODE)) - { - realpeer->last_reset = PEER_DOWN_NSF_CLOSE_SESSION; - SET_FLAG (realpeer->sflags, PEER_STATUS_NSF_WAIT); - } - else if (ret == 0 && realpeer->status != Active - && realpeer->status != OpenSent - && realpeer->status != OpenConfirm - && realpeer->status != Connect) - { - /* XXX: This is an awful problem.. - * - * According to the RFC we should just let this connection (of the - * accepted 'peer') continue on to Established if the other - * connection (the 'realpeer' one) is in state Connect, and deal - * with the more larval FSM as/when it gets far enough to receive - * an Open. We don't do that though, we instead close the (more - * developed) accepted connection. - * - * This means there's a race, which if hit, can loop: - * - * FSM for A FSM for B - * realpeer accept-peer realpeer accept-peer - * - * Connect Connect - * Active - * OpenSent OpenSent - * <arrive here, - * Notify, delete> - * Idle Active - * OpenSent OpenSent - * <arrive here, - * Notify, delete> - * Idle - * <wait> <wait> - * Connect Connect - * - * - * If both sides are Quagga, they're almost certain to wait for - * the same amount of time of course (which doesn't preclude other - * implementations also waiting for same time). The race is - * exacerbated by high-latency (in bgpd and/or the network). - * - * The reason we do this is because our FSM is tied to our peer - * structure, which carries our configuration information, etc. - * I.e. we can't let the accepted-peer FSM continue on as it is, - * cause it's not associated with any actual peer configuration - - * it's just a dummy. - * - * It's possible we could hack-fix this by just bgp_stop'ing the - * realpeer and continueing on with the 'transfer FSM' below. - * Ideally, we need to seperate FSMs from struct peer. - * - * Setting one side to passive avoids the race, as a workaround. - */ - if (BGP_DEBUG (events, EVENTS)) - zlog_debug ("%s peer status is %s close connection", - realpeer->host, LOOKUP (bgp_status_msg, - realpeer->status)); - bgp_notify_send (peer, BGP_NOTIFY_CEASE, - BGP_NOTIFY_CEASE_CONNECT_REJECT); - - return -1; - } - - if (BGP_DEBUG (events, EVENTS)) - zlog_debug ("%s [Event] Transfer accept BGP peer to real (state %s)", - peer->host, - LOOKUP (bgp_status_msg, realpeer->status)); - - bgp_stop (realpeer); - - /* Transfer file descriptor. */ - realpeer->fd = peer->fd; - peer->fd = -1; - - /* Transfer input buffer. */ - stream_free (realpeer->ibuf); - realpeer->ibuf = peer->ibuf; - realpeer->packet_size = peer->packet_size; - peer->ibuf = NULL; - - /* Transfer status. */ - realpeer->status = peer->status; - bgp_stop (peer); - - /* peer pointer change. Open packet send to neighbor. */ - peer = realpeer; - bgp_open_send (peer); - if (peer->fd < 0) - { - zlog_err ("bgp_open_receive peer's fd is negative value %d", - peer->fd); - return -1; - } - BGP_READ_ON (peer->t_read, bgp_read, peer->fd); - } + /* Encode Route Refresh message. */ + stream_putw(s, rr->afi) ; + stream_putc(s, 0); + stream_putc(s, rr->safi); - /* remote router-id check. */ - if (remote_id.s_addr == 0 - || ntohl (remote_id.s_addr) >= 0xe0000000 - || ntohl (peer->local_id.s_addr) == ntohl (remote_id.s_addr)) - { - if (BGP_DEBUG (normal, NORMAL)) - 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; - } + /* Process as many (remaining) ORF entries as can into message */ + if (!done) + done = bgp_msg_orf_part(s, connection, rr) ; - /* Set remote router-id */ - peer->remote_id = remote_id; + /* Set BGP message length & dispatch. */ + msg_len = bgp_packet_set_size(s) ; - /* Peer BGP version check. */ - if (version != BGP_VERSION_4) - { - u_int8_t maxver = BGP_VERSION_4; - if (BGP_DEBUG (normal, NORMAL)) - zlog_debug ("%s bad protocol version, remote requested %d, local request %d", - peer->host, version, BGP_VERSION_4); - bgp_notify_send_with_data (peer, - BGP_NOTIFY_OPEN_ERR, - BGP_NOTIFY_OPEN_UNSUP_VERSION, - &maxver, 1); - return -1; - } - - /* Check neighbor as number. */ - if (remote_as != peer->as) - { if (BGP_DEBUG (normal, NORMAL)) - 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 (peer, - BGP_NOTIFY_OPEN_ERR, - BGP_NOTIFY_OPEN_UNACEP_HOLDTIME); - 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; + zlog_debug ("%s sending REFRESH_REQ for afi/safi: %d/%d length %d", + connection->host, rr->afi, rr->safi, msg_len) ; - if (holdtime < send_holdtime) - peer->v_holdtime = holdtime; - else - peer->v_holdtime = send_holdtime; - - peer->v_keepalive = peer->v_holdtime / 3; - - /* Open option part parse. */ - if (optlen != 0) - { - ret = bgp_open_option_parse (peer, optlen, &capability); + ret = bgp_connection_write(connection, s) ; if (ret < 0) - return ret; - } - else - { - if (BGP_DEBUG (normal, NORMAL)) - zlog_debug ("%s rcvd OPEN w/ OPTION parameter len: 0", - peer->host); - } + return ret ; - /* Override capability. */ - if (! 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]; - } + } while (!done) ; - /* Get sockname. */ - bgp_getsockname (peer); + return done ; +} ; - BGP_EVENT_ADD (peer, Receive_OPEN_message); +/*------------------------------------------------------------------------------ + * Set the current ORF entry length (if any) + * + * Returns total length of BGP message. + */ +inline static bgp_size_t +bgp_msg_set_orf_length(struct stream* s, unsigned long elenp) +{ + bgp_size_t length = stream_get_endp(s) ; - peer->packet_size = 0; - if (peer->ibuf) - stream_reset (peer->ibuf); + if (elenp != 0) + stream_putw_at(s, elenp, length - elenp - 2) ; - return 0; -} + return length ; +} ; -/* Parse BGP Update packet and make attribute object. */ +/*------------------------------------------------------------------------------ + * Put ORF entries to the given stream until run out of entries or run out + * of room in the message. + * + * There MUST BE at least one ORF entry to go. + * + * Returns true <=> done all available entries. + */ static int -bgp_update_receive (struct peer *peer, bgp_size_t size) +bgp_msg_orf_part(struct stream* s, bgp_connection connection, + bgp_route_refresh rr) { - int ret; - u_char *end; - struct stream *s; - struct attr attr; - bgp_size_t attribute_len; - bgp_size_t update_len; - bgp_size_t withdraw_len; - struct bgp_nlri update; - struct bgp_nlri withdraw; - struct bgp_nlri mp_update; - struct bgp_nlri mp_withdraw; - char attrstr[BUFSIZ] = ""; - - /* Status must be Established. */ - if (peer->status != Established) - { - zlog_err ("%s [FSM] Update packet received under status %s", - peer->host, LOOKUP (bgp_status_msg, peer->status)); - bgp_notify_send (peer, BGP_NOTIFY_FSM_ERR, 0); - return -1; - } - - /* Set initial values. */ - memset (&attr, 0, sizeof (struct attr)); - memset (&update, 0, sizeof (struct bgp_nlri)); - memset (&withdraw, 0, sizeof (struct bgp_nlri)); - memset (&mp_update, 0, sizeof (struct bgp_nlri)); - memset (&mp_withdraw, 0, sizeof (struct bgp_nlri)); - - 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); + bgp_orf_entry entry ; - /* 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; - } + uint8_t orf_type ; + uint8_t orf_type_sent ; - /* Unfeasible Route packet format check. */ - if (withdraw_len > 0) - { - ret = bgp_nlri_sanity_check (peer, AFI_IP, stream_pnt (s), withdraw_len); - if (ret < 0) - return -1; + unsigned long whenp ; /* where the "when" byte is */ + unsigned long elenp ; /* where the entries length is */ - if (BGP_DEBUG (packet, PACKET_RECV)) - zlog_debug ("%s [Update:RECV] Unfeasible NLRI received", peer->host); + bgp_size_t left ; + bgp_size_t length ; + flag_t done ; + flag_t first ; - withdraw.afi = AFI_IP; - withdraw.safi = SAFI_UNICAST; - withdraw.nlri = stream_pnt (s); - 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; - } + /* Heading for Prefix-Address ORF type section */ + whenp = stream_get_endp(s) ; /* position of "when" */ + stream_putc(s, rr->defer ? BGP_ORF_WTR_DEFER : BGP_ORF_WTR_IMMEDIATE) ; - /* Fetch attribute total length. */ - attribute_len = stream_getw (s); + /* Process ORF entries until run out of entries or space */ - /* 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; - } + elenp = 0 ; /* no entries length, yet */ + orf_type = 0 ; /* no ORF type, yet */ - /* Parse attribute when it exists. */ - if (attribute_len) - { - ret = bgp_attr_parse (peer, &attr, attribute_len, - &mp_update, &mp_withdraw); - if (ret < 0) - return -1; - } + first = 1 ; /* next entry is first of its ORF type */ - /* Logging the attribute. */ - if (BGP_DEBUG (update, UPDATE_IN)) + while (1) { - ret= bgp_dump_attr (peer, &attr, attrstr, BUFSIZ); + entry = bgp_orf_get_entry(rr, rr->next) ; + done = (entry == NULL) ; - if (ret) - zlog (peer->log, LOG_DEBUG, "%s rcvd UPDATE w/ attr: %s", - peer->host, attrstr); - } + if (done) + break ; - /* Network Layer Reachability Information. */ - update_len = end - stream_pnt (s); + /* How much space is there left -- give up if very little + * + * What is "very little" is arbitrary, BUT MUST cover the ORF Type + * byte and the Length of ORF entries word, AT LEAST. + * */ + left = BGP_MSG_MAX_L - stream_get_endp(s) ; - if (update_len) - { - /* Check NLRI packet format and prefix length. */ - ret = bgp_nlri_sanity_check (peer, AFI_IP, stream_pnt (s), update_len); - if (ret < 0) - return -1; - - /* Set NLRI portion to structure. */ - update.afi = AFI_IP; - update.safi = SAFI_UNICAST; - update.nlri = stream_pnt (s); - update.length = update_len; - stream_forward_getp (s, update_len); - } + if (left < 16) + break ; /* NB: done == false */ - /* NLRI is processed only when the peer is configured specific - Address Family and Subsequent Address Family. */ - if (peer->afc[AFI_IP][SAFI_UNICAST]) - { - if (withdraw.length) - bgp_nlri_parse (peer, NULL, &withdraw); - - if (update.length) - { - /* We check well-known attribute only for IPv4 unicast - update. */ - ret = bgp_attr_check (peer, &attr); - if (ret < 0) - return -1; - - bgp_nlri_parse (peer, &attr, &update); - } - - if (mp_update.length - && mp_update.afi == AFI_IP - && mp_update.safi == SAFI_UNICAST) - bgp_nlri_parse (peer, &attr, &mp_update); - - if (mp_withdraw.length - && mp_withdraw.afi == AFI_IP - && mp_withdraw.safi == SAFI_UNICAST) - bgp_nlri_parse (peer, NULL, &mp_withdraw); - - if (! attribute_len && ! withdraw_len) - { - /* End-of-RIB received */ - SET_FLAG (peer->af_sflags[AFI_IP][SAFI_UNICAST], - PEER_STATUS_EOR_RECEIVED); - - /* NSF delete stale route */ - if (peer->nsf[AFI_IP][SAFI_UNICAST]) - bgp_clear_stale_route (peer, AFI_IP, SAFI_UNICAST); - - if (BGP_DEBUG (normal, NORMAL)) - zlog (peer->log, LOG_DEBUG, "rcvd End-of-RIB for IPv4 Unicast from %s", - peer->host); - } - } - if (peer->afc[AFI_IP][SAFI_MULTICAST]) - { - if (mp_update.length - && mp_update.afi == AFI_IP - && mp_update.safi == SAFI_MULTICAST) - bgp_nlri_parse (peer, &attr, &mp_update); - - if (mp_withdraw.length - && mp_withdraw.afi == AFI_IP - && mp_withdraw.safi == SAFI_MULTICAST) - bgp_nlri_parse (peer, NULL, &mp_withdraw); - - if (! withdraw_len - && mp_withdraw.afi == AFI_IP - && mp_withdraw.safi == SAFI_MULTICAST - && mp_withdraw.length == 0) - { - /* End-of-RIB received */ - SET_FLAG (peer->af_sflags[AFI_IP][SAFI_MULTICAST], - PEER_STATUS_EOR_RECEIVED); - - /* NSF delete stale route */ - if (peer->nsf[AFI_IP][SAFI_MULTICAST]) - bgp_clear_stale_route (peer, AFI_IP, SAFI_MULTICAST); - - if (BGP_DEBUG (normal, NORMAL)) - zlog (peer->log, LOG_DEBUG, "rcvd End-of-RIB for IPv4 Multicast from %s", - peer->host); - } - } - if (peer->afc[AFI_IP6][SAFI_UNICAST]) - { - if (mp_update.length - && mp_update.afi == AFI_IP6 - && mp_update.safi == SAFI_UNICAST) - bgp_nlri_parse (peer, &attr, &mp_update); - - if (mp_withdraw.length - && mp_withdraw.afi == AFI_IP6 - && mp_withdraw.safi == SAFI_UNICAST) - bgp_nlri_parse (peer, NULL, &mp_withdraw); - - if (! withdraw_len - && mp_withdraw.afi == AFI_IP6 - && mp_withdraw.safi == SAFI_UNICAST - && mp_withdraw.length == 0) - { - /* End-of-RIB received */ - SET_FLAG (peer->af_sflags[AFI_IP6][SAFI_UNICAST], PEER_STATUS_EOR_RECEIVED); - - /* NSF delete stale route */ - if (peer->nsf[AFI_IP6][SAFI_UNICAST]) - bgp_clear_stale_route (peer, AFI_IP6, SAFI_UNICAST); - - if (BGP_DEBUG (normal, NORMAL)) - zlog (peer->log, LOG_DEBUG, "rcvd End-of-RIB for IPv6 Unicast from %s", - peer->host); - } - } - if (peer->afc[AFI_IP6][SAFI_MULTICAST]) - { - if (mp_update.length - && mp_update.afi == AFI_IP6 - && mp_update.safi == SAFI_MULTICAST) - bgp_nlri_parse (peer, &attr, &mp_update); - - if (mp_withdraw.length - && mp_withdraw.afi == AFI_IP6 - && mp_withdraw.safi == SAFI_MULTICAST) - bgp_nlri_parse (peer, NULL, &mp_withdraw); - - if (! withdraw_len - && mp_withdraw.afi == AFI_IP6 - && mp_withdraw.safi == SAFI_MULTICAST - && mp_withdraw.length == 0) - { - /* End-of-RIB received */ - - /* NSF delete stale route */ - if (peer->nsf[AFI_IP6][SAFI_MULTICAST]) - bgp_clear_stale_route (peer, AFI_IP6, SAFI_MULTICAST); - - if (BGP_DEBUG (update, UPDATE_IN)) - zlog (peer->log, LOG_DEBUG, "rcvd End-of-RIB for IPv6 Multicast from %s", - peer->host); - } - } - if (peer->afc[AFI_IP][SAFI_MPLS_VPN]) - { - if (mp_update.length - && mp_update.afi == AFI_IP - && mp_update.safi == BGP_SAFI_VPNV4) - bgp_nlri_parse_vpnv4 (peer, &attr, &mp_update); - - if (mp_withdraw.length - && mp_withdraw.afi == AFI_IP - && mp_withdraw.safi == BGP_SAFI_VPNV4) - bgp_nlri_parse_vpnv4 (peer, NULL, &mp_withdraw); - - if (! withdraw_len - && mp_withdraw.afi == AFI_IP - && mp_withdraw.safi == BGP_SAFI_VPNV4 - && mp_withdraw.length == 0) - { - /* End-of-RIB received */ - - if (BGP_DEBUG (update, UPDATE_IN)) - zlog (peer->log, LOG_DEBUG, "rcvd End-of-RIB for VPNv4 Unicast from %s", - peer->host); - } - } - - /* Everything is done. We unintern temporary structures which - interned in bgp_attr_parse(). */ - if (attr.aspath) - aspath_unintern (attr.aspath); - if (attr.community) - community_unintern (attr.community); - if (attr.extra) - { - if (attr.extra->ecommunity) - ecommunity_unintern (attr.extra->ecommunity); - if (attr.extra->cluster) - cluster_unintern (attr.extra->cluster); - if (attr.extra->transit) - transit_unintern (attr.extra->transit); - bgp_attr_extra_free (&attr); - } + confirm(16 > BGP_ORF_MIN_L) ; /* Type & Length */ - /* If peering is stopped due to some reason, do not generate BGP - event. */ - if (peer->status != Established) - return 0; + /* Start new collection of ORF entries, if required. */ + if (first || (orf_type != entry->orf_type)) + { + /* fill in length of previous ORF entries, if any */ + bgp_msg_set_orf_length(s, elenp) ; - /* Increment packet counter. */ - peer->update_in++; - peer->update_time = time (NULL); + /* set type and dummy entries length. */ + orf_type = entry->orf_type ; + orf_type_sent = entry->orf_type ; - /* Generate BGP event. */ - BGP_EVENT_ADD (peer, Receive_UPDATE_message); + if ((orf_type == BGP_ORF_T_PREFIX) && connection->route_refresh_pre) + orf_type_sent = BGP_ORF_T_PREFIX_pre ; - return 0; -} - -/* Notify message treatment function. */ -static void -bgp_notify_receive (struct peer *peer, bgp_size_t size) -{ - struct bgp_notify bgp_notify; + stream_putc(s, orf_type_sent) ; /* ORF entries type */ - if (peer->notify.data) - { - XFREE (MTYPE_TMP, peer->notify.data); - peer->notify.data = NULL; - peer->notify.length = 0; - } + elenp = stream_get_endp(s) ; /* offset of the length */ + stream_putw(s, 0) ; /* length of ORF entries */ - bgp_notify.code = stream_getc (peer->ibuf); - bgp_notify.subcode = stream_getc (peer->ibuf); - bgp_notify.length = size - 2; - bgp_notify.data = NULL; + first = 1 ; /* next ORF entry is first of collection */ + } ; - /* 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); - } + /* Insert the entry, if will fit. + * + * sets done <=> fitted + */ + if (entry->unknown) + done = bgp_msg_orf_unknown(s, &entry->body.orf_unknown, left) ; + else + { + if (entry->remove_all) + done = bgp_msg_orf_remove_all(s, left) ; + else + { + uint8_t common = (entry->remove ? BGP_ORF_EA_REMOVE + : BGP_ORF_EA_ADD) + | (entry->deny ? BGP_ORF_EA_DENY + : BGP_ORF_EA_PERMIT) ; + switch (entry->orf_type) + { + case BGP_ORF_T_PREFIX: + done = bgp_msg_orf_prefix(s, common, + &entry->body.orf_prefix, left) ; + break ; + default: + zabort("unknown ORF type") ; + break ; + } ; + } ; + } ; + + /* exit loop now if not enough room for current ORF entry */ + if (!done) + break ; + + /* Done ORF entry. Step to the next. NB: done == true */ + ++rr->next ; + first = 0 ; /* no longer first */ + } ; + + /* If not done, need to: + * + * a) force defer, + * b) undo ORF entries if none output of current type + * + */ + if (!done) + { + stream_putc_at(s, whenp , BGP_ORF_WTR_DEFER) ; + + if (first) + { + stream_set_endp(s, elenp - 1) ; + elenp = 0 ; /* no entries length to set */ + } ; + } ; + + /* fill in length of last ORF entries (if any) */ + length = bgp_msg_set_orf_length(s, elenp) ; + + /* Something has gone wrong if nothing has been output after the "when" + * byte. Two possibilities: + * + * a) have been called again after having reported "done" (so there are + * no more entries to deal with. + * + * b) have been asked to output an "unknown" ORF entry which is too long + * for a BGP message !! + */ + if (length == (whenp + 1)) + { + if (entry == NULL) + zabort("called bgp_msg_send_route_refresh() after said was done") ; + + if (entry->unknown) + zlog_err("%s sending REFRESH_REQ with impossible length (%d) ORF", + connection->host, entry->body.orf_unknown.length) ; + else + zabort("failed to put even one ORF entry") ; - /* For debug */ - { - int i; - int first = 0; - char c[4]; + done = 1 ; /* force done */ + } ; - 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_print(peer, &bgp_notify, "received"); - if (bgp_notify.data) - XFREE (MTYPE_TMP, bgp_notify.data); - } - - /* peer count update */ - peer->notify_in++; - - if (peer->status == Established) - 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); - - /* Also apply to Unsupported Capability until remote router support - capability. */ - if (bgp_notify.code == BGP_NOTIFY_OPEN_ERR && - bgp_notify.subcode == BGP_NOTIFY_OPEN_UNSUP_CAPBL) - UNSET_FLAG (peer->sflags, PEER_STATUS_CAPABILITY_OPEN); - - BGP_EVENT_ADD (peer, Receive_NOTIFICATION_message); -} + return done ; +} ; -/* Keepalive treatment function -- get keepalive send keepalive */ -static void -bgp_keepalive_receive (struct peer *peer, bgp_size_t size) +/*------------------------------------------------------------------------------ + * Put given unknown ORF entry to stream -- verbatim -- if possible. + */ +static int +bgp_msg_orf_unknown(struct stream* s, bgp_orf_unknown_entry orf_unknown, + bgp_size_t left) { - if (BGP_DEBUG (keepalive, KEEPALIVE)) - zlog_debug ("%s KEEPALIVE rcvd", peer->host); + if (left < orf_unknown->length) + return 0 ; - BGP_EVENT_ADD (peer, Receive_KEEPALIVE_message); -} + stream_write(s, orf_unknown->data, orf_unknown->length) ; + return 1 ; +} ; -/* Route refresh message is received. */ -static void -bgp_route_refresh_receive (struct peer *peer, bgp_size_t size) +/*------------------------------------------------------------------------------ + * Put remove all ORF entry to stream -- if possible. + */ +static int +bgp_msg_orf_remove_all(struct stream* s, bgp_size_t left) { - afi_t afi; - safi_t safi; - u_char reserved; - struct stream *s; + if (left == 1) /* only one byte required ! */ + return 0 ; - /* If peer does not have the capability, send notification. */ - if (! CHECK_FLAG (peer->cap, PEER_CAP_REFRESH_ADV)) - { - plog_err (peer->log, "%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) - { - plog_err (peer->log, - "%s [Error] Route refresh packet received under status %s", - peer->host, LOOKUP (bgp_status_msg, peer->status)); - bgp_notify_send (peer, BGP_NOTIFY_FSM_ERR, 0); - return; - } - - s = peer->ibuf; - - /* Parse packet. */ - afi = stream_getw (s); - reserved = stream_getc (s); - safi = stream_getc (s); - - if (BGP_DEBUG (normal, NORMAL)) - zlog_debug ("%s rcvd REFRESH_REQ for afi/safi: %d/%d", - peer->host, afi, safi); - - /* Check AFI and SAFI. */ - if ((afi != AFI_IP && afi != AFI_IP6) - || (safi != SAFI_UNICAST && safi != SAFI_MULTICAST - && safi != BGP_SAFI_VPNV4)) - { - if (BGP_DEBUG (normal, NORMAL)) - { - zlog_debug ("%s REFRESH_REQ for unrecognized afi/safi: %d/%d - ignored", - peer->host, afi, safi); - } - return; - } - - /* Adjust safi code. */ - if (safi == BGP_SAFI_VPNV4) - safi = SAFI_MPLS_VPN; - - 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) - { - u_char *p_pnt = stream_pnt (s); - u_char *p_end = stream_pnt (s) + orf_len; - struct orf_prefix orfp; - u_char common = 0; - u_int32_t seq; - int psize; - char name[BUFSIZ]; - char buf[BUFSIZ]; - int ret; - - if (BGP_DEBUG (normal, NORMAL)) - { - 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) - { - memset (&orfp, 0, sizeof (struct orf_prefix)); - common = *p_pnt++; - if (common & ORF_COMMON_PART_REMOVE_ALL) - { - if (BGP_DEBUG (normal, NORMAL)) - zlog_debug ("%s rcvd Remove-All pfxlist ORF request", peer->host); - prefix_bgp_orf_remove_all (name); - break; - } - memcpy (&seq, p_pnt, sizeof (u_int32_t)); - p_pnt += sizeof (u_int32_t); - orfp.seq = ntohl (seq); - orfp.ge = *p_pnt++; - orfp.le = *p_pnt++; - orfp.p.prefixlen = *p_pnt++; - orfp.p.family = afi2family (afi); - psize = PSIZE (orfp.p.prefixlen); - memcpy (&orfp.p.u.prefix, p_pnt, psize); - p_pnt += psize; - - if (BGP_DEBUG (normal, NORMAL)) - zlog_debug ("%s rcvd %s %s seq %u %s/%d ge %d le %d", - 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, BUFSIZ), - orfp.p.prefixlen, orfp.ge, orfp.le); - - ret = prefix_bgp_orf_set (name, afi, &orfp, - (common & ORF_COMMON_PART_DENY ? 0 : 1 ), - (common & ORF_COMMON_PART_REMOVE ? 0 : 1)); - - if (ret != CMD_SUCCESS) - { - if (BGP_DEBUG (normal, NORMAL)) - zlog_debug ("%s Received misformatted prefixlist ORF. Remove All pfxlist", peer->host); - prefix_bgp_orf_remove_all (name); - break; - } - } - peer->orf_plist[afi][safi] = - prefix_list_lookup (AFI_ORF_PREFIX, name); - } - stream_forward_getp (s, orf_len); - } - if (BGP_DEBUG (normal, NORMAL)) - 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); - - /* Perform route refreshment to the peer */ - bgp_announce_route (peer, afi, safi); -} + stream_putc(s, BGP_ORF_EA_RM_ALL) ; + return 1 ; +} ; +/*------------------------------------------------------------------------------ + * Put given Address-Prefix ORF entry to stream -- if possible. + */ static int -bgp_capability_msg_parse (struct peer *peer, u_char *pnt, bgp_size_t length) +bgp_msg_orf_prefix(struct stream* s, uint8_t common, + bgp_orf_prefix_entry orf_prefix, bgp_size_t left) { - u_char *end; - struct capability_mp_data mpc; - struct capability_header *hdr; - u_char action; - struct bgp *bgp; - afi_t afi; - safi_t safi; - - bgp = peer->bgp; - 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 (normal, NORMAL)) - 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; - } + bgp_size_t plen = (orf_prefix->pfx.prefixlen + 7) / 8 ; - /* Fetch structure to the byte stream. */ - memcpy (&mpc, pnt + 3, sizeof (struct capability_mp_data)); + if (left < (BGP_ORF_E_P_MIN_L + plen)) + return 0 ; - /* We know MP Capability Code. */ - if (hdr->code == CAPABILITY_CODE_MP) - { - afi = ntohs (mpc.afi); - safi = mpc.safi; + stream_putc(s, common) ; + stream_putl(s, orf_prefix->seq) ; + stream_putc(s, orf_prefix->min) ; + stream_putc(s, orf_prefix->max) ; + stream_putc(s, orf_prefix->pfx.prefixlen) ; + stream_write(s, &orf_prefix->pfx.u.prefix, plen) ; - /* Ignore capability when override-capability is set. */ - if (CHECK_FLAG (peer->flags, PEER_FLAG_OVERRIDE_CAPABILITY)) - continue; + return 1 ; +} ; - if (!bgp_afi_safi_valid_indices (afi, &safi)) - { - if (BGP_DEBUG (normal, NORMAL)) - zlog_debug ("%s Dynamic Capability MP_EXT afi/safi invalid " - "(%u/%u)", peer->host, afi, safi); - continue; - } - - /* Address family check. */ - if (BGP_DEBUG (normal, NORMAL)) - zlog_debug ("%s CAPABILITY has %s MP_EXT CAP for afi/safi: %u/%u", - peer->host, - action == CAPABILITY_ACTION_SET - ? "Advertising" : "Removing", - ntohs(mpc.afi) , mpc.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, BGP_CLEAR_ROUTE_NORMAL); - 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; -} +/*============================================================================== + * End-of-RIB -- send an End-of-RIB BGP message (see Graceful Restart) + */ -/* Dynamic Capability is received. +/*------------------------------------------------------------------------------ + * Make End-of-RIB message and dispatch. * - * This is exported for unit-test purposes - */ extern int bgp_capability_receive(struct peer*, bgp_size_t) ; -int -bgp_capability_receive (struct peer *peer, bgp_size_t size) + * + * + * Returns: 2 => written to TCP -- it's gone + * 1 => written to wbuff -- waiting for socket + * 0 => nothing written -- wbuff was not empty ! + * -1 => failed -- error event generated + */ +extern int +bgp_msg_send_end_of_rib(bgp_connection connection, iAFI_t afi, iSAFI_t safi) { - u_char *pnt; + struct stream *s = connection->obuf ; - /* Fetch pointer. */ - pnt = stream_pnt (peer->ibuf); + if (!bgp_connection_write_empty(connection)) + return 0 ; - if (BGP_DEBUG (normal, NORMAL)) - zlog_debug ("%s rcv CAPABILITY", peer->host); + /* Make UPDATE message header */ + bgp_packet_set_marker(s, BGP_MSG_UPDATE) ; - /* If peer does not have the capability, send notification. */ - if (! CHECK_FLAG (peer->cap, PEER_CAP_DYNAMIC_ADV)) - { - plog_err (peer->log, "%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; - } + /* Minimum size UPDATE */ + stream_putw(s, 0) ; /* no Withdrawn Routes */ + stream_putw(s, 0) ; /* no Attributes => no NLRI */ - /* Status must be Established. */ - if (peer->status != Established) - { - plog_err (peer->log, - "%s [Error] Dynamic capability packet received under status %s", peer->host, LOOKUP (bgp_status_msg, peer->status)); - bgp_notify_send (peer, BGP_NOTIFY_FSM_ERR, 0); - return -1; - } + /* If not IPv4/Unicast, need empty MP Unreachable attribute */ + if ((afi != iAFI_IP) || (safi != iSAFI_Unicast)) + { + bgp_size_t attrp = stream_get_endp(s) ; - /* Parse packet. */ - return bgp_capability_msg_parse (peer, pnt, size); -} + stream_putc (s, BGP_ATTR_FLAG_OPTIONAL); + stream_putc (s, BGP_ATTR_MP_UNREACH_NLRI); + stream_putc (s, 3); + stream_putw (s, afi); + stream_putc (s, safi); -/* BGP read utility function. */ -static int -bgp_read_packet (struct peer *peer) -{ - int nbytes; - int readsize; - - readsize = peer->packet_size - stream_get_endp (peer->ibuf); - - /* If size is zero then return. */ - if (! readsize) - return 0; - - /* Read packet from fd. */ - nbytes = stream_read_unblock (peer->ibuf, peer->fd, readsize); - - /* If read byte is smaller than zero then error occured. */ - if (nbytes < 0) - { - if (errno == EAGAIN) - return -1; - - plog_err (peer->log, "%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; - } - - BGP_EVENT_ADD (peer, TCP_fatal_error); - return -1; + stream_putw_at(s, attrp-2, stream_get_endp(s) - attrp) ; } - /* When read byte is zero : clear bgp peer and return */ - if (nbytes == 0) - { - if (BGP_DEBUG (events, EVENTS)) - plog_debug (peer->log, "%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; - } - - BGP_EVENT_ADD (peer, TCP_connection_closed); - return -1; - } + bgp_packet_set_size(s); - /* We read partial packet. */ - if (stream_get_endp (peer->ibuf) != peer->packet_size) - return -1; + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("send End-of-RIB for %s to %s", afi_safi_print (afi, safi), + connection->host) ; - return 0; -} + /* Finally -- write the obuf away */ + return bgp_connection_write(connection, s) ; +} ; -/* Marker check. */ -static int -bgp_marker_all_one (struct stream *s, int length) +/*============================================================================== + * Utilities for creating BGP messages + */ + /* 0 1 2 3 4 5 6 7 */ +static const char bgp_header[] = "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" /* 8 */ + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" /* 16 */ + "\x00" ; +CONFIRM(sizeof(bgp_header) == (BGP_MARKER_SIZE + 2)) ; + +/*------------------------------------------------------------------------------ + * Insert BGP message standard header + * + * 16 bytes of 0xFF + * 2 bytes -- total length of message -- filled in later + * 1 byte -- the type of message as given + */ +extern int +bgp_packet_set_marker(struct stream *s, uint8_t type) { - int i; + /* Fill in marker & dummy total length (to be filled in later on) */ + stream_write(s, bgp_header, BGP_MARKER_SIZE + 2) ; - for (i = 0; i < length; i++) - if (s->data[i] != 0xff) - return 0; + /* BGP packet type. */ + stream_putc (s, type); - return 1; -} + /* Return current stream size. */ + return stream_get_endp (s); +} ; -/* Starting point of packet process function. */ -int -bgp_read (struct thread *thread) +/*------------------------------------------------------------------------------ + * Set BGP packet header size entry and return same. + */ +extern int +bgp_packet_set_size (struct stream *s) { - int ret; - u_char type = 0; - struct peer *peer; - bgp_size_t size; - char notify_data_length[2]; - - /* Yes first of all get peer pointer. */ - peer = THREAD_ARG (thread); - peer->t_read = NULL; - - /* For non-blocking IO check. */ - if (peer->status == Connect) - { - bgp_connect_check (peer); - 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); - } - - /* 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); - - if (BGP_DEBUG (normal, NORMAL) && type != 2 && type != 0) - zlog_debug ("%s rcv message type %d, length (excl. header) %d", - peer->host, type, size - BGP_HEADER_SIZE); - - /* 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 (normal, NORMAL)) - plog_debug (peer->log, - "%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 (normal, NORMAL)) - plog_debug (peer->log, - "%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; - } - - ret = bgp_read_packet (peer); - if (ret < 0) - goto done; - - /* Get size and type again. */ - size = 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 = time(NULL); /* Last read timer reset */ - bgp_update_receive (peer, size); - break; - case BGP_MSG_NOTIFY: - bgp_notify_receive (peer, size); - break; - case BGP_MSG_KEEPALIVE: - peer->readtime = time(NULL); /* Last read timer reset */ - 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; - } + int cp; - /* Clear input buffer. */ - peer->packet_size = 0; - if (peer->ibuf) - stream_reset (peer->ibuf); + /* Preserve current pointer. */ + cp = stream_get_endp (s); + stream_putw_at (s, BGP_MARKER_SIZE, cp); - done: - if (CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER)) - { - if (BGP_DEBUG (events, EVENTS)) - zlog_debug ("%s [Event] Accepting BGP peer delete", peer->host); - peer_delete (peer); - } - return 0; -} + return cp; +} ; |