diff options
Diffstat (limited to 'bgpd/bgp_msg_read.c')
-rw-r--r-- | bgpd/bgp_msg_read.c | 1768 |
1 files changed, 1768 insertions, 0 deletions
diff --git a/bgpd/bgp_msg_read.c b/bgpd/bgp_msg_read.c new file mode 100644 index 00000000..ed49f535 --- /dev/null +++ b/bgpd/bgp_msg_read.c @@ -0,0 +1,1768 @@ +/* BGP Message Read -- functions + * Copyright (C) 2009 Chris Hall (GMCH), Highwayman + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Zebra; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <zebra.h> +#include <time.h> + +#include "bgpd/bgp_common.h" +#include "bgpd/bgp_msg_read.h" +#include "bgpd/bgp_open.h" +#include "bgpd/bgpd.h" +#include "bgpd/bgp_debug.h" +#include "bgpd/bgp_aspath.h" +#include "bgpd/bgp_session.h" +#include "bgpd/bgp_open_state.h" +#include "bgpd/bgp_route_refresh.h" +#include "bgpd/bgp_fsm.h" +#include "bgpd/bgp_vty.h" + +/*------------------------------------------------------------------------------ + * Message handler functions. + */ +static void bgp_msg_unknown_receive(bgp_connection connection, + bgp_size_t body_size) ; +static void bgp_msg_open_receive(bgp_connection connection, + bgp_size_t body_size) ; +static void bgp_msg_update_receive(bgp_connection connection, + bgp_size_t body_size) ; +static void bgp_msg_notify_receive(bgp_connection connection, + bgp_size_t body_size) ; +static void bgp_msg_keepalive_receive(bgp_connection connection, + bgp_size_t body_size) ; +static void bgp_msg_route_refresh_receive(bgp_connection connection, + bgp_size_t body_size) ; +static void bgp_msg_capability_receive(bgp_connection connection, + bgp_size_t body_size) ; + +/*------------------------------------------------------------------------------ + * Get BGP message length, given a pointer to the start of a message. + * + * Make sure things are kosher. + */ +extern bgp_size_t +bgp_msg_get_mlen(uint8_t* p, uint8_t* limit) +{ + uint16_t mlen ; + passert((p + BGP_MH_HEAD_L) <= limit) ; + + mlen = ((bgp_size_t)(*(p + BGP_MH_MARKER_L)) << 8) + + (*(p + BGP_MH_MARKER_L + 1)) ; + + passert((p + mlen) <= limit) ; + + return mlen ; +} ; + +/*============================================================================== + * Header validation and sexing of messages + */ +enum +{ + qBGP_MT_unknown = 0, + qBGP_MT_OPEN, + qBGP_MT_UPDATE, + qBGP_MT_NOTIFICATION, + qBGP_MT_KEEPALIVE, + qBGP_MT_ROUTE_REFRESH, + qBGP_MT_CAPABILITY, + qBGP_MT_ROUTE_REFRESH_pre, + + qBGP_MT_count, +} ; + /* 0 1 2 3 */ +static const uint8_t bgp_header[] = { 0xFF, 0xFF, 0xFF, 0xFF, /* 4 */ + 0xFF, 0xFF, 0xFF, 0xFF, /* 8 */ + 0xFF, 0xFF, 0xFF, 0xFF, /* 12 */ + 0xFF, 0xFF, 0xFF, 0xFF /* 16 */ + } ; +CONFIRM(sizeof(bgp_header) == BGP_MH_MARKER_L) ; + +/* Array to map real BGP message type to qBGP message type */ +static const uint8_t bgp_type_map[256] = +{ + [BGP_MT_OPEN] = qBGP_MT_OPEN, + [BGP_MT_UPDATE] = qBGP_MT_UPDATE, + [BGP_MT_NOTIFICATION] = qBGP_MT_NOTIFICATION, + [BGP_MT_KEEPALIVE] = qBGP_MT_KEEPALIVE, + [BGP_MT_ROUTE_REFRESH] = qBGP_MT_ROUTE_REFRESH, + [BGP_MT_CAPABILITY] = qBGP_MT_CAPABILITY, + [BGP_MT_ROUTE_REFRESH_pre] = qBGP_MT_ROUTE_REFRESH_pre +} ; +CONFIRM(qBGP_MT_unknown == 0) ; + +/* Array of minimum message length -- by qBGP_MT_xxx */ +static const bgp_size_t bgp_type_min_size[] = +{ + [qBGP_MT_unknown] = BGP_MSG_MAX_L + 1, /* invalid ! */ + + [qBGP_MT_OPEN] = BGP_OPM_MIN_L, + [qBGP_MT_OPEN] = BGP_OPM_MIN_L, + [qBGP_MT_UPDATE] = BGP_UPM_MIN_L, + [qBGP_MT_NOTIFICATION] = BGP_NOM_MIN_L, + [qBGP_MT_KEEPALIVE] = BGP_KAM_L, + [qBGP_MT_ROUTE_REFRESH] = BGP_RRM_MIN_L, + [qBGP_MT_CAPABILITY] = BGP_MH_HEAD_L, /* pro tem */ + [qBGP_MT_ROUTE_REFRESH_pre] = BGP_RRM_MIN_L +} ; + +/* Array of message handler functions -- by qBGP_MT_xxx */ +static bgp_msg_handler* const bgp_type_handler[] = +{ + [qBGP_MT_unknown] = bgp_msg_unknown_receive, + + [qBGP_MT_OPEN] = bgp_msg_open_receive, + [qBGP_MT_UPDATE] = bgp_msg_update_receive, + [qBGP_MT_NOTIFICATION] = bgp_msg_notify_receive, + [qBGP_MT_KEEPALIVE] = bgp_msg_keepalive_receive, + [qBGP_MT_ROUTE_REFRESH] = bgp_msg_route_refresh_receive, + [qBGP_MT_CAPABILITY] = bgp_msg_capability_receive, + [qBGP_MT_ROUTE_REFRESH_pre] = bgp_msg_route_refresh_receive +} ; + +/* Array of message type names by qBGP_MT_xxxx */ +static const char* bgp_type_name[] = +{ + [qBGP_MT_unknown] = "*unknown*", + + [qBGP_MT_OPEN] = "OPEN", + [qBGP_MT_UPDATE] = "UPDATE", + [qBGP_MT_NOTIFICATION] = "NOTIFICATION", + [qBGP_MT_KEEPALIVE] = "KEEPALIVE", + [qBGP_MT_ROUTE_REFRESH] = "ROUTE-REFRESH", + [qBGP_MT_CAPABILITY] = "CAPABILITY", + [qBGP_MT_ROUTE_REFRESH_pre] = "ROUTE-REFRESH(pre)" +} ; + +/*------------------------------------------------------------------------------ + * The message size field is invalid per RFC + * + * Issue notification and kick FSM. + */ +static void +bgp_msg_header_bad_len(bgp_connection connection, uint8_t type, bgp_size_t size) +{ + uint16_t notify_size = htons(size) ; + + if (BGP_DEBUG (normal, NORMAL)) + plog_debug (connection->log, + "%s bad message length - %d for %s", + connection->host, size, + bgp_type_name[bgp_type_map[type]]) ; + + bgp_fsm_exception(connection, bgp_session_eInvalid_msg, + bgp_notify_new_with_data(BGP_NOMC_HEADER, BGP_NOMS_H_BAD_LEN, + (void*)¬ify_size, 2)) ; +} ; + +/*------------------------------------------------------------------------------ + * The message type is either unknown, or not enabled by capability exchange. + * + * Issue notification and kick FSM. + */ +static void +bgp_msg_header_bad_type(bgp_connection connection, uint8_t type) +{ + if (BGP_DEBUG (normal, NORMAL)) + { + if (bgp_type_map[type] == qBGP_MT_unknown) + plog_debug (connection->log, "%s unknown message type 0x%02x", + connection->host, type) ; + else + plog_err (connection->log, "%s [Error] BGP %s is not enabled", + connection->host, bgp_type_name[bgp_type_map[type]]) ; + } ; + + bgp_fsm_exception(connection, bgp_session_eInvalid_msg, + bgp_notify_new_with_data(BGP_NOMC_HEADER, BGP_NOMS_H_BAD_TYPE, + (void*)&type, 1)) ; +} ; + +/*------------------------------------------------------------------------------ + * Validate header part of BGP message. + * + * Have just read the header part (BGP_MH_HEAD_L) into connection->ibuf, need + * to check it's valid, and find how much more to read to complete the + * message. + * + * Advances the stream getp past the header. + * + * Plants: msg_type -- the message type + * msg_body_size -- the size of the message *body* + * msg_func -- the function to process the message + * + * in the connection, ready for bgp_msg_dispatch(). + * + * Returns: number of bytes still to read (ie msg_body_size) -- may be 0 + * + * NB: deals with invalid header, unknown message type and less than minimum + * message length for the message type. + * + * These all raise the required events, and return with minimum size + * message and the function set to bgp_msg_unknown_receive. + * + * The effect is that the reader will not read any more, and will dispatch + * to bgp_msg_unknown_receive. + */ +int +bgp_msg_check_header(bgp_connection connection) +{ + uint8_t type ; + bgp_size_t size ; + uint8_t qt ; + bgp_size_t min_size ; + + /* Get size and type. */ + stream_forward_getp (connection->ibuf, BGP_MH_MARKER_L); + size = stream_getw (connection->ibuf); + type = stream_getc (connection->ibuf); + + if (BGP_DEBUG (normal, NORMAL) && type != 2 && type != 0) + zlog_debug ("%s rcv message type %d, length (excl. header) %d", + connection->host, type, size - BGP_MH_HEAD_L); + + /* Marker check */ + /* TODO: why did old code only do this on OPEN and KEEPALIVE ? */ + if (memcmp(connection->ibuf->data, bgp_header, BGP_MH_MARKER_L) == 0) + { + /* BGP type check and minimum/maximum message length checks. */ + + qt = bgp_type_map[type] ; /* qBGP_MT_unknown if not valid */ + min_size = bgp_type_min_size[qt] ;/* > BGP_MSG_MAX_L if not valid */ + + if ((size < min_size) || (size > BGP_MSG_MAX_L)) + { + if (qt == qBGP_MT_unknown) + { + if (BGP_DEBUG (normal, NORMAL)) + plog_debug (connection->log, "%s unknown message type 0x%02x", + connection->host, type); + + bgp_fsm_exception(connection, bgp_session_eInvalid_msg, + bgp_notify_new_with_data(BGP_NOMC_HEADER, BGP_NOMS_H_BAD_TYPE, + (void*)&type, 1)) ; + } + else + bgp_msg_header_bad_len(connection, type, size) ; + + size = BGP_MH_HEAD_L ; /* can stop reading, now */ + } + } + else + { + bgp_fsm_exception(connection, bgp_session_eInvalid_msg, + bgp_notify_new(BGP_NOMC_HEADER, BGP_NOMS_H_NOT_SYNC)) ; + qt = qBGP_MT_unknown ; /* force unknown message */ + size = BGP_MH_HEAD_L ; /* can stop reading, now */ + } ; + + + connection->msg_type = type ; + connection->msg_body_size = size - BGP_MH_HEAD_L ; + connection->msg_func = bgp_type_handler[qt] ; + + return connection->msg_body_size ; +} ; + +/*============================================================================== + * Invalid message handler. + * + * Does nothing at all -- the error has already been deal with, this just + * allows unknown (and invalid) messages to be handled just like OK ones. + */ +static void bgp_msg_unknown_receive(bgp_connection connection, bgp_size_t body_size) +{ + return ; +} ; + +/*============================================================================== + * BGP OPEN message + * + * + * + */ + +static int +bgp_msg_open_option_parse (bgp_connection connection, bgp_notify notification, + sucker sr) ; +static int +bgp_msg_capability_option_parse (bgp_connection connection, + bgp_notify notification, sucker sr) ; +static int +bgp_msg_open_error(bgp_notify notification, bgp_nom_subcode_t subcode) ; + +static int +bgp_msg_open_invalid(bgp_notify notification) ; + +/*------------------------------------------------------------------------------ + * Receive BGP open packet and parse it into the connection's open_recv + * + * NB: requires the session to be locked (connection-wise) and not NULL. + */ +static void +bgp_msg_open_receive (bgp_connection connection, bgp_size_t body_size) +{ + int ret; + u_char version; + u_char optlen; + struct in_addr remote_id; + bgp_open_state open_recv; + struct stream* s ; + struct sucker ssr ; + unsigned holdtime ; + + ++connection->session->stats.open_in ; + + /* Start with an unspecific OPEN notification */ + bgp_notify notification = bgp_notify_new(BGP_NOMC_OPEN, + BGP_NOMS_UNSPECIFIC) ; + + /* To receive the parsed open message */ + open_recv = connection->open_recv + = bgp_open_state_init_new(connection->open_recv) ; + + /* Parse fixed part of the open packet */ + s = connection->ibuf ; + + version = stream_getc (s); + open_recv->my_as2 = stream_getw (s); + open_recv->holdtime = stream_getw (s); + open_recv->bgp_id = stream_get_ipv4 (s); + + open_recv->my_as = open_recv->my_as2 ; + + remote_id.s_addr = open_recv->bgp_id ; + + /* 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", + connection->host, version, + open_recv->my_as, open_recv->holdtime, + safe_inet_ntoa (remote_id)); + + /* Peer BGP version check. */ + if (version != BGP_VERSION_4) + { + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug("%s bad protocol version, remote requested %d, local max %d", + connection->host, version, BGP_VERSION_4); + + bgp_msg_open_error(notification, BGP_NOMS_O_VERSION) ; + bgp_notify_append_w(notification, BGP_VERSION_4) ; + + goto reject ; + } + + /* Remote bgp_id may not be multicast, or the same as here */ + if (IN_MULTICAST(ntohl(open_recv->bgp_id)) || + (open_recv->bgp_id == connection->session->open_send->bgp_id)) + { + zlog_debug ("%s rcv OPEN, multicast or our id %s", + connection->host, safe_inet_ntoa (remote_id)) ; + bgp_msg_noms_o_bad_id(notification, open_recv->bgp_id) ; + goto reject ; + } ; + + /* 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. + + See below where sets keepalive to hold / 3 !! + */ + if (open_recv->holdtime < 3 && open_recv->holdtime != 0) + { + bgp_msg_open_error(notification, BGP_NOMS_O_H_TIME) ; + goto reject ; + } ; + + /* Open option part parse */ + + optlen = stream_getc(s) ; + + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s rcv OPEN w/ OPTION parameter len: %u", + connection->host, optlen) ; + + if (optlen != stream_get_left(s)) + { + zlog_err ("%s bad OPEN, message length %u but option length %u", + connection->host, (unsigned)stream_get_endp(s), optlen) ; + bgp_msg_open_invalid(notification) ; + goto reject ; + } ; + + suck_init(&ssr, stream_pnt(s), optlen) ; + + ret = bgp_msg_open_option_parse (connection, notification, &ssr) ; + if (ret < 0) + goto reject ; + + /* Now worry about the AS number */ + + /* ASN == 0 is odd for AS2, error for AS4 */ + if (open_recv->my_as == 0) + { + if (open_recv->can_as4) + { + zlog_err ("%s [AS4] bad OPEN, got AS4 capability, but AS4 set to 0", + connection->host) ; + bgp_msg_open_error(notification, BGP_NOMS_O_BAD_AS) ; + goto reject ; + } + else + { + if (BGP_DEBUG (as4, AS4)) + zlog_debug ("%s [AS4] OPEN remote_as is 0 (not AS4 speaker)" + " odd, but proceeding.", connection->host) ; + } ; + } ; + + /* ASN = BGP_AS_TRANS is odd for AS2, error for AS4 */ + if (open_recv->my_as == BGP_ASN_TRANS) + { + if (open_recv->can_as4) + { + zlog_err ("%s [AS4] NEW speaker using AS_TRANS for AS4, not allowed", + connection->host); + bgp_msg_open_error(notification, BGP_NOMS_O_BAD_AS) ; + goto reject ; + } + else + { + if (BGP_DEBUG (as4, AS4)) + zlog_debug ("%s [AS4] OPEN remote_as is AS_TRANS (not AS4 speaker)" + " odd, but proceeding.", connection->host) ; + } ; + } ; + + /* Worry about my_as2 for AS4 speaker, if as2 != as4 */ + if ((open_recv->can_as4) && (open_recv->my_as != open_recv->my_as2)) + { + if (open_recv->my_as2 == BGP_ASN_TRANS) + { + if ((open_recv->my_as <= BGP_AS2_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.", + connection->host, open_recv->my_as) ; + } + else + { + zlog_err ("%s bad OPEN, got AS4 capability, but remote_as %u" + " mismatch with 16bit 'myasn' %u in open", + connection->host, open_recv->my_as, open_recv->my_as2) ; + + bgp_msg_open_error(notification, BGP_NOMS_O_BAD_AS) ; + goto reject ; + } ; + } ; + + /* Finally -- require the AS to be the configured AS */ + if (open_recv->my_as != connection->session->as_peer) + { + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s bad OPEN, remote AS is %u, expected %u", + connection->host, open_recv->my_as, + connection->session->as_peer) ; + + bgp_msg_open_error(notification, BGP_NOMS_O_BAD_AS) ; + if (open_recv->can_as4) + bgp_notify_append_l(notification, open_recv->my_as) ; + else + bgp_notify_append_w(notification, open_recv->my_as) ; + + goto reject ; + } + + /*............................................................................ + * It's OK ! Update the connection and issue event. + */ + bgp_notify_free(notification) ; /* No further use for this */ + + holdtime = connection->session->open_send->holdtime ; + + if (holdtime > open_recv->holdtime) + holdtime = open_recv->holdtime ; /* use smaller of theirs & ours */ + if (holdtime < 3) + holdtime = 0 ; /* no slip ups */ + + connection->hold_timer_interval = holdtime ; + connection->keepalive_timer_interval = holdtime / 3 ; + + connection->as4 = open_recv->can_as4 ; + connection->route_refresh = open_recv->can_r_refresh ; + connection->orf_prefix = open_recv->can_orf_prefix ; + + bgp_fsm_open_received(connection) ; + + return ; + + /*............................................................................ + * Failed. Reject the OPEN with the required notification. + */ +reject: + bgp_fsm_exception(connection, bgp_session_eOpen_reject, notification); +} ; + +/*------------------------------------------------------------------------------ + * Set notification to BGP_NOMC_OPEN/BGP_NOMS_O_BAD_ID and set the data part + * to be the given bad id. + * + * Create notification if required. + */ +extern bgp_notify +bgp_msg_noms_o_bad_id(bgp_notify notification, bgp_id_t id) +{ + notification = bgp_notify_reset(notification, BGP_NOMC_OPEN, + BGP_NOMS_O_BAD_ID) ; + bgp_notify_append_data(notification, &id, 4) ; + + return notification ; +} ; + +/*------------------------------------------------------------------------------ + * Reset notification to BGP_NOMC_OPEN with the given subcode, and return -1. + */ +static int +bgp_msg_open_error(bgp_notify notification, bgp_nom_subcode_t subcode) +{ + bgp_notify_reset(notification, BGP_NOMC_OPEN, subcode) ; + return -1 ; +} ; + +/*------------------------------------------------------------------------------ + * Reset notification to BGP_NOMC_OPEN/BGP_NOMS_UNSPECIFIC, and return -1. + */ +static int +bgp_msg_open_invalid(bgp_notify notification) +{ + return bgp_msg_open_error(notification, BGP_NOMS_UNSPECIFIC) ; +} ; + +/*------------------------------------------------------------------------------ + * Add unsupported capability to notification. + * + * The sr points at the start of the capability value. + */ +static void +bgp_msg_capability_unsupported(bgp_notify notification, sucker sr) +{ + ptr_t p_cap ; + int cap_len ; + + if (notification->subcode != BGP_NOMS_O_CAPABILITY) + bgp_notify_reset(notification, BGP_NOMC_OPEN, BGP_NOMS_O_CAPABILITY) ; + + cap_len = suck_total(sr) ; + p_cap = suck_start(sr) - BGP_CAP_MIN_L ; + + assert(*(p_cap + 1) == cap_len) ; + + bgp_notify_append_data(notification, p_cap, BGP_CAP_MIN_L + cap_len) ; +} ; + +/*------------------------------------------------------------------------------ + * Parse OPEN message options part. + * + * Expects the notification to be set up: BGP_NOMC_OPEN, BGP_NOMS_UNSPECIFIC + * with no data, yet. + * + * Returns: -1 => error -- see notification + * 0 => OK, no capabilities + * 1 => OK, at least one capability + */ +static int +bgp_msg_open_option_parse (bgp_connection connection, bgp_notify notification, + sucker sr) +{ + int ret, capability ; + int left ; + bgp_session session = connection->session ; + bgp_open_state open_send = session->open_send ; + bgp_open_state open_recv = connection->open_recv ; + + /* Prepare to read BGP OPEN message options */ + + ret = 0 ; /* OK so far */ + capability = 0 ; /* No capability option, yet */ + + while ((left = suck_left(sr)) > 0) + { + struct sucker ssr ; + u_char opt_type ; + u_char opt_length ; + + /* Fetch option type and length, if possible */ + if ((left -= 2) > 0) + { + opt_type = suck_b(sr); + opt_length = suck_b(sr); + left -= opt_length ; + } + else + { + opt_type = 0 ; /* ensure initialised */ + opt_length = 0 ; + } + + /* Must not have exceeded available bytes */ + if (left < 0) + { + zlog_info ("%s Option length error", connection->host); + return bgp_msg_open_invalid(notification) ; + } + + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s rcvd OPEN w/ optional parameter type %u (%s) len %u", + connection->host, opt_type, + opt_type == BGP_OPEN_OPT_AUTH ? "Authentication" : + opt_type == BGP_OPEN_OPT_CAP ? "Capability" : "Unknown", + opt_length); + + suck_push(sr, opt_length, &ssr) ; + + switch (opt_type) + { + case BGP_OPT_AUTH: + return bgp_msg_open_error(notification, BGP_NOMS_O_AUTH) ; + + case BGP_OPT_CAPS: + capability = 1 ; /* does => can */ + + ret = bgp_msg_capability_option_parse(connection, notification, sr) ; + if (ret < 0) + return bgp_msg_open_invalid(notification) ; + + break; + + default: + return bgp_msg_open_error(notification, BGP_NOMS_O_OPTION) ; + } ; + + suck_pop(sr, &ssr) ; + } ; + + /* All OPEN option is parsed. + * + * Do "strict" capability checks: + * + * 1) if there were any unknown capabilities, or any AFI/SAFI which are + * unknown or are not configured, then that is now an error. + * + * 2) the local AFI/SAFI must be the same as the remote AFI/SAFI. + * + * NB: cap_override and cap_strict are mutually exclusive + * + * TODO: what about graceful restart and no CAP-MP ?? + */ + if (session->cap_strict) + { + /* Treat any unsupported capability as an error. */ + if (bgp_notify_get_subcode(notification) == BGP_NOMS_O_CAPABILITY) + return -1 ; + + /* Check local AFI/SAFI set is same as the remote one. */ + if (open_recv->can_mp_ext != open_send->can_mp_ext) + return bgp_msg_open_error(notification, BGP_NOMS_O_CAPABILITY) ; + } ; + + /* If there were any capabilities, and not going to override AFI/SAFI, + * then check that there is at least one AFI/SAFI in common. + */ + if (capability && ! session->cap_override) + { + if ((open_recv->can_mp_ext & open_send->can_mp_ext) == 0) + { + plog_err (connection->log, "%s [Error] No common capability", + connection->host) ; + if (bgp_notify_get_subcode(notification) != BGP_NOMS_O_CAPABILITY) + bgp_msg_open_error(notification, BGP_NOMS_O_CAPABILITY) ; + + return -1 ; + } + } ; + + return connection->open_recv->can_capability = capability ; +} ; + +/*------------------------------------------------------------------------------ + * From IANA "Capability Codes (last updated 2009-08-04) Reference: [RFC5492]" + * + * Range Registration Procedures + * --------- -------------------------- + * 1- 63 IETF Review + * 64-127 First Come First Served + * 128-255 Reserved for Private Use (IANA does not assign) + * + * 1 Multiprotocol Extensions for BGP-4 [RFC2858] + * 2 Route Refresh Capability for BGP-4 [RFC2918] + * 3 Outbound Route Filtering Capability [RFC5291] + * 4 Multiple routes to a destination capability [RFC3107] + * 5 Extended Next Hop Encoding [RFC5549] + * 64 Graceful Restart Capability [RFC4724] + * 65 Support for 4-octet AS number capability [RFC4893] + * 66 Deprecated (2003-03-06) + * 67 Support for Dynamic Capability (capability specific) [Chen] + * 68 Multisession BGP Capability [Appanna] + * 69 ADD-PATH Capability [draft-ietf-idr-add-paths] + * + * 66 is, in fact, for draft-ietf-idr-dynamic-cap-02 of the Support for + * Dynamic Capability (capability specific) + * + * Supported: + * + * 1 BGP_CAN_MP_EXT -- Multiprotocol Extensions + * 2 BGP_CAN_R_REFRESH -- Route Refresh + * 3 BGP_CAN_ORF -- Outbound Route Filtering + * 64 BGP_CAN_G_RESTART -- Graceful Restart + * 65 BGP_CAN_AS4 -- AS4 + * 66 BGP_CAN_DYNAMIC_CAP_old -- Dynamic Capability (old form) + * 128 BGP_CAN_R_REFRESH_pre -- pre-RFC Route Refresh + * 130 BGP_CAN_ORF_pre -- pre-RFC Outbound Route Filtering + */ + +CONFIRM(BGP_CAP_MPE_L == sizeof (struct capability_mp_data)) ; +CONFIRM(BGP_CAP_RRF_L == CAPABILITY_CODE_REFRESH_LEN) ; +CONFIRM(BGP_CAP_ORFE_MIN_L == sizeof (struct capability_orf_entry)) ; +CONFIRM(BGP_CAP_GR_MIN_L == sizeof (struct capability_gr)) ; +CONFIRM(BGP_CAP_AS4_L == CAPABILITY_CODE_AS4_LEN) ; +CONFIRM(BGP_CAP_DYN_L == CAPABILITY_CODE_DYNAMIC_LEN) ; + +CONFIRM(BGP_CAN_MP_EXT == CAPABILITY_CODE_MP) ; +CONFIRM(BGP_CAN_R_REFRESH == CAPABILITY_CODE_REFRESH) ; +CONFIRM(BGP_CAN_ORF == CAPABILITY_CODE_ORF) ; +CONFIRM(BGP_CAN_G_RESTART == CAPABILITY_CODE_RESTART) ; +CONFIRM(BGP_CAN_AS4 == CAPABILITY_CODE_AS4) ; +CONFIRM(BGP_CAN_DYNAMIC_CAP_old == CAPABILITY_CODE_DYNAMIC) ; +CONFIRM(BGP_CAN_R_REFRESH_pre == CAPABILITY_CODE_REFRESH_OLD) ; +CONFIRM(BGP_CAN_ORF_pre == CAPABILITY_CODE_ORF_OLD) ; + +/* TODO: clarify value for BGP_CAN_DYNAMIC_CAP !! */ + +/* Minimum sizes for length field of each cap (so not inc. the header) */ +static const unsigned cap_minsizes[] = +{ + [BGP_CAN_MP_EXT] = BGP_CAP_MPE_L, + [BGP_CAN_R_REFRESH] = BGP_CAP_RRF_L, + [BGP_CAN_ORF] = BGP_CAP_ORFE_MIN_L, + [BGP_CAN_G_RESTART] = BGP_CAP_GR_MIN_L, + [BGP_CAN_AS4] = BGP_CAP_AS4_L, + [BGP_CAN_DYNAMIC_CAP_old] = BGP_CAP_DYN_L, + [BGP_CAN_DYNAMIC_CAP] = BGP_CAP_DYN_L, + [BGP_CAN_R_REFRESH_pre] = BGP_CAP_RRF_L, + [BGP_CAN_ORF_pre] = BGP_CAP_ORFE_MIN_L, +} ; + +static const unsigned cap_maxsizes[] = +{ + [BGP_CAN_MP_EXT] = BGP_CAP_MPE_L, + [BGP_CAN_R_REFRESH] = BGP_CAP_RRF_L, + [BGP_CAN_ORF] = BGP_CAP_MAX_L, /* variable */ + [BGP_CAN_G_RESTART] = BGP_CAP_MAX_L, /* variable */ + [BGP_CAN_AS4] = BGP_CAP_AS4_L, + [BGP_CAN_DYNAMIC_CAP_old] = BGP_CAP_DYN_L, + [BGP_CAN_DYNAMIC_CAP] = BGP_CAP_DYN_L, + [BGP_CAN_R_REFRESH_pre] = BGP_CAP_RRF_L, + [BGP_CAN_ORF_pre] = BGP_CAP_MAX_L, /* variable */ +} ; + +/* Forward references for parsing individual capabilities, return -1 if the + * capability is malformed or contains invalid values. + */ +static int +bgp_msg_capability_mp(bgp_connection connection, sucker sr) ; + +static int +bgp_msg_capability_orf (bgp_connection connection, uint8_t cap_code, sucker sr); + +static int +bgp_msg_capability_restart (bgp_connection connection, sucker sr) ; + +static int +bgp_msg_capability_as4 (bgp_connection connection, sucker sr) ; + +/*------------------------------------------------------------------------------ + * Set notification to malformed/invalid. + * + * Returns -1 ! + */ +static int +bgp_msg_capability_bad(bgp_notify notification) +{ + bgp_notify_reset(notification, BGP_NOMC_OPEN, BGP_NOMS_UNSPECIFIC) ; + return -1 ; +} ; + +/*------------------------------------------------------------------------------ + * Parse given capability option -- may contain multiple capabilities. + * + * Adjusts the open_recv open state according to the capabilities seen. + * + * Collects unsupported capabilities in the notification, where a + * BGP_NOMS_O_CAPABILITY message will be created. So, at the end of the + * process, can tell if there are any unsupported capabilities. + * + * The unsupported capabilities will be: + * + * * MP Extensions AFI/SAFI which are unknown or are not configured at + * this end. + * + * * any unknown capabilities < 128 + * + * If an invalid or malformed capability is found, the notification is set + * BGP_NOMS_UNSPECIFIC -- see bgp_msg_capability_bad() above. + * + * Returns: 0 => OK (but may have collected some unsupported capabilities) + * -1 => invalid or malformed + */ +static int +bgp_msg_capability_option_parse (bgp_connection connection, + bgp_notify notification, sucker sr) +{ + int ret, left ; + bgp_open_state open_recv = connection->open_recv ; + + while ((left = suck_left(sr)) > 0) + { + struct sucker ssr ; + int cap_code ; + unsigned cap_length ; + + /* We need at least capability code and capability length. */ + if ((left -= 2) >= 0) + { + cap_code = suck_b(sr); + cap_length = suck_b(sr); + left -= cap_length ; + } + + if (left < 0) + { + zlog_info ("%s Capability length error (< header)", connection->host); + return bgp_msg_capability_bad(notification) ; + } ; + + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s OPEN has %s capability (%u), length %u", + connection->host, + LOOKUP (capcode_str, cap_code), + cap_code, cap_length) ; + + /* Length sanity check, type-specific, for known capabilities */ + switch (cap_code) + { + case BGP_CAN_MP_EXT: + case BGP_CAN_R_REFRESH: + case BGP_CAN_ORF: + case BGP_CAN_G_RESTART: + case BGP_CAN_AS4: + case BGP_CAN_DYNAMIC_CAP: + case BGP_CAN_R_REFRESH_pre: + case BGP_CAN_ORF_pre: + /* Check length. */ + if ( (cap_length < cap_minsizes[cap_code]) || + (cap_length > cap_maxsizes[cap_code]) ) + { + const char* tag = "" ; + if (cap_minsizes[cap_code] != cap_maxsizes[cap_code]) + tag = "at least " ; + zlog_info ("%s %s Capability length error: got %u," + " expected %s%u", + connection->host, + LOOKUP (capcode_str, cap_code), + cap_length, tag, + (unsigned) cap_minsizes[cap_code]) ; + return bgp_msg_capability_bad(notification) ; + /* invalid: stop dead */ + } ; + break ; + /* we deliberately ignore unknown codes, see below */ + default: + break ; + } ; + + /* By this point the capability length is exactly right for the + * fixed length capabilities, at least the minimum length for the rest. + * Also, then capability fits within the capability option. + */ + suck_push(sr, cap_length, &ssr) ; + + ret = 0 ; + switch (cap_code) + { + case BGP_CAN_MP_EXT: + /* Ignore capability when override-capability is set. + * + * NB: bgp_msg_capability_mp() returns > 0 if the AFI/SAFI is not + * recognised or is not one of the configured ones. + */ + if (! connection->session->cap_override) + ret = bgp_msg_capability_mp(connection, sr); + break; + + case BGP_CAN_R_REFRESH: + open_recv->can_r_refresh |= bgp_form_rfc ; + break ; + + case BGP_CAN_R_REFRESH_pre: + open_recv->can_r_refresh |= bgp_form_pre; + break; + + case BGP_CAN_ORF: + case BGP_CAN_ORF_pre: + ret = bgp_msg_capability_orf(connection, cap_code, sr) ; + break; + + case BGP_CAN_G_RESTART: + ret = bgp_msg_capability_restart(connection, sr) ; + break; + + case BGP_CAN_DYNAMIC_CAP_old: + open_recv->can_dynamic = 1; + break; + + case BGP_CAN_AS4: + ret = bgp_msg_capability_as4(connection, sr) ; + break; + + default: + if (cap_code >= 128) + { + /* We don't send Notification for unknown vendor specific + capabilities. It seems reasonable for now... */ + zlog_warn ("%s Vendor specific capability %d", + connection->host, cap_code); + } + else + { + zlog_warn ("%s unrecognized capability code: %d - ignored", + connection->host, cap_code) ; + ret = 1 ; /* collect unsupported capability */ + } + + /* Add given unknown capability and its value */ + bgp_open_state_unknown_add(open_recv, cap_code, + suck_start(sr), suck_total(sr)) ; + } + + if (ret < 0) + return bgp_msg_capability_bad(notification) ; + + if (ret > 0) + bgp_msg_capability_unsupported(notification, sr) ; + + suck_pop(sr, &ssr) ; + } + + return 0 ; +} ; + +static qafx_bit_t +bgp_msg_afi_safi(sucker sr, struct iAFI_SAFI* mp) +{ + mp->afi = suck_w(sr) ; + suck_x(sr) ; + mp->safi = suck_b(sr) ; + + return qafx_bit_from_iAFI_iSAFI(mp->afi, mp->safi) ; +} ; + +/*------------------------------------------------------------------------------ + * Process value of Multiprotocol Extensions -- BGP_CAN_MP_EXT -- RFC2858 + * + * Capability is: AFI -- word + * reserved -- byte + * SAFI -- byte + * + * This is a fixed length capability, so that's been dealt with. + * + * Treats: invalid AFI/SAFI combinations ) + * unknown AFI/SAFI combinations ) unsupported capability + * unsupported AFI/SAFI combinations ) + * + * Sets any known AFI/SAFI combination in open_recv->can_mp_ext. + * + * Returns: 0 => OK -- AFI/SAFI is in the open_sent set + * 1 => -- AFI/SAFI is known, but not in the open_sent set + * 2 => -- AFI/SAFI is not known, may be invalid + */ +static int +bgp_msg_capability_mp(bgp_connection connection, sucker sr) +{ + struct iAFI_SAFI mp ; + qafx_bit_t qb ; + bgp_cap_afi_safi cap ; + + qb = bgp_msg_afi_safi(sr, &mp) ; + + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s OPEN has MP_EXT CAP for afi/safi: %u/%u", + connection->host, mp.afi, mp.safi) ; + + cap = bgp_open_state_afi_safi_add(connection->open_recv, mp.afi, mp.safi, + (qb != 0), BGP_CAN_MP_EXT) ; + if (qb == 0) + { + zlog_warn ("Unknown afi/safi combination (%u/%u)", mp.afi, mp.safi) ; + return 2 ; + } ; + + /* Now can register the capability */ + connection->open_recv->can_mp_ext |= qb; + + /* Return: 0 => is in the open_send set + * 1 => is not + */ + return ((connection->session->open_send->can_mp_ext & qb) != 0) ? 0 : 1 ; +} ; + +/*------------------------------------------------------------------------------ + * Process value of Outbound Route Filtering -- BGP_CAN_ORF -- RFC5291 + * + * Must have at least one ORF Entry, but may have several and each is of + * variable length. + * + * Requirement for at least one entry has already been checked. + * + * Returns: 0 => OK + * -1 => malformed ! + * + * NB: treats multiple ORF capabilities and multiple entries as additive. + * Does not track what AFI/SAFI and ORF types have been declared. + */ + +static int bgp_msg_capability_orf_entry(bgp_connection connection, + uint8_t cap_code, sucker sr) ; + +static int +bgp_msg_capability_orf(bgp_connection connection, uint8_t cap_code, sucker sr) +{ + while (suck_left(sr) > 0) + { + if (suck_left(sr) < BGP_CAP_ORFE_MIN_L) + { + zlog_info ("%s ORF Capability length error," + " Cap length left %u", + connection->host, suck_left(sr)) ; + return -1; + } ; + + if (bgp_msg_capability_orf_entry (connection, cap_code, sr) == -1) + return -1; + } ; + + return 0; +} ; + +/* Process ORF Entry: + * + * Entry is: AFI -- word + * reserved -- byte + * SAFI -- byte + * number -- byte + * type -- byte ) repeated "number" times + * send/recv -- byte ) + * + * Returns: 0 => OK + * -1 => malformed + */ +static int +bgp_msg_capability_orf_entry(bgp_connection connection, uint8_t cap_code, + sucker sr) +{ + iAFI_SAFI_t mp ; + int number ; + int length ; + sucker_t ssr ; + bgp_cap_afi_safi cap ; + + bgp_open_state open_recv = connection->open_recv ; + qafx_bit_t qb ; + + /* ORF Entry header */ + qb = bgp_msg_afi_safi(sr, &mp) ; + number = suck_b(sr) ; + + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s ORF Cap entry for afi/safi: %u/%u %d types", + connection->host, mp.afi, mp.safi, number) ; + + /* Check AFI and SAFI. */ + if (qb == 0) + zlog_info ("%s Addr-family %d/%d not known." + " Ignoring the ORF capability", + connection->host, mp.afi, mp.safi) ; + + /* Validate number field */ + length = number * BGP_CAP_ORFT_L ; + + if (suck_left(sr) < length) + { + zlog_info ("%s ORF Capability entry length error," + " Cap length left %u, num %u", + connection->host, suck_left(sr), number) ; + return -1; + } ; + + /* Process the supported ORF types */ + + suck_push(sr, length, &ssr) ; + + while (number--) + { + qafx_bit_t qbs ; + + uint8_t type = suck_b(sr) ; + uint8_t mode = suck_b(sr) ; + + /* ORF Mode error check */ + switch (mode) + { + case BGP_CAP_ORFT_M_RECV: + case BGP_CAP_ORFT_M_SEND: + case BGP_CAP_ORFT_M_BOTH: + break; + default: + zlog_info ("%s Invalid send/receive 'mode' value %d" + " in ORF capability", + connection->host, mode) ; + return -1 ; + } ; + + /* ORF Type and afi/safi sexing */ + qbs = 0 ; + switch (cap_code) + { + case BGP_CAN_ORF: + switch (type) + { + case BGP_CAP_ORFT_T_PFIX: + open_recv->can_orf_prefix |= bgp_form_rfc ; + qbs = qafx_ipv4_unicast_bit + | qafx_ipv4_multicast_bit + | qafx_ipv6_unicast_bit ; + break ; + default: + break ; + } + break; + case BGP_CAN_ORF_pre: + switch (type) + { + case BGP_CAP_ORFT_T_PFIX_pre: + open_recv->can_orf_prefix |= bgp_form_pre ; + qbs = qafx_ipv4_unicast_bit + | qafx_ipv4_multicast_bit + | qafx_ipv6_unicast_bit ; + break ; + default: + break ; + } + break ; + default: + break ; + } ; + + cap = bgp_open_state_afi_safi_add(connection->open_recv, mp.afi, mp.safi, + (qb != 0), cap_code) ; + cap->caps.orf.known_orf_type = (qbs != 0) ; + cap->caps.orf.type = type ; + cap->caps.orf.send = ((mode & BGP_CAP_ORFT_M_SEND) != 0) ; + cap->caps.orf.recv = ((mode & BGP_CAP_ORFT_M_RECV) != 0) ; + + if ((qbs & qb) == 0) + { + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s Addr-family %d/%d has" + " ORF type/mode %d/%d not supported", + connection->host, mp.afi, mp.safi, type, mode) ; + continue ; + } ; + + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s OPEN has %s ORF capability" + " as %s for afi/safi: %d/%d", + connection->host, LOOKUP (orf_type_str, type), + LOOKUP (orf_mode_str, mode), + mp.afi, mp.safi) ; + + if (mode & BGP_CAP_ORFT_M_SEND) + open_recv->can_orf_prefix_send |= qb ; + if (mode & BGP_CAP_ORFT_M_RECV) + open_recv->can_orf_prefix_recv |= qb ; + + confirm((BGP_CAP_ORFT_M_RECV & BGP_CAP_ORFT_M_SEND) == 0) ; + + confirm((BGP_CAP_ORFT_M_SEND & BGP_CAP_ORFT_M_SEND) != 0) ; + confirm((BGP_CAP_ORFT_M_BOTH & BGP_CAP_ORFT_M_SEND) != 0) ; + + confirm((BGP_CAP_ORFT_M_RECV & BGP_CAP_ORFT_M_RECV) != 0) ; + confirm((BGP_CAP_ORFT_M_BOTH & BGP_CAP_ORFT_M_RECV) != 0) ; + } ; + + /* Should now be exactly at the end. */ + suck_pop_exact(sr, &ssr) ; + + return 0; +} ; + +/*------------------------------------------------------------------------------ + * Process value of Graceful Restart capability -- BGP_CAN_G_RESTART -- RFC2918 + * + * Capability is: time -- word + * AFI -- word ) + * SAFI -- byte ) repeated 0 or more times + * flag -- byte ) + * + * This is a variable length capability, minimum size already checked. + * + * The sr covers from the start to the end of the capability value. + * + * Returns: 0 => OK + * -1 => malformed ! + * + * TODO: RFC 4724 suggests "implicit" IPv4/Unicast if no CAP-MP + * TODO: RFC 4760 says MUST CAP-MP if propose to use CAP-MP ! + * + */ +static int +bgp_msg_capability_restart (bgp_connection connection, sucker sr) +{ + bgp_open_state open_recv = connection->open_recv; + u_int16_t restart_flag_time ; + int length ; + bgp_cap_afi_safi cap ; + + length = suck_left(sr) ; /* total length of value, for reporting */ + + /* RFC4724: "If more than one instance of the Graceful Restart Capability + * is carried in the capability advertisement, the receiver of + * the advertisement MUST ignore all but the last instance..." + */ + + open_recv->can_g_restart = 1; + + /* Deal with the restart time and restarted flag */ + restart_flag_time = suck_w(sr) ; + + open_recv->has_restarted = (restart_flag_time & BGP_CAP_GR_T_R_FLAG) != 0 ; + open_recv->restart_time = restart_flag_time & BGP_CAP_GR_T_MASK ; + + open_recv->can_preserve = 0 ; + open_recv->has_preserved = 0 ; + + if (BGP_DEBUG (normal, NORMAL)) + { + zlog_debug ("%s OPEN has Graceful Restart capability", connection->host); + zlog_debug ("%s Peer has%srestarted. Restart Time : %d", + connection->host, open_recv->has_restarted ? " " : " not ", + open_recv->restart_time); + } ; + + if ((suck_left(sr) % BGP_CAP_GRE_L) != 0) + { + zlog_info ("%s Graceful Restart Capability length error," + " Cap length %u", + connection->host, length); + return -1; + } ; + + while (suck_left(sr) > 0) + { + iAFI_SAFI_t mp ; + uint8_t flags ; + qafx_bit_t qb ; + int has_preserved ; + + mp.afi = suck_w(sr) ; + mp.safi = suck_b(sr) ; + flags = suck_b(sr) ; + + qb = qafx_bit_from_iAFI_iSAFI(mp.afi, mp.safi) ; + has_preserved = ((flags & BGP_CAP_GRE_F_FORW) != 0) ; + + cap = bgp_open_state_afi_safi_add(connection->open_recv, mp.afi, mp.safi, + (qb != 0), BGP_CAN_G_RESTART) ; + cap->caps.gr.has_preserved = has_preserved ; + + if (qb == 0) + { + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s Addr-family %d/%d(afi/safi) not supported." + " Ignore the Graceful Restart capability", + connection->host, mp.afi, mp.safi); + } + else + { + /* Now can register the capability */ + + if (connection->session->open_send->can_mp_ext & qb) + { + open_recv->can_preserve |= qb ; + open_recv->has_preserved |= (has_preserved ? qb : 0) ; + + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s Address family %s is%spreserved", + connection->host, + afi_safi_print (mp.afi, mp.safi), + (has_preserved ? " " : " not ")) ; + } + else + { + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s Addr-family %d/%d(afi/safi) not enabled." + " Ignore the Graceful Restart capability", + connection->host, mp.afi, mp.safi); + } ; + } ; + } ; + + return 0 ; +} ; + +/*------------------------------------------------------------------------------ + * Process value of AS4 capability -- BGP_CAN_AS4 -- RFC4893 + * + * Capability is: ASN -- long word (4 bytes) + * + * This is a fixed length capability, so that's been dealt with. + * + * Validation of ASN and cross-check against my_as2, done elsewhere. + * + * Returns: 0 => OK + */ +static int +bgp_msg_capability_as4 (bgp_connection connection, sucker sr) +{ + bgp_open_state open_recv = connection->open_recv ; + + open_recv->can_as4 = 1 ; + open_recv->my_as = suck_l(sr) ; + + if (BGP_DEBUG (as4, AS4)) + zlog_debug ("%s [AS4] about to set cap PEER_CAP_AS4_RCV, got as4 %u", + connection->host, open_recv->my_as) ; + + return 0 ; +} ; + +/*============================================================================== + * BGP UPDATE message + * + * NB: requires the session to be locked (connection-wise) and not NULL. + */ +static void +bgp_msg_update_receive (bgp_connection connection, bgp_size_t body_size) +{ + /* Must be prepared to receive "update" like messages */ + if (bgp_fsm_pre_update(connection) != 0) + { + plog_err(connection->log, + "%s [Error] Update message received while in %s State", + connection->host, LOOKUP(bgp_status_msg, connection->state)) ; + return ; + } ; + + ++connection->session->stats.update_in ; + connection->session->stats.update_time = bgp_clock() ; + + /* PRO TEM: pass raw update message across to Routing Engine */ + /* TODO: decode update messages in the BGP Engine. */ + bgp_session_update_recv(connection->session, connection->ibuf, body_size); +} + +/*============================================================================== + * BGP KEEPALIVE message + * + * NB: requires the session to be locked (connection-wise) and not NULL. + */ +static void +bgp_msg_keepalive_receive (bgp_connection connection, bgp_size_t body_size) +{ + ++connection->session->stats.keepalive_in ; + + if (BGP_DEBUG (keepalive, KEEPALIVE)) + zlog_debug ("%s KEEPALIVE rcvd", connection->host); + + if (body_size == 0) + bgp_fsm_keepalive_received(connection) ; + else + bgp_msg_header_bad_len(connection, BGP_MT_KEEPALIVE, body_size) ; +} ; + +/*============================================================================== + * BGP NOTIFICATION message + * + * NB: requires the session to be locked (connection-wise) and not NULL. + */ +static void +bgp_msg_notify_receive (bgp_connection connection, bgp_size_t body_size) +{ + bgp_notify notification ; + + bgp_nom_code_t code = stream_getc (connection->ibuf); + bgp_nom_subcode_t subcode = stream_getc (connection->ibuf); + + ++connection->session->stats.notify_in ; + + notification = bgp_notify_new_with_data(code, subcode, + stream_pnt(connection->ibuf), body_size - 2) ; + bgp_notify_set_received(notification) ; + + bgp_notify_print(connection->session->peer, notification) ; /* Logging */ + + bgp_fsm_notification_exception(connection, notification) ; +} ; + +/*============================================================================== + * BGP ROUTE-REFRESH message + * + * NB: requires the session to be locked (connection-wise) and not NULL. + */ +static int +bgp_msg_orf_recv(bgp_connection connection, bgp_route_refresh rr, + qafx_bit_t qb, sucker sr) ; +static int +bgp_msg_orf_prefix_recv(orf_prefix orfpe, qafx_bit_t qb, sucker sr) ; + +/*------------------------------------------------------------------------------ + * Process BGP ROUTE-REFRESH message + * + * This may contain ORF stuff ! + */ +static void +bgp_msg_route_refresh_receive(bgp_connection connection, bgp_size_t body_size) +{ + struct iAFI_SAFI mp ; + sucker_t ssr ; + qafx_bit_t qb ; + bgp_route_refresh rr ; + unsigned form ; + int ret ; + + ++connection->session->stats.refresh_in ; + + /* If peer does not have the capability, treat as bad message type */ + + switch (connection->msg_type) + { + case BGP_MT_ROUTE_REFRESH: + form = bgp_form_rfc ; + break ; + case BGP_MT_ROUTE_REFRESH_pre: + form = bgp_form_pre ; + break ; + default: /* should not happen, really */ + form = 0 ; + break ; + } ; + + if ((connection->route_refresh & form) == 0) + { + bgp_msg_header_bad_type(connection, connection->msg_type) ; + return ; + } ; + + /* Must be prepared to receive "update" like messages */ + if (connection->state != bgp_fsm_sEstablished) + { + plog_err(connection->log, + "%s [Error] Route-Refresh message received while in %s State", + connection->host, LOOKUP(bgp_status_msg, connection->state)) ; + return ; + } ; + + /* Set about parsing the message */ + + dassert(stream_get_left(connection->ibuf) == body_size) ; + suck_init(&ssr, stream_pnt(connection->ibuf), body_size) ; + + /* Start with AFI, reserved, SAFI */ + qb = bgp_msg_afi_safi(&ssr, &mp) ; + + qb &= qafx_ipv4_unicast_bit | qafx_ipv4_multicast_bit | + qafx_ipv6_unicast_bit | qafx_ipv6_multicast_bit | + qafx_ipv4_mpls_vpn_bit ; + + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s rcvd REFRESH_REQ for afi/safi: %d/%d%s", + connection->host, mp.afi, mp.safi, + (qb == 0) ? " -- unknown combination" : "") ; + + rr = bgp_route_refresh_new(mp.afi, mp.safi, 0) ; + + /* If there are any ORF entries, time to suck them up now. */ + ret = 0 ; + + if (suck_left(&ssr) != 0) + { + uint8_t when_to_refresh ; + bool defer = 0 ; + + when_to_refresh = suck_b(&ssr) ; + + switch (when_to_refresh) + { + case BGP_ORF_WTR_IMMEDIATE: + defer = 0 ; + break ; + case BGP_ORF_WTR_DEFER: + defer = 1 ; + break ; + default: + zlog_info ("%s ORF route refresh invalid 'when' value %d" + " (AFI/SAFI %d/%d)", + connection->host, when_to_refresh, rr->afi, rr->safi) ; + ret = -1 ; + break ; + } ; + + if (ret >= 0) + { + bgp_route_refresh_set_orf_defer(rr, defer) ; + + /* After the when to refresh, expect 1 or more ORFs */ + do + { + ret = bgp_msg_orf_recv(connection, rr, qb, &ssr) ; + } while ((ret == 0) && (suck_left(&ssr) != 0)) ; + } ; + } ; + + if (ret < 0) + { + bgp_fsm_exception(connection, bgp_session_eInvalid_msg, + bgp_notify_new(BGP_NOMC_CEASE, BGP_NOMS_UNSPECIFIC)) ; + return ; + } + + bgp_session_route_refresh_recv(connection->session, rr) ; + return ; +} ; + +/*------------------------------------------------------------------------------ + * Process ORF Type and following ORF Entries. + * + * Expects there to be at least one ORF entry -- that is, the length of the + * ORF Entries may not be 0. + * + * Returns: 0 => OK + * -1 => invalid or malformed + */ +static int +bgp_msg_orf_recv(bgp_connection connection, bgp_route_refresh rr, + qafx_bit_t qb, sucker sr) +{ + sucker_t ssr ; + int left ; + uint8_t orf_type ; + bgp_size_t orf_len ; + int unknown ; + int form ; + int ret ; + + /* Suck up the ORF type and the length of the entries that follow */ + left = suck_left(sr) - BGP_ORF_MIN_L ; + if (left >= 0) + { + orf_type = suck_b(sr) ; + orf_len = suck_w(sr) ; + } + else + { + orf_type = 0 ; /* ensure initialised */ + orf_len = 0 ; + } ; + + /* The length may not be zero and may not exceed what there is left */ + if ((orf_len == 0) || (left < orf_len)) + { + zlog_info ("%s ORF route refresh length error: %d when %d left" + " (AFI/SAFI %d/%d, type %d length %d)", + connection->host, orf_len, left, + rr->afi, rr->safi, orf_type, orf_len) ; + return -1 ; + } ; + + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s rcvd ORF type %d length %d", + connection->host, orf_type, orf_len); + + /* Sex the ORF type */ + form = bgp_form_none ; + unknown = 1 ; + switch (orf_type) + { + case BGP_ORF_T_PREFIX: + if ((connection->orf_prefix & bgp_form_rfc) != 0) + { + form = bgp_form_rfc ; + unknown = (qb != 0) ; + } ; + break ; + + case BGP_ORF_T_PREFIX_pre: + if ((connection->orf_prefix & bgp_form_pre) != 0) + { + orf_type = BGP_ORF_T_PREFIX ; + form = bgp_form_pre ; + unknown = (qb != 0) ; + } ; + break ; + + default: + break ; + } ; + + /* Suck up the ORF entries. NB: orf_len != 0 */ + suck_push(sr, orf_len, &ssr) ; + + if (unknown) + bgp_orf_add_unknown(rr, orf_type, orf_len, suck_step(sr, orf_len)) ; + else + { + do + { + bool remove_all = 0 ; + bool remove = 0 ; + bool deny = 0 ; + uint8_t common ; + + common = suck_b(sr) ; + switch (common & BGP_ORF_EA_MASK) + { + case BGP_ORF_EA_ADD: + break ; + case BGP_ORF_EA_REMOVE: + remove = 1 ; + break ; + case BGP_ORF_EA_RM_ALL: + remove_all = 1 ; + break ; + default: + zlog_info ("%s ORF route refresh invalid common byte: %u" + " (AFI/SAFI %d/%d, type %d length %d)", + connection->host, common, + rr->afi, rr->safi, orf_type, orf_len) ; + return -1 ; + } ; + + deny = ((common & BGP_ORF_EA_DENY) != 0) ; + + ret = 0 ; + if (remove_all) + bgp_orf_add_remove_all(rr, orf_type, form) ; + else + { + bgp_orf_entry orfe ; + orfe = bgp_orf_add(rr, orf_type, form, remove, deny) ; + + switch (orf_type) + { + case BGP_ORF_T_PREFIX: + ret = bgp_msg_orf_prefix_recv(&orfe->body.orf_prefix, qb, + sr) ; + break ; + + default: + zabort("Lost track of ORF type") ; + break ; + } ; + + if (ret < 0) + { + zlog_info ("%s ORF route refresh invalid Prefix ORF entry" + " (AFI/SAFI %d/%d, type %d length %d)", + connection->host, + rr->afi, rr->safi, orf_type, orf_len) ; + return -1 ; + } ; + } ; + + } while (suck_left(sr) > 0) ; + } ; + + suck_pop_exact(sr, &ssr) ; + return 0 ; +} ; + +/*------------------------------------------------------------------------------ + * Process ORF Prefix entry, from after the common byte. + * + * This is for entries which are *not* remove-all + * + * Returns: 0 => OK + * -1 => invalid or malformed + */ +static int +bgp_msg_orf_prefix_recv(orf_prefix orfpe, qafx_bit_t qb, sucker sr) +{ + sa_family_t paf ; + int left ; + + assert(qb != 0) ; + paf = get_sa_family(qafx_num(qb)) ; + + /* Must have the minimum Prefix ORF entry, less the common byte, left */ + left = suck_left(sr) - (BGP_ORF_E_P_MIN_L - BGP_ORF_E_COM_L) ; + if (left >= 0) + { + uint8_t plen ; + uint8_t blen ; + + orfpe->seq = suck_l(sr) ; + orfpe->ge = suck_b(sr) ; /* aka min */ + orfpe->le = suck_b(sr) ; /* aka max */ + plen = suck_b(sr) ; + + memset(&orfpe->p, 0, sizeof(struct prefix)) ; + + blen = (plen + 7) / 8 ; + if ((left -= blen) >= 0) + { + orfpe->p.family = paf ; + orfpe->p.prefixlen = plen ; + if (blen != 0) + { + if (blen <= prefix_blen(&orfpe->p)) + memcpy(&orfpe->p.u.prefix, suck_step(sr, blen), blen) ; + else + left = -1 ; + } ; + } ; + } ; + + return left ; +} ; + +/*============================================================================== + * BGP CAPABILITY message -- Dynamic Capabilities + * + * NB: requires the session to be locked (connection-wise) and not NULL. + */ +static void bgp_msg_capability_receive(bgp_connection connection, + bgp_size_t body_size) +{ + ++connection->session->stats.dynamic_cap_in ; + + return ; +} ; |