diff options
Diffstat (limited to 'bgpd/bgp_msg_read.c')
-rw-r--r-- | bgpd/bgp_msg_read.c | 1707 |
1 files changed, 972 insertions, 735 deletions
diff --git a/bgpd/bgp_msg_read.c b/bgpd/bgp_msg_read.c index bbe8796d..58a80485 100644 --- a/bgpd/bgp_msg_read.c +++ b/bgpd/bgp_msg_read.c @@ -35,26 +35,7 @@ /* prototypes */ -static int bgp_msg_marker_all_one (struct stream *s, int length); static int bgp_msg_open_receive (bgp_connection connection, bgp_size_t size); -static as_t bgp_msg_peek_for_as4_capability (bgp_connection connection, - u_char length); -static as_t bgp_msg_capability_as4 (bgp_connection connection, - struct capability_header *hdr); -static int bgp_msg_open_option_parse (bgp_connection connection, u_char length, - int *capability); -static int bgp_msg_capability_parse (bgp_connection connection, size_t length, - u_char **error); -static int bgp_msg_capability_mp (bgp_connection connection, - struct capability_header *hdr); -static int bgp_msg_capability_restart (bgp_connection connection, - struct capability_header *caphdr); -static int bgp_msg_capability_orf_entry (bgp_connection connection, - struct capability_header *hdr); -static int bgp_msg_capability_orf (bgp_connection connection, - struct capability_header *hdr); -static void bgp_msg_capability_orf_not_support (char *host, afi_t afi, - safi_t safi, u_char type, u_char mode); static int bgp_msg_update_receive (bgp_connection connection, bgp_size_t size); static int bgp_msg_keepalive_receive (bgp_connection connection, bgp_size_t size); @@ -70,138 +51,169 @@ static void bgp_msg_notify_send_with_data (bgp_connection connection, extern bgp_size_t bgp_msg_get_mlen(uint8_t* p) { - return (*(p + BGP_MARKER_SIZE)) + (*(p + BGP_MARKER_SIZE + 1) << 8) ; + return (*(p + BGP_MH_MARKER_L)) + (*(p + BGP_MH_MARKER_L + 1) << 8) ; } ; -/* read and validate header. - * return >= 0 number of bytes still to read + +/*------------------------------------------------------------------------------ + * Header validation support. + */ + /* 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 return minimum message length. + * + * Length includes the header, so cannot possibly be zero ! + */ +static const bgp_size_t bgp_type_min_size[256] = +{ + [BGP_MT_OPEN] = BGP_OPM_MIN_L, + [BGP_MT_UPDATE] = BGP_UPM_MIN_L, + [BGP_MT_NOTIFICATION] = BGP_NOM_MIN_L, + [BGP_MT_KEEPALIVE] = BGP_KAM_L, + [BGP_MT_ROUTE_REFRESH] = BGP_RRM_MIN_L, + [BGP_MT_CAPABILITY] = BGP_MH_HEAD_L, /* pro tem */ + [BGP_MT_ROUTE_REFRESH_pre] = BGP_RRM_MIN_L +} ; + +/*------------------------------------------------------------------------------ + * The message size field is invalid per RFC + * + * Issue notification and kick FSM. + */ +static int +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, + type == 128 ? "ROUTE-REFRESH" : bgp_type_str[(int) type]) ; + + bgp_msg_notify_send_with_data (connection, bgp_session_eInvalid_msg, + BGP_NOMC_HEADER, BGP_NOMS_H_BAD_LEN, + (void*)¬ify_size, 2) ; + return -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_size -- the size of the message body + * + * in the connection, ready for bgp_msg_dispatch(). + * + * return >= 0 number of bytes still to read (ie msg_size) * return -1 error */ int bgp_msg_check_header(bgp_connection connection) { - u_char type = 0; - bgp_size_t size; - char notify_data_length[2]; - bgp_session session = connection->session; + uint8_t type ; + bgp_size_t size ; + bgp_size_t min_size ; - assert(session); - - /* Get size and type. */ - stream_forward_getp (connection->ibuf, BGP_MARKER_SIZE); - memcpy (notify_data_length, stream_pnt (connection->ibuf), 2); + /* 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", - session->host, type, size - BGP_HEADER_SIZE); + connection->host, type, size - BGP_MH_HEAD_L); - /* Marker check */ - if (((type == BGP_MSG_OPEN) || (type == BGP_MSG_KEEPALIVE)) - && ! bgp_msg_marker_all_one (connection->ibuf, BGP_MARKER_SIZE)) + /* 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_msg_notify_send (connection, bgp_session_eInvalid_msg, BGP_NOTIFY_HEADER_ERR, BGP_NOTIFY_HEADER_NOT_SYNC); return -1; - } + } ; + + /* BGP type check. */ + min_size = bgp_type_min_size[type] ; - /* 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 (min_size == 0) { if (BGP_DEBUG (normal, NORMAL)) - plog_debug (session->log, + plog_debug (connection->log, "%s unknown message type 0x%02x", - session->host, type); - bgp_msg_notify_send_with_data (connection, bgp_session_eInvalid_msg, - BGP_NOTIFY_HEADER_ERR, - BGP_NOTIFY_HEADER_BAD_MESTYPE, - &type, 1); - return -1; - } + connection->host, type); - /* 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 (session->log, - "%s bad message length - %d for %s", - session->host, size, - type == 128 ? "ROUTE-REFRESH" : - bgp_type_str[(int) type]); bgp_msg_notify_send_with_data (connection, bgp_session_eInvalid_msg, - BGP_NOTIFY_HEADER_ERR, - BGP_NOTIFY_HEADER_BAD_MESLEN, - (u_char *) notify_data_length, 2); + BGP_NOMC_HEADER, + BGP_NOMS_H_BAD_TYPE, + &type, 1); return -1; - } + } ; - return size - BGP_HEADER_SIZE; -} + /* Mimimum and maximum message length checks. */ + if ((size < min_size) || (size > BGP_MSG_MAX_L)) + return bgp_msg_header_bad_len(connection, type, size) ; -/* Marker check. */ -static int -bgp_msg_marker_all_one (struct stream *s, int length) -{ - int i; - - for (i = 0; i < length; i++) - if (s->data[i] != 0xff) - return 0; + connection->msg_type = type ; + connection->msg_size = size - BGP_MH_HEAD_L ; - return 1; + return connection->msg_size ; } -/* Deal with the BGP message. MUST remove from ibuf before returns ! */ +/*------------------------------------------------------------------------------ + * Dispatch BGP message. + * + * Arrives in the connection->ibuf, with the getp sitting after the message + * header. Also: + * + * connection->msg_type -- type + * connection->msg_size -- size of message body (excludes header) + * + * Must have finished with ibuf when returns. + */ void bgp_msg_dispatch(bgp_connection connection) { - u_char type = 0; - bgp_size_t size; - - /* Get size and type again. */ - size = stream_getw_from (connection->ibuf, BGP_MARKER_SIZE); - type = stream_getc_from (connection->ibuf, BGP_MARKER_SIZE + 2); - - size -= BGP_HEADER_SIZE; + uint8_t type = connection->msg_size ; + bgp_size_t size = connection->msg_type ; /* Read rest of the packet and call each sort of packet routine */ switch (type) { - case BGP_MSG_OPEN: + case BGP_MT_OPEN: bgp_msg_open_receive (connection, size); break; - case BGP_MSG_UPDATE: + case BGP_MT_UPDATE: bgp_msg_update_receive (connection, size); break; - case BGP_MSG_NOTIFY: + case BGP_MT_NOTIFICATION: bgp_msg_notify_receive(connection, size); break; - case BGP_MSG_KEEPALIVE: + case BGP_MT_KEEPALIVE: bgp_msg_keepalive_receive(connection, size); break; - case BGP_MSG_ROUTE_REFRESH_NEW: - case BGP_MSG_ROUTE_REFRESH_OLD: + case BGP_MT_ROUTE_REFRESH: + case BGP_MT_ROUTE_REFRESH_pre: /* TODO: refresh? */ #if 0 peer->refresh_in++; bgp_route_refresh_receive (peer, size); #endif break; - case BGP_MSG_CAPABILITY: + case BGP_MT_CAPABILITY: /* TODO: dynamic capability? */ #if 0 peer->dynamic_cap_in++; @@ -210,16 +222,32 @@ bgp_msg_dispatch(bgp_connection connection) break; default: assert(0); /* types already validated */ - } + } ; +} ; - /* remove message from ibuf */ - if (connection->ibuf) - stream_reset (connection->ibuf); -} +/*============================================================================== + * 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) ; -/* Receive BGP open packet and parse it into the session's - * open_recv - * return 0 OK +static int +bgp_msg_open_invalid(bgp_notify notification) ; + +/*------------------------------------------------------------------------------ + * Receive BGP open packet and parse it into the connection's open_recv + * + * return 0 OK * return -1 error */ static int @@ -228,822 +256,1029 @@ bgp_msg_open_receive (bgp_connection connection, bgp_size_t size) int ret; u_char version; u_char optlen; - as_t remote_as = 0; - as_t as4 = 0; struct in_addr remote_id; - int capability; - u_int8_t notify_data_remote_as[2]; - u_int8_t notify_data_remote_id[4]; - bgp_session session = connection->session; bgp_open_state open_recv; + struct stream* s ; + struct sucker ssr ; + unsigned holdtime ; + + /* Start with an unspecific OPEN notification */ + bgp_notify notification = bgp_notify_new(BGP_NOMC_OPEN, + BGP_NOMS_UNSPECIFIC, 0) ; - assert(session); + /* To receive the parsed open message */ + open_recv = connection->open_recv + = bgp_open_state_init_new(connection->open_recv) ; - /* To receive the parsed open message */ - session->open_recv = bgp_open_state_init_new(session->open_recv); - open_recv = session->open_recv; + /* Parse fixed part of the open packet */ + s = connection->ibuf ; - /* Parse open packet. */ - version = stream_getc (connection->ibuf); - memcpy (notify_data_remote_as, stream_pnt (connection->ibuf), 2); - open_recv->my_as = stream_getw (connection->ibuf); - open_recv->holdtime = stream_getw (connection->ibuf); - memcpy (notify_data_remote_id, stream_pnt (connection->ibuf), 4); - remote_id.s_addr = stream_get_ipv4 (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); - /* Receive OPEN message log */ + 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", - session->host, version, open_recv->my_as, open_recv->holdtime, + connection->host, version, + open_recv->my_as, open_recv->holdtime, inet_ntoa (remote_id)); - /* BEGIN to read the capability here, but don't do it yet */ - capability = 0; - optlen = stream_getc (connection->ibuf); - - if (optlen != 0) + /* Peer BGP version check. */ + if (version != BGP_VERSION_4) { - /* 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 = bgp_msg_peek_for_as4_capability (connection, optlen); + 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 ; } - /* Just in case we have a silly peer who sends AS4 capability set to 0 */ - if (open_recv->can_as4 && !as4) + /* Remote bgp_id may not be multicast, or the same as here */ + if (IN_MULTICAST(open_recv->bgp_id) || + (open_recv->bgp_id == connection->session->open_send->bgp_id)) { - zlog_err ("%s bad OPEN, got AS4 capability, but AS4 set to 0", - session->host); - bgp_msg_notify_send (connection, bgp_session_eOpen_reject, - BGP_NOTIFY_OPEN_ERR, - BGP_NOTIFY_OPEN_BAD_PEER_AS); - return -1; - } + zlog_debug ("%s rcv OPEN, *multicast* id %s", + connection->host, 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 (remote_as == BGP_AS_TRANS) + if (optlen != stream_get_left(s)) { - /* 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 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", - session->host); - bgp_msg_notify_send (connection, bgp_session_eOpen_reject, - BGP_NOTIFY_OPEN_ERR, - BGP_NOTIFY_OPEN_BAD_PEER_AS); - return -1; + connection->host); + bgp_msg_open_error(notification, BGP_NOMS_O_BAD_AS) ; + goto reject ; } - - if (!as4 && BGP_DEBUG (as4, AS4)) - zlog_debug ("%s [AS4] OPEN remote_as is AS_TRANS, but no AS4." - " Odd, but proceeding.", session->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.", session->host, as4); - if (as4) - remote_as = as4; - } - else + 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)) { - /* 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 (open_recv->can_as4 && as4 != remote_as) + 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 { - /* 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", - session->host, as4, remote_as); - bgp_msg_notify_send (connection, bgp_session_eOpen_reject, - BGP_NOTIFY_OPEN_ERR, - BGP_NOTIFY_OPEN_BAD_PEER_AS); - return -1; - } - } + connection->host, open_recv->my_as, open_recv->my_as2) ; - /* Set remote router-id */ - open_recv->bgp_id = remote_id.s_addr; + bgp_msg_open_error(notification, BGP_NOMS_O_BAD_AS) ; + goto reject ; + } ; + } ; - /* 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", - session->host, version, BGP_VERSION_4); - bgp_msg_notify_send_with_data (connection, bgp_session_eOpen_reject, - BGP_NOTIFY_OPEN_ERR, - BGP_NOTIFY_OPEN_UNSUP_VERSION, - &maxver, 1); - return -1; - } - - /* TODO: How? Check neighbor as number. */ -#if 0 - if (remote_as != session->as) + /* Finally -- require the AS to be the configured AS */ + if (open_recv->my_as != connection->session->as_expected) { if (BGP_DEBUG (normal, NORMAL)) zlog_debug ("%s bad OPEN, remote AS is %u, expected %u", - session->host, remote_as, session->as); - bgp_msg_notify_send_with_data (connection, bgp_session_eOpen_reject, - BGP_NOTIFY_OPEN_ERR, - BGP_NOTIFY_OPEN_BAD_PEER_AS, - notify_data_remote_as, 2); - return -1; - } -#endif + connection->host, open_recv->my_as, + connection->session->as_expected) ; - /* 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 (open_recv->holdtime < 3 && open_recv->holdtime != 0) - { - bgp_msg_notify_send (connection, bgp_session_eOpen_reject, - BGP_NOTIFY_OPEN_ERR, - BGP_NOTIFY_OPEN_UNACEP_HOLDTIME); - return -1; - } + 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) ; - /* Open option part parse. */ - if (optlen != 0) - { - ret = bgp_msg_open_option_parse (connection, optlen, &open_recv->can_capability); - if (ret < 0) - return ret; - } - else - { - if (BGP_DEBUG (normal, NORMAL)) - zlog_debug ("%s rcvd OPEN w/ OPTION parameter len: 0", - session->host); + goto reject ; } - bgp_fsm_event(connection, bgp_fsm_Receive_OPEN_message); + /*............................................................................ + * It's OK ! Update the connection and issue event. + */ + bgp_notify_free(notification) ; /* No further use for this */ - return 0; -} + holdtime = connection->session->open_send->holdtime ; -static as_t -bgp_msg_capability_as4 (bgp_connection connection, struct capability_header *hdr) -{ - as_t as4 = stream_getl (connection->ibuf); - bgp_session session = connection->session; - assert(session); + if (holdtime > open_recv->holdtime) + holdtime = open_recv->holdtime ; /* use smaller of theirs & ours */ + if (holdtime < 3) + holdtime = 0 ; /* no slip ups */ - if (BGP_DEBUG (as4, AS4)) - zlog_debug ("%s [AS4] about to set cap PEER_CAP_AS4_RCV, got as4 %u", - session->host, as4); + connection->hold_timer_interval = holdtime ; + connection->keepalive_timer_interval = holdtime / 3 ; - return as4; -} + connection->as4 = open_recv->can_as4 ; + connection->route_refresh_pre = open_recv->can_r_refresh == bgp_cap_form_old; + connection->orf_prefix_pre = open_recv->can_orf_prefix == bgp_cap_form_old; -/* peek into option, stores ASN to *as4 if the AS4 capability was found. - * Returns 0 if no as4 found, as4cap value otherwise. - */ -static as_t -bgp_msg_peek_for_as4_capability (bgp_connection connection, u_char length) -{ - struct stream *s = connection->ibuf; - size_t orig_getp = stream_get_getp (s); - size_t end = orig_getp + length; - as_t as4 = 0; - bgp_session session = connection->session; - assert(session); + bgp_fsm_event(connection, bgp_fsm_Receive_OPEN_message) ; - /* The full capability parser will better flag the error.. */ - if (STREAM_READABLE(s) < length) - return 0; + return 0; - if (BGP_DEBUG (as4, AS4)) - zlog_info ("%s [AS4] rcv OPEN w/ OPTION parameter len: %u," - " peeking for as4", - session->host, length); - /* the error cases we DONT handle, we ONLY try to read as4 out of - * correctly formatted options. + /*............................................................................ + * Failed. Reject the OPEN with the required notification. */ - while (stream_get_getp(s) < end) - { - u_char opt_type; - u_char opt_length; +reject: + bgp_fsm_raise_exception(connection, bgp_session_eOpen_reject, notification); - /* Check the length. */ - if (stream_get_getp (s) + 2 > end) - goto end; - - /* Fetch option type and length. */ - opt_type = stream_getc (s); - opt_length = stream_getc (s); - - /* Option length check. */ - if (stream_get_getp (s) + opt_length > end) - goto end; - - if (opt_type == BGP_OPEN_OPT_CAP) - { - unsigned long capd_start = stream_get_getp (s); - unsigned long capd_end = capd_start + opt_length; + return -1 ; +} ; - assert (capd_end <= end); +/*------------------------------------------------------------------------------ + * 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) ; - while (stream_get_getp (s) < capd_end) - { - struct capability_header hdr; + return notification ; +} ; - if (stream_get_getp (s) + 2 > capd_end) - goto end; +/*------------------------------------------------------------------------------ + * 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 ; +} ; - hdr.code = stream_getc (s); - hdr.length = stream_getc (s); +/*------------------------------------------------------------------------------ + * 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) ; +} ; - if ((stream_get_getp(s) + hdr.length) > capd_end) - goto end; +/*------------------------------------------------------------------------------ + * 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 (hdr.code == CAPABILITY_CODE_AS4) - { - if (hdr.length != CAPABILITY_CODE_AS4_LEN) - goto end; + if (notification->subcode != BGP_NOMS_O_CAPABILITY) + bgp_notify_reset(notification, BGP_NOMC_OPEN, BGP_NOMS_O_CAPABILITY) ; - if (BGP_DEBUG (as4, AS4)) - zlog_info ("[AS4] found AS4 capability, about to parse"); - as4 = stream_getl (connection->ibuf); - session->open_recv->can_as4 = 1; + cap_len = suck_total(sr) ; + p_cap = suck_start(sr) - BGP_CAP_MIN_L ; - goto end; - } - stream_forward_getp (s, hdr.length); - } - } - } + assert(*(p_cap + 1) == cap_len) ; -end: - stream_set_getp (s, orig_getp); - return as4; -} + bgp_notify_append_data(notification, p_cap, BGP_CAP_MIN_L + cap_len) ; +} ; -/* Parse open option */ +/*------------------------------------------------------------------------------ + * 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, u_char length, int *capability) +bgp_msg_open_option_parse (bgp_connection connection, bgp_notify notification, + sucker sr) { - int ret; - u_char *error; - u_char error_data[BGP_MAX_PACKET_SIZE]; - struct stream *s = connection->ibuf; - size_t end = stream_get_getp (s) + length; - bgp_session session = connection->session; - assert(session); + 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 ; - ret = 0; - error = error_data; + /* Prepare to read BGP OPEN message options */ - if (BGP_DEBUG (normal, NORMAL)) - zlog_debug ("%s rcv OPEN w/ OPTION parameter len: %u", - session->host, length); + ret = 0 ; /* OK so far */ + capability = 0 ; /* No capability option, yet */ - while (stream_get_getp(s) < end) + while ((left = suck_left(sr)) > 0) { - u_char opt_type; - u_char opt_length; + struct sucker ssr ; + u_char opt_type ; + u_char opt_length ; - /* Must have at least an OPEN option header */ - if (STREAM_READABLE(s) < 2) + /* Fetch option type and length, if possible */ + if ((left -= 2) > 0) { - zlog_info ("%s Option length error", session->host); - bgp_msg_notify_send (connection, bgp_session_eOpen_reject, - BGP_NOTIFY_CEASE, 0); - return -1; - } + opt_type = suck_b(sr); + opt_length = suck_b(sr); + left -= opt_length ; + } ; - /* Fetch option type and length. */ - opt_type = stream_getc (s); - opt_length = stream_getc (s); - - /* Option length check. */ - if (STREAM_READABLE (s) < opt_length) + /* Must not have exceeded available bytes */ + if (left < 0) { - zlog_info ("%s Option length error", session->host); - bgp_msg_notify_send (connection, bgp_session_eOpen_reject, - BGP_NOTIFY_CEASE, 0); - return -1; + 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", - session->host, opt_type, + 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_OPEN_OPT_AUTH: - bgp_msg_notify_send (connection, bgp_session_eOpen_reject, - BGP_NOTIFY_OPEN_ERR, - BGP_NOTIFY_OPEN_AUTH_FAILURE); - ret = -1; - break; - case BGP_OPEN_OPT_CAP: - ret = bgp_msg_capability_parse (connection, opt_length, &error); - *capability = 1; + 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: - bgp_msg_notify_send (connection, bgp_session_eOpen_reject, - BGP_NOTIFY_OPEN_ERR, - BGP_NOTIFY_OPEN_UNSUP_PARAM); - ret = -1; - break; - } + 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. + * + * TODO: verify that (2) should be checked *before* any "OVERRIDE". + */ + if (session->cap_strict) + { + /* Treat any unsuppprted capability as an error. */ + if (bgp_notify_get_subcode(notification) == BGP_NOMS_O_CAPABILITY) + return -1 ; - /* Parse error. To accumulate all unsupported capability codes, - bgp_capability_parse does not return -1 when encounter - unsupported capability code. To detect that, please check - error and erro_data pointer, like below. */ - if (ret < 0) - 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) ; + } ; - /* All OPEN option is parsed. Check capability when strict compare - flag is enabled.*/ - if (session->cap_strict) + /* 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 Unsupported Capability exists. */ - if (error != error_data) + if ((open_recv->can_mp_ext & open_send->can_mp_ext) == 0) { - bgp_msg_notify_send_with_data (connection, bgp_session_eOpen_reject, - BGP_NOTIFY_OPEN_ERR, - BGP_NOTIFY_OPEN_UNSUP_CAPBL, - error_data, error - error_data); - return -1; - } + 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) ; - /* Check local capability does not negotiated with remote - peer. */ - if (session->open_recv->can_mp_ext != session->open_send->can_mp_ext) - { - bgp_msg_notify_send (connection, bgp_session_eOpen_reject, - BGP_NOTIFY_OPEN_ERR, - BGP_NOTIFY_OPEN_UNSUP_CAPBL); - return -1; + return -1 ; } - } + } ; - /* Check there is no common capability send Unsupported Capability - error. */ - if (*capability && ! session->cap_override) - { - if (session->open_recv->can_mp_ext == 0) - { - plog_err (session->log, "%s [Error] No common capability", session->host); + return connection->open_recv->can_capability = capability ; +} ; - if (error != error_data) +/*------------------------------------------------------------------------------ + * 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 + */ - bgp_msg_notify_send_with_data (connection, bgp_session_eOpen_reject, - BGP_NOTIFY_OPEN_ERR, - BGP_NOTIFY_OPEN_UNSUP_CAPBL, - error_data, error - error_data); - else - bgp_msg_notify_send (connection, bgp_session_eOpen_reject, - BGP_NOTIFY_OPEN_ERR, - BGP_NOTIFY_OPEN_UNSUP_CAPBL); - return -1; - } - } - return 0; -} +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) ; -/* Parse given capability. - * XXX: This is reading into a stream, but not using stream API +static int +bgp_msg_capability_as4 (bgp_connection connection, sucker sr) ; + +/*------------------------------------------------------------------------------ + * Set notification to malformed/invalid. + * + * Returns -1 ! */ static int -bgp_msg_capability_parse (bgp_connection connection, size_t length, u_char **error) +bgp_msg_capability_bad(bgp_notify notification) { - int ret; - struct stream *s = connection->ibuf; - size_t end = stream_get_getp (s) + length; - bgp_session session = connection->session; - bgp_open_state open_recv = session->open_recv; + bgp_notify_reset(notification, BGP_NOMC_OPEN, BGP_NOMS_UNSPECIFIC) ; + return -1 ; +} ; - assert (STREAM_READABLE (s) >= length); +/*------------------------------------------------------------------------------ + * 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 (stream_get_getp (s) < end) + while ((left = suck_left(sr)) > 0) { - size_t start; - u_char *sp = stream_pnt (s); - struct capability_header caphdr; + struct sucker ssr ; + int cap_code ; + unsigned cap_length ; /* We need at least capability code and capability length. */ - if (stream_get_getp(s) + 2 > end) + if ((left -= 2) > 0) { - zlog_info ("%s Capability length error (< header)", session->host); - bgp_msg_notify_send (connection, bgp_session_eOpen_reject, - BGP_NOTIFY_CEASE, 0); - return -1; + cap_code = suck_b(sr); + cap_length = suck_b(sr); + left -= cap_length ; } - caphdr.code = stream_getc (s); - caphdr.length = stream_getc (s); - start = stream_get_getp (s); - - /* Capability length check sanity check. */ - if (start + caphdr.length > end) + if (left < 0) { - zlog_info ("%s Capability length error (< length)", session->host); - bgp_msg_notify_send (connection, bgp_session_eOpen_reject, - BGP_NOTIFY_CEASE, 0); - return -1; - } + 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", - session->host, - LOOKUP (capcode_str, caphdr.code), - caphdr.code, caphdr.length); + connection->host, + LOOKUP (capcode_str, cap_code), + cap_code, cap_length) ; /* Length sanity check, type-specific, for known capabilities */ - switch (caphdr.code) + switch (cap_code) { - case CAPABILITY_CODE_MP: - case CAPABILITY_CODE_REFRESH: - case CAPABILITY_CODE_REFRESH_OLD: - case CAPABILITY_CODE_ORF: - case CAPABILITY_CODE_ORF_OLD: - case CAPABILITY_CODE_RESTART: - case CAPABILITY_CODE_AS4: - case CAPABILITY_CODE_DYNAMIC: + 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 (caphdr.length < cap_minsizes[caphdr.code]) + 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 at least %u", - session->host, - LOOKUP (capcode_str, caphdr.code), - caphdr.length, - (unsigned) cap_minsizes[caphdr.code]); - bgp_msg_notify_send (connection, bgp_session_eOpen_reject, - BGP_NOTIFY_CEASE, 0); - return -1; - } + " 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; - } + 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) ; - switch (caphdr.code) + ret = 0 ; + switch (cap_code) { - case CAPABILITY_CODE_MP: - { - /* Ignore capability when override-capability is set. */ - if (! session->cap_override) - { - /* Set negotiated value. */ - ret = bgp_msg_capability_mp (connection, &caphdr); - - /* Unsupported Capability. */ - if (ret < 0) - { - /* Store return data. */ - memcpy (*error, sp, caphdr.length + 2); - *error += caphdr.length + 2; - } - } - } + 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 CAPABILITY_CODE_REFRESH: - case CAPABILITY_CODE_REFRESH_OLD: - { - /* BGP refresh capability */ - if (caphdr.code == CAPABILITY_CODE_REFRESH_OLD) - open_recv->can_r_refresh |= bgp_cap_form_old; - else - open_recv->can_r_refresh |= bgp_cap_form_new; - } + + case BGP_CAN_R_REFRESH: + open_recv->can_r_refresh |= bgp_cap_form_new ; + break ; + + case BGP_CAN_R_REFRESH_pre: + open_recv->can_r_refresh |= bgp_cap_form_old; break; - case CAPABILITY_CODE_ORF: - case CAPABILITY_CODE_ORF_OLD: - if (bgp_msg_capability_orf (connection, &caphdr)) - return -1; + + case BGP_CAN_ORF: + case BGP_CAN_ORF_pre: + ret = bgp_msg_capability_orf(connection, cap_code, sr) ; break; - case CAPABILITY_CODE_RESTART: - if (bgp_msg_capability_restart (connection, &caphdr)) - return -1; + + case BGP_CAN_G_RESTART: + ret = bgp_msg_capability_restart(connection, sr) ; break; - case CAPABILITY_CODE_DYNAMIC: + + case BGP_CAN_DYNAMIC_CAP_old: open_recv->can_dynamic = 1; break; - case CAPABILITY_CODE_AS4: - /* Already handled as a special-case parsing of the capabilities - * at the beginning of OPEN processing. So we care not a jot - * for the value really, only error case. - */ - if (!bgp_msg_capability_as4 (connection, &caphdr)) - return -1; - break; + + case BGP_CAN_AS4: + ret = bgp_msg_capability_as4(connection, sr) ; + break; + default: - if (caphdr.code > 128) + 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", - session->host, caphdr.code); + connection->host, cap_code); } else { zlog_warn ("%s unrecognized capability code: %d - ignored", - session->host, caphdr.code); - memcpy (*error, sp, caphdr.length + 2); - *error += caphdr.length + 2; + connection->host, cap_code) ; + ret = 1 ; /* collect unsupported capability */ } /* Add given unknown capability and its value */ - bgp_open_state_unknown_add(open_recv, caphdr.code, - stream_pnt (s), caphdr.length); + bgp_open_state_unknown_add(open_recv, cap_code, + suck_start(sr), suck_total(sr)) ; } - if (stream_get_getp(s) != (start + caphdr.length)) - { - if (stream_get_getp(s) > (start + caphdr.length)) - zlog_warn ("%s Cap-parser for %s read past cap-length, %u!", - session->host, LOOKUP (capcode_str, caphdr.code), - caphdr.length); - stream_set_getp (s, start + caphdr.length); - } + + if (ret < 0) + return bgp_msg_capability_bad(notification) ; + + if (ret > 0) + bgp_msg_capability_unsupported(notification, sr) ; + + suck_pop(sr, &ssr) ; } - return 0; -} -/* Set negotiated capability value. */ -static int -bgp_msg_capability_mp (bgp_connection connection, struct capability_header *hdr) + return 0 ; +} ; + +static qafx_bit_t +bgp_msg_afi_safi(sucker sr, struct iAFI_SAFI* mp) { - struct capability_mp_data mpc; - struct stream *s = connection->ibuf; - bgp_session session = connection->session; - bgp_open_state open_recv = session->open_recv; - bgp_open_state open_send = session->open_send; - qafx_bit_t qb; + mp->afi = suck_w(sr) ; + suck_x(sr) ; + mp->safi = suck_b(sr) ; + + return qafx_bit_from_iAFI_iSAFI(mp->afi, mp->safi) ; +} ; - assert(session); +/*------------------------------------------------------------------------------ + * 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_capability_mp_data (s, &mpc); + 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", - session->host, mpc.afi, mpc.safi); + connection->host, mp.afi, mp.safi) ; - if (!bgp_afi_safi_valid_indices (mpc.afi, &mpc.safi)) - return -1; + if (qb == 0) + { + zlog_warn ("Unknown afi/safi combination (%u/%u)", mp.afi, mp.safi) ; + return 2 ; + } ; - /* Now safi remapped, and afi/safi are valid array indices */ - qb = qafx_bit(qafx_num_from_qAFI_qSAFI(mpc.afi, mpc.safi)); + /* Now can register the capability */ + connection->open_recv->can_mp_ext |= qb; - 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 ; +} ; - /* won't negotiate on afi/safi? */ - if ((open_send->can_mp_ext & qb) == 0) - return -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. + */ - return 0; -} +static int bgp_msg_capability_orf_entry(bgp_connection connection, + uint8_t cap_code, sucker sr) ; static int -bgp_msg_capability_restart (bgp_connection connection, struct capability_header *caphdr) +bgp_msg_capability_orf(bgp_connection connection, uint8_t cap_code, sucker sr) { - struct stream *s = connection->ibuf; - bgp_session session = connection->session; - bgp_open_state open_recv = session->open_recv; - bgp_open_state open_send = session->open_send; - u_int16_t restart_flag_time; - int restart_bit = 0; - size_t end = stream_get_getp (s) + caphdr->length; - - open_recv->can_g_restart = 1; - restart_flag_time = stream_getw(s); - if (CHECK_FLAG (restart_flag_time, RESTART_R_BIT)) - restart_bit = 1; - UNSET_FLAG (restart_flag_time, 0xF000); - open_recv->restart_time = restart_flag_time; - - if (BGP_DEBUG (normal, NORMAL)) - { - zlog_debug ("%s OPEN has Graceful Restart capability", session->host); - zlog_debug ("%s Peer has%srestarted. Restart Time : %d", - session->host, restart_bit ? " " : " not ", - open_recv->restart_time); - } - - while (stream_get_getp (s) + 4 < end) + while (suck_left(sr) > 0) { - afi_t afi = stream_getw (s); - safi_t safi = stream_getc (s); - u_char flag = stream_getc (s); - - if (!bgp_afi_safi_valid_indices (afi, &safi)) + if (suck_left(sr) < BGP_CAP_ORFE_MIN_L) { - if (BGP_DEBUG (normal, NORMAL)) - zlog_debug ("%s Addr-family %d/%d(afi/safi) not supported." - " Ignore the Graceful Restart capability", - session->host, afi, safi); - } - else - { - qafx_bit_t qb = qafx_bit(qafx_num_from_qAFI_qSAFI(afi, safi)); - - if (!(open_send->can_mp_ext & qb)) - { - if (BGP_DEBUG (normal, NORMAL)) - zlog_debug ("%s Addr-family %d/%d(afi/safi) not enabled." - " Ignore the Graceful Restart capability", - session->host, afi, safi); - } - else - { - if (BGP_DEBUG (normal, NORMAL)) - zlog_debug ("%s Address family %s is%spreserved", session->host, - afi_safi_print (afi, safi), - CHECK_FLAG (flag, RESTART_F_BIT) - ? " " : " not "); - - open_recv->can_preserve |= qb; - if (CHECK_FLAG (flag, RESTART_F_BIT)) - open_recv->has_preserved |= qb; - } - } - } + zlog_info ("%s ORF Capability length error," + " Cap length left %u", + connection->host, suck_left(sr)) ; + return -1; + } ; - return 0; -} + 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, struct capability_header *hdr) +bgp_msg_capability_orf_entry(bgp_connection connection, uint8_t cap_code, + sucker sr) { - struct stream *s = connection->ibuf; - struct capability_orf_entry entry; - afi_t afi; - safi_t safi; - u_char type; - u_char mode; - int i; - bgp_session session = connection->session; - qafx_bit_t qb; - - assert(session); - - /* ORF Entry header */ - bgp_capability_mp_data (s, &entry.mpc); - entry.num = stream_getc (s); - afi = entry.mpc.afi; - safi = entry.mpc.safi; + iAFI_SAFI_t mp ; + int number ; + int length ; + sucker_t ssr ; + + 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", - session->host, entry.mpc.afi, entry.mpc.safi); + 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 (!bgp_afi_safi_valid_indices (entry.mpc.afi, &safi)) - { - zlog_info ("%s Addr-family %d/%d not supported." - " Ignoring the ORF capability", - session->host, entry.mpc.afi, entry.mpc.safi); - return 0; - } + /* 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 */ - if (sizeof (struct capability_orf_entry) + (entry.num * 2) > hdr->length) + /* Validate number field */ + length = number * BGP_CAP_ORFT_L ; + + if (suck_left(sr) < length) { zlog_info ("%s ORF Capability entry length error," - " Cap length %u, num %u", - session->host, hdr->length, entry.num); - bgp_msg_notify_send (connection, bgp_session_eOpen_reject, - BGP_NOTIFY_CEASE, 0); + " Cap length left %u, num %u", + connection->host, suck_left(sr), number) ; return -1; - } + } ; + + /* Process the supported ORF types */ + + suck_push(sr, length, &ssr) ; - for (i = 0 ; i < entry.num ; i++) + while (number--) { - type = stream_getc(s); - mode = stream_getc(s); + qafx_bit_t qbs ; - /* ORF Mode error check */ + uint8_t type = suck_b(sr) ; + uint8_t mode = suck_b(sr) ; + + /* ORF Mode error check */ switch (mode) { - case ORF_MODE_BOTH: - case ORF_MODE_SEND: - case ORF_MODE_RECEIVE: + case BGP_CAP_ORFT_M_RECV: + case BGP_CAP_ORFT_M_SEND: + case BGP_CAP_ORFT_M_BOTH: break; default: - bgp_msg_capability_orf_not_support (session->host, afi, safi, type, mode); - continue; - } - /* ORF Type and afi/safi error checks */ - /* capcode versus type */ - switch (hdr->code) + 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 CAPABILITY_CODE_ORF: + case BGP_CAN_ORF: switch (type) { - case ORF_TYPE_PREFIX: - break; + case BGP_CAP_ORFT_T_PFIX: + open_recv->can_orf_prefix |= bgp_cap_form_new ; + qbs = qafx_ipv4_unicast_bit + | qafx_ipv4_multicast_bit + | qafx_ipv6_unicast_bit ; + break ; default: - bgp_msg_capability_orf_not_support (session->host, afi, safi, type, mode); - continue; + break ; } break; - case CAPABILITY_CODE_ORF_OLD: + case BGP_CAN_ORF_pre: switch (type) { - case ORF_TYPE_PREFIX_OLD: - break; + case BGP_CAP_ORFT_T_PFIX_pre: + open_recv->can_orf_prefix |= bgp_cap_form_old ; + qbs = qafx_ipv4_unicast_bit + | qafx_ipv4_multicast_bit + | qafx_ipv6_unicast_bit ; + break ; default: - bgp_msg_capability_orf_not_support (session->host, afi, safi, type, mode); - continue; + break ; } - break; + break ; default: - bgp_msg_capability_orf_not_support (session->host, afi, safi, type, mode); - continue; - } + break ; + } ; - /* AFI vs SAFI */ - if (!((afi == AFI_IP && safi == SAFI_UNICAST) - || (afi == AFI_IP && safi == SAFI_MULTICAST) - || (afi == AFI_IP6 && safi == SAFI_UNICAST))) + if ((qbs & qb) == 0) { - bgp_msg_capability_orf_not_support (session->host, afi, safi, type, mode); + 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", - session->host, LOOKUP (orf_type_str, type), + connection->host, LOOKUP (orf_type_str, type), LOOKUP (orf_mode_str, mode), - entry.mpc.afi, safi); + 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 ; - if (hdr->code == CAPABILITY_CODE_ORF) - session->open_recv->can_orf_prefix |= bgp_cap_form_new; - else if (hdr->code == CAPABILITY_CODE_ORF_OLD) - session->open_recv->can_orf_prefix |= bgp_cap_form_old; - else - { - bgp_msg_capability_orf_not_support (session->host, afi, safi, type, mode); - continue; - } + confirm((BGP_CAP_ORFT_M_RECV & BGP_CAP_ORFT_M_SEND) == 0) ; - qb = qafx_bit(qafx_num_from_qAFI_qSAFI(afi, safi)); - switch (mode) - { - case ORF_MODE_BOTH: - session->open_recv->can_orf_prefix_send |= qb; - session->open_recv->can_orf_prefix_recv |= qb; - break; - case ORF_MODE_SEND: - session->open_recv->can_orf_prefix_send |= qb; - break; - case ORF_MODE_RECEIVE: - session->open_recv->can_orf_prefix_recv |= qb; - break; - } - } - return 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 -- 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 ! + */ static int -bgp_msg_capability_orf (bgp_connection connection, struct capability_header *hdr) +bgp_msg_capability_restart (bgp_connection connection, sucker sr) { - struct stream *s = connection->ibuf; - size_t end = stream_get_getp (s) + hdr->length; + bgp_open_state open_recv = connection->open_recv; + u_int16_t restart_flag_time ; + int length ; - assert (stream_get_getp(s) + sizeof(struct capability_orf_entry) <= end); + length = suck_left(sr) ; /* total length of value, for reporting */ - /* We must have at least one ORF entry, as the caller has already done - * minimum length validation for the capability code - for ORF there must - * at least one ORF entry (header and unknown number of pairs of bytes). + /* 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..." */ - do + + 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)) { - if (bgp_msg_capability_orf_entry (connection, hdr) == -1) - return -1; - } - while (stream_get_getp(s) + sizeof(struct capability_orf_entry) < end); + 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); + } ; - return 0; -} + 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; + } ; -static void -bgp_msg_capability_orf_not_support (char *host, afi_t afi, safi_t safi, - u_char type, u_char mode) + while (suck_left(sr) > 0) + { + iAFI_SAFI_t mp ; + uint8_t flags ; + qafx_bit_t qb ; + + mp.afi = suck_w(sr) ; + mp.safi = suck_b(sr) ; + flags = suck_b(sr) ; + + qb = qafx_bit_from_iAFI_iSAFI(mp.afi, mp.safi) ; + + 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) + { + qafx_bit_t has ; + if (flags & BGP_CAP_GRE_F_FORW) + has = qb ; + else + has = 0 ; + + open_recv->can_preserve |= qb ; + open_recv->has_preserved |= has ; + + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s Address family %s is%spreserved", + connection->host, + afi_safi_print (mp.afi, mp.safi), + has ? " " : " 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) { - if (BGP_DEBUG (normal, NORMAL)) - zlog_debug ("%s Addr-family %d/%d has ORF type/mode %d/%d not supported", - host, afi, safi, type, mode); -} + 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 + */ /* Parse BGP Update packet. */ static int @@ -1054,23 +1289,26 @@ bgp_msg_update_receive (bgp_connection connection, bgp_size_t size) return 0; } -/* Keepalive treatment function -- get keepalive send keepalive */ +/*============================================================================== + * BGP KEEPALIVE message + */ static int bgp_msg_keepalive_receive (bgp_connection connection, bgp_size_t size) { - bgp_session session = connection->session; - - assert(session); - if (BGP_DEBUG (keepalive, KEEPALIVE)) - zlog_debug ("%s KEEPALIVE rcvd", session->host); + zlog_debug ("%s KEEPALIVE rcvd", connection->host); + + if (size != 0) + return bgp_msg_header_bad_len(connection, BGP_MT_KEEPALIVE, size) ; bgp_fsm_event(connection, bgp_fsm_Receive_KEEPALIVE_message); return 0; } -/* Notify message treatment function. */ +/*============================================================================== + * BGP NOTIFICATION message + */ static void bgp_msg_notify_receive (bgp_connection connection, bgp_size_t size) { @@ -1080,8 +1318,7 @@ bgp_msg_notify_receive (bgp_connection connection, bgp_size_t size) bgp_size_t expect = size - 2; notification = bgp_notify_new(code, subcode, expect); - notification = bgp_notify_append_data(notification, - stream_pnt(connection->ibuf), expect); + bgp_notify_append_data(notification, stream_pnt(connection->ibuf), expect); bgp_fsm_notification_exception(connection, notification); } @@ -1105,6 +1342,6 @@ bgp_msg_notify_send_with_data (bgp_connection connection, { bgp_notify notification; notification = bgp_notify_new(code, sub_code, datalen); - notification = bgp_notify_append_data(notification, data, datalen); + bgp_notify_append_data(notification, data, datalen); bgp_fsm_raise_exception(connection, except, notification); } |